Merge branch '3.x' into dev/3.x-work

This commit is contained in:
Matthew Dean
2017-10-08 15:30:08 -07:00
56 changed files with 326 additions and 348 deletions

View File

@@ -72,6 +72,7 @@
"all"
],
"space-infix-ops": 2,
"spaced-comment": 1,
"space-before-blocks": [
2,
"always"

View File

@@ -3,6 +3,7 @@ cache:
directories:
- travis-phantomjs
node_js:
- "8"
- "6"
- "4"
- "0.12"

View File

@@ -122,14 +122,15 @@ module.exports = function (grunt) {
var pass = process.env.SAUCE_ACCESS_KEY;
git.short(function(hash) {
require('request').put({
require('phin')({
method: 'PUT',
url: ['https://saucelabs.com/rest/v1', user, 'jobs', result.job_id].join('/'),
auth: { user: user, pass: pass },
json: {
data: {
passed: result.passed,
build: 'build-' + hash
}
}, function (error, response, body) {
}, function (error, response) {
if (error) {
console.log(error);
callback(error);
@@ -519,7 +520,7 @@ module.exports = function (grunt) {
grunt.registerTask('test', testTasks);
// Run all tests
grunt.registerTask('quicktest', testTasks.slice(0, testTasks.length - 1));
grunt.registerTask('quicktest', testTasks.slice(0, -1));
// generate a good test environment for testing sourcemaps
grunt.registerTask('sourcemap-test', [

View File

@@ -1,6 +1,6 @@
[![npm version](https://badge.fury.io/js/less.svg)](http://badge.fury.io/js/less) [![Build Status](https://travis-ci.org/less/less.js.svg?branch=master)](https://travis-ci.org/less/less.js)
[![Dependencies](https://david-dm.org/less/less.js.svg)](https://david-dm.org/less/less.js) [![devDependency Status](https://david-dm.org/less/less.js/dev-status.svg)](https://david-dm.org/less/less.js#info=devDependencies) [![optionalDependency Status](https://david-dm.org/less/less.js/optional-status.svg)](https://david-dm.org/less/less.js#info=optionalDependencies)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/less.svg)](https://saucelabs.com/u/less) [![Build status](https://ci.appveyor.com/api/projects/status/bx2qspy3qbuxpl9q/branch/3.x?svg=true)](https://ci.appveyor.com/project/lukeapage/less-js/branch/3.x)
[![npm version](https://badge.fury.io/js/less.svg)](http://badge.fury.io/js/less) [![Build Status](https://travis-ci.org/less/less.js.svg?branch=master)](https://travis-ci.org/less/less.js) [![Build status](https://ci.appveyor.com/api/projects/status/bx2qspy3qbuxpl9q/branch/3.x?svg=true)](https://ci.appveyor.com/project/lukeapage/less-js/branch/3.x) [![Dependencies](https://david-dm.org/less/less.js.svg)](https://david-dm.org/less/less.js) [![devDependency Status](https://david-dm.org/less/less.js/dev-status.svg)](https://david-dm.org/less/less.js#info=devDependencies) [![optionalDependency Status](https://david-dm.org/less/less.js/optional-status.svg)](https://david-dm.org/less/less.js#info=optionalDependencies) [![Twitter Follow](https://img.shields.io/twitter/follow/lesstocss.svg?style=flat-square)](https://twitter.com/lesstocss) [![Join the chat at https://gitter.im/less/less.js](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/less/less.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <sup>Chat with Less.js users</sup>
[![Sauce Test Status](https://saucelabs.com/browser-matrix/less.svg)](https://saucelabs.com/u/less)
# [Less.js](http://lesscss.org)
@@ -8,8 +8,6 @@
This is the JavaScript, official, stable version of Less.
###### :point_right: [![Join the chat at https://gitter.im/less/less.js](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/less/less.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <sup>Chat with Less.js users</sup>
## Getting Started
@@ -51,6 +49,6 @@ Copyright (c) 2009-2016 [Alexis Sellier](http://cloudhead.io) & The Core Less Te
Licensed under the [Apache License](LICENSE).
[so]: http://stackoverflow.com/questions/tagged/twitter-bootstrap+less "StackOverflow.com"
[so]: http://stackoverflow.com/questions/tagged/less "StackOverflow.com"
[issues]: https://github.com/less/less.js/issues "GitHub Issues for Less.js"
[download]: https://github.com/less/less.js/zipball/master "Download Less.js"

View File

@@ -5,6 +5,7 @@ environment:
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "8"
# Install scripts. (runs after repo cloning)
install:

View File

@@ -19,22 +19,10 @@ var less = require('../lib/less-node'),
args = process.argv.slice(1),
silent = false,
verbose = false,
options = require('../lib/less/default-options')();
options = less.options;
options.plugins = plugins;
if (less.options) {
for (var i = 0, keys = Object.keys(options); i < keys.length; i++) {
if (!less.options[keys[i]]) {
less.options[keys[i]] = options[keys[i]];
}
}
options = less.options;
}
else {
less.options = options;
}
var sourceMapOptions = {};
var continueProcessing = true;

View File

@@ -3,14 +3,14 @@
* used in the browser distributed version of less
* to kick-start less using the browser api
*/
/*global window, document */
/* global window, document */
// TODO - consider switching this out for a recommendation for this polyfill?
// <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
// Browsers have good Promise support
require("promise/polyfill");
var options = require('../less/default-options');
var options = require('../less/default-options')();
if (window.less) {
for (key in window.less) {

View File

@@ -18,7 +18,7 @@ module.exports = function(window, options, logger) {
cache.setItem(path + ':vars', JSON.stringify(modifyVars));
}
} catch (e) {
//TODO - could do with adding more robust error handling
// TODO - could do with adding more robust error handling
logger.error('failed to save "' + path + '" to local storage for caching.');
}
}

View File

@@ -112,7 +112,7 @@ module.exports = function(window, less, options) {
}
function removeErrorConsole(path) {
//no action
// no action
}
function removeError(path) {

View File

@@ -1,4 +1,4 @@
/*global window, XMLHttpRequest */
/* global window, XMLHttpRequest */
module.exports = function(options, logger) {
@@ -6,7 +6,7 @@ module.exports = function(options, logger) {
var fileCache = {};
//TODOS - move log somewhere. pathDiff and doing something similar 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
var FileManager = function() {
};

View File

@@ -22,7 +22,7 @@ module.exports = function(window, options) {
var cache = less.cache = options.cache || require("./cache")(window, options, less.logger);
require('./image-size')(less.environment);
//Setup user functions - Deprecate?
// Setup user functions - Deprecate?
if (options.functions) {
less.functions.functionRegistry.addMultiple(options.functions);
}
@@ -54,7 +54,7 @@ module.exports = function(window, options) {
var lessText = style.innerHTML || '';
instanceOptions.filename = document.location.href.replace(/#.*$/, '');
/*jshint loopfunc:true */
/* jshint loopfunc:true */
// use closure to store current style
less.render(lessText, instanceOptions,
bind(function(style, e, result) {
@@ -110,7 +110,7 @@ module.exports = function(window, options) {
}
//TODO add tests around how this behaves when reloading
// TODO add tests around how this behaves when reloading
errors.remove(path);
instanceOptions.rootFileInfo = newFileInfo;

View File

@@ -13,7 +13,9 @@ less.PluginLoader = require("./plugin-loader");
less.fs = require("./fs");
less.FileManager = FileManager;
less.UrlFileManager = UrlFileManager;
less.options = require('../less/default-options');
// Set up options
less.options = require('../less/default-options')();
less.options.paths = [
path.join(process.cwd(), "node_modules")
];

View File

@@ -3,7 +3,7 @@
// helper functions for lessc
var lessc_helper = {
//Stylize a string
// Stylize a string
stylize : function(str, style) {
var styles = {
'reset' : [0, 0],
@@ -19,7 +19,7 @@ var lessc_helper = {
'\x1b[' + styles[style][1] + 'm';
},
//Print command line options
// Print command line options
printUsage: function() {
console.log("usage: lessc [option option=parameter ...] <source> [destination]");
console.log("");

View File

@@ -1,6 +1,5 @@
/* jshint rhino:true, unused: false */
/* jscs:disable validateIndentation */
/*global name:true, less, loadStyleSheet, os */
/* global name:true, less, loadStyleSheet, os */
function formatError(ctx, options) {
options = options || {};
@@ -9,7 +8,7 @@ function formatError(ctx, options) {
var extract = ctx.extract;
var error = [];
// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };
// 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
@@ -211,8 +210,8 @@ function writeFile(filename, content) {
break;
case 'h':
case 'help':
//TODO
// require('../lib/less/lessc_helper').printUsage();
// TODO
// require('../lib/less/lessc_helper').printUsage();
continueProcessing = false;
break;
case 'x':
@@ -255,7 +254,7 @@ function writeFile(filename, content) {
.split(os.type().match(/Windows/) ? /:(?!\\)|;/ : ':')
.map(function(p) {
if (p) {
// return path.resolve(process.cwd(), p);
// return path.resolve(process.cwd(), p);
return p;
}
});
@@ -336,20 +335,20 @@ function writeFile(filename, content) {
var name = args[0];
if (name && name != '-') {
// name = path.resolve(process.cwd(), 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);
// output = path.resolve(process.cwd(), output);
if (warningMessages) {
console.log(warningMessages);
}
}
// options.sourceMapBasepath = process.cwd();
// options.sourceMapBasepath = '';
// options.sourceMapBasepath = process.cwd();
// options.sourceMapBasepath = '';
if (options.sourceMap === true) {
console.log("output: " + output);
@@ -367,24 +366,25 @@ function writeFile(filename, content) {
console.log("lessc: no inout files");
console.log("");
// TODO
// require('../lib/less/lessc_helper').printUsage();
// 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);
// }
// };
/*
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) {

View File

@@ -40,17 +40,17 @@ contexts.Parse = function(options) {
};
var evalCopyProperties = [
'paths', // additional include paths
'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 Inline JavaScript is enabled. if undefined, defaults to false
'pluginManager', // Used as the plugin manager for the session
'importantScope' // used to bubble up !important statements
'paths', // additional include paths
'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 Inline JavaScript is enabled. if undefined, defaults to false
'pluginManager', // Used as the plugin manager for the session
'importantScope' // used to bubble up !important statements
];
contexts.Eval = function(options, frames) {
@@ -108,4 +108,4 @@ contexts.Eval.prototype.normalizePath = function( path ) {
return path.join("/");
};
//todo - do the same for the toCSS ?
// todo - do the same for the toCSS ?

View File

@@ -0,0 +1,15 @@
var functionRegistry = require("./function-registry"),
Anonymous = require("../tree/anonymous"),
Keyword = require("../tree/keyword");
functionRegistry.addMultiple({
boolean: function(condition) {
return condition ? Keyword.True : Keyword.False;
},
'if': function(condition, trueValue, falseValue) {
return condition ? trueValue
: (falseValue || new Anonymous);
}
});

View File

@@ -278,7 +278,7 @@ colorFunctions = {
if (typeof dark === 'undefined') {
dark = colorFunctions.rgba(0, 0, 0, 1.0);
}
//Figure out which is actually light and dark!
// Figure out which is actually light and dark:
if (dark.luma() > light.luma()) {
var t = light;
light = dark;

View File

@@ -7,7 +7,7 @@ function makeRegistry( base ) {
name = name.toLowerCase();
if (this._data.hasOwnProperty(name)) {
//TODO warn
// TODO warn
}
this._data[name] = func;
},

View File

@@ -4,7 +4,8 @@ module.exports = function(environment) {
functionCaller: require("./function-caller")
};
//register functions
// register functions
require("./boolean");
require("./default");
require("./color");
require("./color-blending");

View File

@@ -19,12 +19,12 @@ functionRegistry.addMultiple({
result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement);
return new Quoted(string.quote || '', result, string.escaped);
},
'%': function (string /* arg, arg, ...*/) {
'%': 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 */
/* jshint loopfunc:true */
result = result.replace(/%[sda]/i, function(token) {
var value = ((args[i].type === "Quoted") &&
token.match(/s/i)) ? args[i].value : args[i].toCSS();

View File

@@ -37,7 +37,7 @@ module.exports = function(environment, fileManagers) {
};
var t, api = Object.create(initial);
for (var n in initial.tree) {
/*eslint guard-for-in: 0 */
/* eslint guard-for-in: 0 */
t = initial.tree[n];
if (typeof t === "function") {
api[n] = ctor(t);
@@ -45,7 +45,7 @@ module.exports = function(environment, fileManagers) {
else {
api[n] = Object.create(null);
for (var o in t) {
/*eslint guard-for-in: 0 */
/* eslint guard-for-in: 0 */
api[n][o] = ctor(t[o]);
}
}

View File

@@ -1,11 +1,11 @@
var chunker = require('./chunker');
module.exports = function() {
var input, // LeSS input string
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
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`
@@ -209,7 +209,7 @@ module.exports = function() {
parserInput.peekNotNumeric = function() {
var c = input.charCodeAt(parserInput.i);
//Is the first char of the dimension 0-9, '.', '+' or '-'
// Is the first char of the dimension 0-9, '.', '+' or '-'
return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
};

View File

@@ -35,8 +35,8 @@ var LessError = require('../less-error'),
// 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();
@@ -314,7 +314,7 @@ var Parser = function Parser(context, imports, fileInfo) {
}
node = mixin.definition() || this.declaration() || this.ruleset() ||
mixin.call() || this.rulesetCall() || this.entities.call() || this.atrule();
mixin.call() || this.variableCall() || this.entities.call() || this.atrule();
if (node) {
root.push(node);
} else {
@@ -383,13 +383,10 @@ var Parser = function Parser(context, imports, fileInfo) {
//
// 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;
var name, args, func, index = parserInput.i;
// http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
if (parserInput.peek(/^url\(/i)) {
@@ -399,20 +396,22 @@ var Parser = function Parser(context, imports, fileInfo) {
parserInput.save();
name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/);
if (!name) { parserInput.forget(); return; }
if (!name) {
parserInput.forget();
return;
}
name = name[1];
nameLC = name.toLowerCase();
if (nameLC === 'alpha') {
alpha = parsers.alpha();
if (alpha) {
func = this.customFuncCall(name);
if (func) {
args = func.parse();
if (args && func.stop) {
parserInput.forget();
return alpha;
return args;
}
}
args = this.arguments();
args = this.arguments(args);
if (!parserInput.$char(')')) {
parserInput.restore("Could not parse call arguments or missing ')'");
@@ -422,47 +421,72 @@ var Parser = function Parser(context, imports, fileInfo) {
parserInput.forget();
return new(tree.Call)(name, args, index, fileInfo);
},
arguments: function () {
var argsSemiColon = [], argsComma = [],
expressions = [],
isSemiColonSeparated, value, arg;
//
// Parsing rules for functions with non-standard args, e.g.:
//
// boolean(not(2 > 1))
//
// This is a quick prototype, to be modified/improved when
// more custom-parsed funcs come (e.g. `selector(...)`)
//
customFuncCall: function (name) {
/* Ideally the table is to be moved out of here for faster perf.,
but it's quite tricky since it relies on all these `parsers`
and `expect` available only here */
return {
alpha: f(parsers.ieAlpha, true),
boolean: f(condition),
'if': f(condition)
}[name.toLowerCase()];
function f(parse, stop) {
return {
parse: parse, // parsing function
stop: stop // when true - stop after parse() and return its result,
// otherwise continue for plain args
};
}
function condition() {
return [expect(parsers.condition, 'expected condition')];
}
},
arguments: function (prevArgs) {
var argsComma = prevArgs || [],
argsSemiColon = [],
isSemiColonSeparated, value;
parserInput.save();
while (true) {
if (prevArgs) {
prevArgs = false;
} else {
value = parsers.detachedRuleset() || this.assignment() || parsers.expression();
if (!value) {
break;
}
arg = parsers.detachedRuleset() || this.assignment() || parsers.expression();
if (value.value && value.value.length == 1) {
value = value.value[0];
}
if (!arg) {
break;
argsComma.push(value);
}
value = arg;
if (arg.value && arg.value.length == 1) {
value = arg.value[0];
}
if (value) {
expressions.push(value);
}
argsComma.push(value);
if (parserInput.$char(',')) {
continue;
}
if (parserInput.$char(';') || isSemiColonSeparated) {
isSemiColonSeparated = true;
if (expressions.length > 1) {
value = new(tree.Value)(expressions);
}
value = (argsComma.length < 1) ? argsComma[0]
: new tree.Value(argsComma);
argsSemiColon.push(value);
expressions = [];
argsComma = [];
}
}
@@ -687,15 +711,17 @@ var Parser = function Parser(context, imports, fileInfo) {
},
//
// The variable part of a variable definition. Used in the `rule` parser
// Call a variable value
//
// @fink();
// @fink()
//
rulesetCall: function () {
variableCall: function () {
var name;
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\(\s*\)\s*;/))) {
return new tree.RulesetCall(name[1]);
if (parserInput.currentChar() === '@'
&& (name = parserInput.$re(/^(@[\w-]+)\(\s*\)/))
&& parsers.end()) {
return new tree.VariableCall(name[1]);
}
},
@@ -1017,17 +1043,18 @@ var Parser = function Parser(context, imports, fileInfo) {
//
// alpha(opacity=88)
//
alpha: function () {
ieAlpha: function () {
var value;
// http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
if (!parserInput.$re(/^opacity=/i)) { return; }
value = parserInput.$re(/^\d+/);
if (!value) {
value = expect(this.entities.variable, "Could not parse alpha");
value = expect(parsers.entities.variable, "Could not parse alpha");
value = '@{' + value.name.slice(1) + '}';
}
expectChar(')');
return new(tree.Alpha)(value);
return new tree.Quoted('', 'alpha(opacity=' + value + ')');
},
//

View File

@@ -47,7 +47,7 @@ module.exports = function (environment) {
SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
//ignore adding empty strings
// ignore adding empty strings
if (!chunk) {
return;
}

View File

@@ -1,28 +0,0 @@
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;

View File

@@ -61,11 +61,11 @@ AtRule.prototype.genCSS = function (context, output) {
AtRule.prototype.eval = function (context) {
var mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules;
//media stored inside other atrule should not bubble over it
//backpup media bubbling information
// media stored inside other atrule should not bubble over it
// backpup media bubbling information
mediaPathBackup = context.mediaPath;
mediaBlocksBackup = context.mediaBlocks;
//deleted media bubbling information
// deleted media bubbling information
context.mediaPath = [];
context.mediaBlocks = [];
@@ -77,7 +77,7 @@ AtRule.prototype.eval = function (context) {
rules = [rules[0].eval(context)];
rules[0].root = true;
}
//restore media bubbling information
// restore media bubbling information
context.mediaPath = mediaPathBackup;
context.mediaBlocks = mediaBlocksBackup;

View File

@@ -132,7 +132,7 @@ Color.prototype.toHSL = function () {
}
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
// 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,

View File

@@ -61,7 +61,7 @@ Dimension.prototype.genCSS = function (context, output) {
// we default to the first Dimension's unit,
// so `1px + 2` will yield `3px`.
Dimension.prototype.operate = function (context, op, other) {
/*jshint noempty:false */
/* jshint noempty:false */
var value = this._operate(context, op, this.value, other.value),
unit = this.unit.clone();

View File

@@ -36,7 +36,7 @@ Extend.prototype.eval = function (context) {
Extend.prototype.clone = function (context) {
return new Extend(this.selector, this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
};
//it concatenates (joins) all selectors in selector array
// it concatenates (joins) all selectors in selector array
Extend.prototype.findSelfSelectors = function (selectors) {
var selfElements = [],
i,

View File

@@ -1,7 +1,6 @@
var tree = Object.create(null);
tree.Node = require('./node');
tree.Alpha = require('./alpha');
tree.Color = require('./color');
tree.AtRule = require('./atrule');
// Backwards compatibility
@@ -41,6 +40,6 @@ tree.Media = require('./media');
tree.UnicodeDescriptor = require('./unicode-descriptor');
tree.Negative = require('./negative');
tree.Extend = require('./extend');
tree.RulesetCall = require('./ruleset-call');
tree.VariableCall = require('./variable-call');
module.exports = tree;

View File

@@ -31,7 +31,7 @@ JsEvalNode.prototype.evaluateJavaScript = function (expression, context) {
var variables = context.frames[0].variables();
for (var k in variables) {
if (variables.hasOwnProperty(k)) {
/*jshint loopfunc:true */
/* jshint loopfunc:true */
evalContext[k.slice(1)] = {
value: variables[k].value,
toJS: function () {

View File

@@ -48,19 +48,8 @@ Media.prototype.eval = function (context) {
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;
}
}
media.features = this.features.eval(context);
context.mediaPath.push(media);
context.mediaBlocks.push(media);

View File

@@ -43,7 +43,7 @@ Definition.prototype.accept = function (visitor) {
}
};
Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) {
/*jshint boss:true */
/* jshint boss:true */
var frame = new Ruleset(null, null),
varargs, arg,
params = utils.copyArray(this.params),
@@ -156,7 +156,7 @@ Definition.prototype.evalCall = function (context, args, important) {
Definition.prototype.matchCondition = function (args, context) {
if (this.condition && !this.condition.eval(
new contexts.Eval(context,
[this.evalParams(context, /* the parameter variables*/
[this.evalParams(context, /* the parameter variables */
new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
.concat(this.frames || []) // the parent namespace/mixin frames
.concat(context.frames)))) { // the current environment frames

View File

@@ -63,8 +63,8 @@ Node.prototype._operate = function (context, op, 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));
// add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999...) are properly rounded:
return (precision) ? Number((value + 2e-16).toFixed(precision)) : value;
};
Node.compare = function (a, b) {
/* returns:
@@ -124,13 +124,13 @@ Node.prototype.removeVisibilityBlock = function () {
}
this.visibilityBlocks = this.visibilityBlocks - 1;
};
//Turns on node visibility - if called node will be shown in output regardless
//of whether it comes from import by reference or not
// Turns on node visibility - if called node will be shown in output regardless
// of whether it comes from import by reference or not
Node.prototype.ensureVisibility = function () {
this.nodeVisible = true;
};
//Turns off node visibility - if called node will NOT be shown in output regardless
//of whether it comes from import by reference or not
// Turns off node visibility - if called node will NOT be shown in output regardless
// of whether it comes from import by reference or not
Node.prototype.ensureInvisibility = function () {
this.nodeVisible = false;
};

View File

@@ -10,7 +10,7 @@ var Quoted = function (str, content, escaped, index, currentFileInfo) {
this._index = index;
this._fileInfo = currentFileInfo;
};
Quoted.prototype = new JsEvalNode();
Quoted.prototype = new Node();
Quoted.prototype.type = "Quoted";
Quoted.prototype.genCSS = function (context, output) {
if (!this.escaped) {
@@ -22,13 +22,10 @@ Quoted.prototype.genCSS = function (context, output) {
}
};
Quoted.prototype.containsVariables = function() {
return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/);
return this.value.match(/@\{([\w-]+)\}/);
};
Quoted.prototype.eval = function (context) {
var that = this, value = this.value;
var javascriptReplacement = function (_, exp) {
return String(that.evaluateJavaScript(exp, context));
};
var variableReplacement = function (_, name) {
var v = new Variable('@' + name, that.getIndex(), that.fileInfo()).eval(context, true);
return (v instanceof Quoted) ? v.value : v.toCSS();
@@ -45,7 +42,6 @@ Quoted.prototype.eval = function (context) {
} while (value !== evaluatedValue);
return evaluatedValue;
}
value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement);
value = iterativeReplace(value, /@\{([\w-]+)\}/g, variableReplacement);
value = iterativeReplace(value, /\$\{([\w-]+)\}/g, propertyReplacement);
return new Quoted(this.quote + value + this.quote, value, this.escaped, this.getIndex(), this.fileInfo());

View File

@@ -122,7 +122,7 @@ Ruleset.prototype.eval = function (context) {
// Evaluate mixin calls.
for (i = 0; (rule = rsRules[i]); i++) {
if (rule.type === "MixinCall") {
/*jshint loopfunc:true */
/* jshint loopfunc:true */
rules = rule.eval(context).filter(function(r) {
if ((r instanceof Declaration) && r.variable) {
// do not pollute the scope if the variable is
@@ -135,8 +135,8 @@ Ruleset.prototype.eval = function (context) {
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
i += rules.length - 1;
ruleset.resetCache();
} else if (rule.type === "RulesetCall") {
/*jshint loopfunc:true */
} else if (rule.type === "VariableCall") {
/* jshint loopfunc:true */
rules = rule.eval(context).rules.filter(function(r) {
if ((r instanceof Declaration) && r.variable) {
// do not pollute the scope at all
@@ -529,7 +529,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
// our new selector path
newSelectorPath = [];
//construct the joined selector - if & is the first thing this will be empty,
// 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 (beginningPath.length > 0) {
newSelectorPath = utils.copyArray(beginningPath);
@@ -559,7 +559,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
newSelectorPath.push(newJoinedSelector);
}
//put together the parent selectors after the join (e.g. the rest of the parent)
// put together the parent selectors after the join (e.g. the rest of the parent)
if (addPath.length > 1) {
var restOfPath = addPath.slice(1);
restOfPath = restOfPath.map(function (selector) {
@@ -654,7 +654,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
var nestedPaths = [], replaced, replacedNewSelectors = [];
replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
hadParentSelector = hadParentSelector || replaced;
//the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
// the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
for (k = 0; k < nestedPaths.length; k++) {
var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);

View File

@@ -5,12 +5,11 @@ var Node = require("./node"),
var Selector = function (elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
this.extendList = extendList;
this.condition = condition;
this.evaldCondition = !condition;
this._index = index;
this._fileInfo = currentFileInfo;
this.elements = this.getElements(elements);
if (!condition) {
this.evaldCondition = true;
}
this.mixinElements_ = undefined;
this.copyVisibilityInfo(visibilityInfo);
this.setParent(this.elements, this);
};
@@ -29,10 +28,9 @@ Selector.prototype.accept = function (visitor) {
};
Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
elements = this.getElements(elements);
var info = this.visibilityInfo();
evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
var newSelector = new Selector(elements, extendList || this.extendList, null, this.getIndex(), this.fileInfo(), info);
newSelector.evaldCondition = evaldCondition;
var newSelector = new Selector(elements, extendList || this.extendList,
null, this.getIndex(), this.fileInfo(), this.visibilityInfo());
newSelector.evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
newSelector.mediaEmpty = this.mediaEmpty;
return newSelector;
};
@@ -66,14 +64,13 @@ Selector.prototype.match = function (other) {
len = elements.length,
olen, i;
other.cacheElements();
olen = other._elements.length;
other = other.mixinElements();
olen = other.length;
if (olen === 0 || len < olen) {
return 0;
} else {
for (i = 0; i < olen; i++) {
if (elements[i].value !== other._elements[i]) {
if (elements[i].value !== other[i]) {
return 0;
}
}
@@ -81,9 +78,9 @@ Selector.prototype.match = function (other) {
return olen; // return number of matched elements
};
Selector.prototype.cacheElements = function() {
if (this._elements) {
return;
Selector.prototype.mixinElements = function() {
if (this.mixinElements_) {
return this.mixinElements_;
}
var elements = this.elements.map( function(v) {
@@ -98,7 +95,7 @@ Selector.prototype.cacheElements = function() {
elements = [];
}
this._elements = elements;
return (this.mixinElements_ = elements);
};
Selector.prototype.isJustParentSelector = function() {
return !this.mediaEmpty &&
@@ -120,12 +117,9 @@ Selector.prototype.genCSS = function (context, output) {
if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") {
output.add(' ', this.fileInfo(), this.getIndex());
}
if (!this._css) {
//TODO caching? speed comparison?
for (i = 0; i < this.elements.length; i++) {
element = this.elements[i];
element.genCSS(context, output);
}
for (i = 0; i < this.elements.length; i++) {
element = this.elements[i];
element.genCSS(context, output);
}
};
Selector.prototype.getIsOutput = function() {

View File

@@ -65,7 +65,7 @@ Unit.prototype.usedUnits = function() {
var group, result = {}, mapUnit, groupName;
mapUnit = function (atomicUnit) {
/*jshint loopfunc:true */
/* jshint loopfunc:true */
if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
result[groupName] = atomicUnit;
}

View File

@@ -1,14 +1,14 @@
var Node = require("./node"),
Variable = require("./variable");
var RulesetCall = function (variable) {
var VariableCall = function (variable) {
this.variable = variable;
this.allowRoot = true;
};
RulesetCall.prototype = new Node();
RulesetCall.prototype.type = "RulesetCall";
RulesetCall.prototype.eval = function (context) {
VariableCall.prototype = new Node();
VariableCall.prototype.type = "VariableCall";
VariableCall.prototype.eval = function (context) {
var detachedRuleset = new Variable(this.variable).eval(context);
return detachedRuleset.callEval(context);
};
module.exports = RulesetCall;
module.exports = VariableCall;

View File

@@ -3,7 +3,7 @@ var tree = require("../tree"),
logger = require("../logger"),
utils = require("../utils");
/*jshint loopfunc:true */
/* jshint loopfunc:true */
var ExtendFinderVisitor = function() {
this._visitor = new Visitor(this);
@@ -137,7 +137,7 @@ ProcessExtendsVisitor.prototype = {
iterationCount = iterationCount || 0;
//loop through comparing every extend with every target extend.
// 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.
@@ -368,7 +368,7 @@ ProcessExtendsVisitor.prototype = {
},
extendSelector:function (matches, selectorPath, replacementSelector, isVisible) {
//for a set of matches, replace each match with the replacement selector
// for a set of matches, replace each match with the replacement selector
var currentSelectorPathIndex = 0,
currentSelectorPathElementIndex = 0,

View File

@@ -9,14 +9,14 @@ var CSSVisitorUtils = function(context) {
CSSVisitorUtils.prototype = {
containsSilentNonBlockedChild: function(bodyRules) {
var rule;
if (bodyRules == null) {
if (!bodyRules) {
return false;
}
for (var r = 0; r < bodyRules.length; r++) {
rule = bodyRules[r];
if (rule.isSilent && rule.isSilent(this._context) && !rule.blocksVisibility()) {
//the atrule contains something that was referenced (likely by extend)
//therefore it needs to be shown in output too
// the atrule contains something that was referenced (likely by extend)
// therefore it needs to be shown in output too
return true;
}
}
@@ -24,28 +24,21 @@ CSSVisitorUtils.prototype = {
},
keepOnlyVisibleChilds: function(owner) {
if (owner == null || owner.rules == null) {
return ;
if (owner && owner.rules) {
owner.rules = owner.rules.filter(function(thing) {
return thing.isVisible();
});
}
owner.rules = owner.rules.filter(function(thing) {
return thing.isVisible();
}
);
},
isEmpty: function(owner) {
if (owner == null || owner.rules == null) {
return true;
}
return owner.rules.length === 0;
return (owner && owner.rules)
? (owner.rules.length === 0) : true;
},
hasVisibleSelector: function(rulesetNode) {
if (rulesetNode == null || rulesetNode.paths == null) {
return false;
}
return rulesetNode.paths.length > 0;
return (rulesetNode && rulesetNode.paths)
? (rulesetNode.paths.length > 0) : false;
},
resolveVisibility: function (node, originalRules) {
@@ -146,9 +139,16 @@ ToCSSVisitor.prototype = {
}
},
visitAnonymous: function(anonymousNode, visitArgs) {
if (!anonymousNode.blocksVisibility()) {
anonymousNode.accept(this._visitor);
return anonymousNode;
}
},
visitAtRuleWithBody: function(atRuleNode, visitArgs) {
//if there is only one nested ruleset and that one has no path, then it is
//just fake ruleset
// if there is only one nested ruleset and that one has no path, then it is
// just fake ruleset
function hasFakeRuleset(atRuleNode) {
var bodyRules = atRuleNode.rules;
return bodyRules.length === 1 && (!bodyRules[0].paths || bodyRules[0].paths.length === 0);
@@ -161,9 +161,9 @@ ToCSSVisitor.prototype = {
return nodeRules;
}
//it is still true that it is only one ruleset in array
//this is last such moment
//process childs
// it is still true that it is only one ruleset in array
// this is last such moment
// process childs
var originalRules = getBodyRules(atRuleNode);
atRuleNode.accept(this._visitor);
visitArgs.visitDeeper = false;
@@ -221,13 +221,13 @@ ToCSSVisitor.prototype = {
},
visitRuleset: function (rulesetNode, visitArgs) {
//at this point rulesets are nested into each other
// at this point rulesets are nested into each other
var rule, rulesets = [];
this.checkValidNodes(rulesetNode.rules, rulesetNode.firstRoot);
if (!rulesetNode.root) {
//remove invisible paths
// remove invisible paths
this._compileRulesetPaths(rulesetNode);
// remove rulesets from this ruleset body and compile them separately
@@ -253,7 +253,7 @@ ToCSSVisitor.prototype = {
}
visitArgs.visitDeeper = false;
} else { //if (! rulesetNode.root) {
} else { // if (! rulesetNode.root) {
rulesetNode.accept(this._visitor);
visitArgs.visitDeeper = false;
}
@@ -263,7 +263,7 @@ ToCSSVisitor.prototype = {
this._removeDuplicateRules(rulesetNode.rules);
}
//now decide whether we keep the ruleset
// now decide whether we keep the ruleset
if (this.utils.isVisibleRuleset(rulesetNode)) {
rulesetNode.ensureVisibility();
rulesets.splice(0, 0, rulesetNode);
@@ -321,72 +321,39 @@ ToCSSVisitor.prototype = {
}
},
_mergeRules: function (rules) {
if (!rules) { return; }
var groups = {},
parts,
rule,
key;
_mergeRules: function(rules) {
if (!rules) {
return;
}
var groups = {},
groupsArr = [];
for (var i = 0; i < rules.length; i++) {
rule = rules[i];
if ((rule instanceof tree.Declaration) && rule.merge) {
key = [rule.name,
rule.important ? "!" : ""].join(",");
if (!groups[key]) {
groups[key] = [];
} else {
rules.splice(i--, 1);
}
var rule = rules[i];
if (rule.merge) {
var key = rule.name;
groups[key] ? rules.splice(i--, 1) :
groupsArr.push(groups[key] = []);
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 = [];
groupsArr.forEach(function(group) {
if (group.length > 0) {
var result = group[0],
space = [],
comma = [new tree.Expression(space)];
group.forEach(function(rule) {
if ((rule.merge === '+') && (space.length > 0)) {
comma.push(new tree.Expression(space = []));
}
lastSpacedGroup.push(p);
space.push(rule.value);
result.important = result.important || rule.important;
});
spacedGroups.push(toExpression(lastSpacedGroup));
rule.value = toValue(spacedGroups);
result.value = new tree.Value(comma);
}
});
},
visitAnonymous: function(anonymousNode, visitArgs) {
if (anonymousNode.blocksVisibility()) {
return ;
}
anonymousNode.accept(this._visitor);
return anonymousNode;
}
};

View File

@@ -11,7 +11,7 @@ function indexNodeTypes(parent, ticker) {
// add .typeIndex to tree node types for lookup table
var key, child;
for (key in parent) {
/*eslint guard-for-in: 0 */
/* eslint guard-for-in: 0 */
child = parent[key];
switch (typeof child) {
case "function":

View File

@@ -66,7 +66,7 @@
"performance-now": "^0.2.0",
"phantomjs-prebuilt": "^2.1.7",
"promise": "^7.1.1",
"request": "^2.73.0",
"phin": "^2.2.3",
"time-grunt": "^1.3.0"
},
"keywords": [

View File

@@ -148,7 +148,7 @@ testSheet = function (sheet) {
});
};
//TODO: do it cleaner - the same way as in css
// TODO: do it cleaner - the same way as in css
function extractId(href) {
return href.replace(/^[a-z-]+:\/+?[^\/]+/i, '') // Remove protocol & domain

View File

@@ -1,4 +1,4 @@
/*jshint latedef: nofunc */
/* jshint latedef: nofunc */
// This is used to copy a folder (the test/less/* files & sub-folders), adding a BOM to the start of each LESS and CSS file.
// This is a based on the copySync method from fs-extra (https://github.com/jprichardson/node-fs-extra/).

View File

@@ -199,3 +199,16 @@
html {
color: #8080ff;
}
#boolean {
a: true;
b: false;
c: false;
}
#if {
a: 1;
b: 2;
c: 3;
e: ;
f: 6;
/* results in void */
}

View File

@@ -1,4 +1,4 @@
@media (-o-min-device-pixel-ratio: 2/1) {
@media (-o-min-device-pixel-ratio: 2) {
.test-math-and-units {
font: ignores 0/0 rules;
test-division: 7em;

View File

@@ -10,16 +10,17 @@
background: url(data://img1.png);
}
.test4 {
transform: rotate(90deg), skew(30deg);
transform: scale(2, 4) !important;
transform: rotate(90deg), skew(30deg), scale(2, 4) !important;
}
.test5 {
transform: rotate(90deg), skew(30deg);
transform: scale(2, 4) !important;
transform: rotate(90deg), skew(30deg), scale(2, 4) !important;
}
.test6 {
transform: scale(2, 4);
}
.test7 {
transform: scale(2, 4), scale(2, 4), scale(2, 4) !important;
}
.test-interleaved {
transform: t1, t2, t3;
background: b1, b2, b3;

View File

@@ -1,4 +1,4 @@
/*jshint latedef: nofunc */
/* jshint latedef: nofunc */
module.exports = function() {
var path = require('path'),
@@ -45,21 +45,21 @@ module.exports = function() {
queueRunning = false;
function queue(func) {
if (queueRunning) {
//console.log("adding to queue");
// console.log("adding to queue");
queueList.push(func);
} else {
//console.log("first in queue - starting");
// console.log("first in queue - starting");
queueRunning = true;
func();
}
}
function release() {
if (queueList.length) {
//console.log("running next in queue");
// console.log("running next in queue");
var func = queueList.shift();
setTimeout(func, 0);
} else {
//console.log("stopping queue");
// console.log("stopping queue");
queueRunning = false;
}
}
@@ -283,7 +283,7 @@ module.exports = function() {
if (err.stack) {
process.stdout.write(err.stack + "\n");
} else {
//this sometimes happen - show the whole error object
// this sometimes happen - show the whole error object
console.log(err);
}
}

View File

@@ -31,8 +31,8 @@
.wrap-mixin(@ruleset);
.desktop-and-old-ie(@rules) {
@media screen and (min-width: 1200) { @rules(); }
html.lt-ie9 & { @rules(); }
@media screen and (min-width: 1200) { @rules() }
html.lt-ie9 & { @rules() }
}
header {

View File

@@ -1,2 +0,0 @@
@plugin "../plugin/plugin-tree-nodes";
test-alpha();

View File

@@ -1,3 +0,0 @@
SyntaxError: Alpha node returned by a function is not valid here in {path}functions-2-alpha.less on line 2, column 1:
1 @plugin "../plugin/plugin-tree-nodes";
2 test-alpha();

View File

@@ -231,3 +231,20 @@ html {
color: mix(blue, @color2, 50%);
}
#boolean {
a: boolean(not(2 < 1));
b: boolean(not(2 > 1) and (true));
c: boolean(not(boolean((true))));
}
#if {
a: if(not(false), 1, 2);
b: if(not(true), 1, 2);
@1: if(not(false), {c: 3}, {d: 4}); @1();
e: if(not(true), 5);
@f: boolean((3 = 4));
f: if(not(@f), 6);
if((false), {g: 7}); /* results in void */
}

View File

@@ -1,4 +1,4 @@
@media (-o-min-device-pixel-ratio: 2/1) {
@media (-o-min-device-pixel-ratio: 2) {
.test-math-and-units {
font: ignores 0/0 rules;
test-division: 4 / 2 + 5em;

View File

@@ -36,17 +36,20 @@
.first-background();
}
.test4 {
// Won't merge values from sources that merked as !important, for backwards compatibility with css
.first-transform();
.fifth-transform();
}
.test5 {
// Won't merge values from mixins that merked as !important, for backwards compatibility with css
.first-transform();
.second-transform() !important;
}
.test6 {
// Ignores !merge if no peers found
.second-transform();
}
.test7 {
// inherit !important from merged subrules
.second-transform();
.second-transform() !important;
.second-transform();
}

View File

@@ -7,19 +7,19 @@ functions.addMultiple({
return less.AtRule(arg1.value, arg2.value);
},
"test-extend": function() {
//TODO
// TODO
},
"test-import": function() {
//TODO
// TODO
},
"test-media": function() {
//TODO
// TODO
},
"test-mixin-call": function() {
//TODO
// TODO
},
"test-mixin-definition": function() {
//TODO
// TODO
},
"test-ruleset-call": function() {
return less.Combinator(' ');
@@ -32,9 +32,6 @@ functions.addMultiple({
return true;
},
// These cause root errors
"test-alpha": function() {
return less.Alpha(30);
},
"test-assignment": function() {
return less.Assignment("bird", "robin");
},