diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js index 1b778319..a9f8aaff 100644 --- a/lib/less/functions/index.js +++ b/lib/less/functions/index.js @@ -10,6 +10,7 @@ module.exports = function(environment) { require('./color'); require('./color-blending'); require('./data-uri')(environment); + require('./list'); require('./math'); require('./number'); require('./string'); diff --git a/lib/less/functions/list.js b/lib/less/functions/list.js new file mode 100644 index 00000000..05a5717d --- /dev/null +++ b/lib/less/functions/list.js @@ -0,0 +1,54 @@ +var Dimension = require('../tree/dimension'), + functionRegistry = require('./function-registry'); + +var getItemsFromNode = function(node) { + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + var items = Array.isArray(node.value) ? + node.value : Array(node); + + return items; +}; + +functionRegistry.addMultiple({ + _SELF: function(n) { + return n; + }, + extract: function(values, index) { + index = index.value - 1; // (1-based index) + + return getItemsFromNode(values)[index]; + }, + length: function(values) { + return new Dimension(getItemsFromNode(values).length); + }, + each: function(list, ruleset) { + var i = 0, rules = [], rs, newRules; + + rs = ruleset.ruleset; + + list.value.forEach(function(item) { + i = i + 1; + newRules = rs.rules.slice(0); + newRules.push(new Rule(ruleset && vars.value[1] ? '@' + vars.value[1].value : '@item', + item, + false, false, this.index, this.currentFileInfo)); + newRules.push(new Rule(ruleset && vars.value[0] ? '@' + vars.value[0].value : '@index', + new Dimension(i), + false, false, this.index, this.currentFileInfo)); + + rules.push(new Ruleset([ new(Selector)([ new Element("", '&') ]) ], + newRules, + rs.strictImports, + rs.visibilityInfo() + )); + }); + + return new Ruleset([ new(Selector)([ new Element("", '&') ]) ], + rules, + rs.strictImports, + rs.visibilityInfo() + ).eval(this.context); + + } +}); diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js index f1706d3b..219bcf23 100644 --- a/lib/less/functions/types.js +++ b/lib/less/functions/types.js @@ -20,15 +20,8 @@ var isa = function (n, Type) { throw { type: 'Argument', message: 'Second argument to isunit should be a unit or a string.' }; } return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False; - }, - getItemsFromNode = function(node) { - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - var items = Array.isArray(node.value) ? - node.value : Array(node); - - return items; }; + functionRegistry.addMultiple({ isruleset: function (n) { return isa(n, DetachedRuleset); @@ -77,16 +70,5 @@ functionRegistry.addMultiple({ }, 'get-unit': function (n) { return new Anonymous(n.unit); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - - return getItemsFromNode(values)[index]; - }, - length: function(values) { - return new Dimension(getItemsFromNode(values).length); - }, - _SELF: function(n) { - return n; } }); diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 0a5f14ad..c648e5f6 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -929,7 +929,7 @@ var Parser = function Parser(context, imports, fileInfo) { }, args: function (isCall) { var entities = parsers.entities, - returner = { args:null, variadic: false }, + returner = { args: null, variadic: false }, expressions = [], argsSemiColon = [], argsComma = [], isSemiColonSeparated, expressionContainsNamed, name, nameLoop, value, arg, expand; @@ -1065,7 +1065,7 @@ var Parser = function Parser(context, imports, fileInfo) { // the `{...}` block. // definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; + var name, match; if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') || parserInput.peek(/^[^{]*\}/)) { return; @@ -1076,32 +1076,10 @@ var Parser = function Parser(context, imports, fileInfo) { 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.$str('when')) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { + block = this.body(name); + if (block) { parserInput.forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); + return block; } else { parserInput.restore(); } @@ -1109,6 +1087,41 @@ var Parser = function Parser(context, imports, fileInfo) { parserInput.forget(); } }, + + body: function(name) { + var argInfo, params, variadic, cond, ruleset; + + parserInput.save(); + + 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.$str('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(); + } + }, ruleLookups: function() { var rule, args, lookups = []; @@ -1378,9 +1391,14 @@ var Parser = function Parser(context, imports, fileInfo) { }, detachedRuleset: function() { + var args; + if (parserInput.$char('(')) { + args = this.mixin.args().args; + expectChar(')'); + } var blockRuleset = this.blockRuleset(); if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); + return new tree.DetachedRuleset(blockRuleset, args); } },