less-js-source 1.1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/Gemfile +1 -0
  2. data/less-js-source.gemspec +1 -1
  3. data/lib/less_js/less.js +579 -572
  4. metadata +4 -5
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source "http://rubygems.org"
2
2
 
3
+ gem 'rake'
3
4
  # Specify your gem's dependencies in less-js-source.gemspec
4
5
  gemspec
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "less-js-source"
6
- s.version = '1.1.1.1'
6
+ s.version = '1.1.2'
7
7
  s.authors = ["Alexis Sellier"]
8
8
  s.email = ["alexis@cloudhead.io"]
9
9
  s.homepage = "http://lesscss.org"
@@ -1,5 +1,5 @@
1
1
  //
2
- // LESS - Leaner CSS v1.1.1
2
+ // LESS - Leaner CSS v1.1.2
3
3
  // http://lesscss.org
4
4
  //
5
5
  // Copyright (c) 2009-2011, Alexis Sellier
@@ -625,7 +625,7 @@ less.Parser = function Parser(env) {
625
625
  // The arguments are parsed with the `entities.arguments` parser.
626
626
  //
627
627
  call: function () {
628
- var name, args;
628
+ var name, args, index = i;
629
629
 
630
630
  if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return;
631
631
 
@@ -642,7 +642,7 @@ less.Parser = function Parser(env) {
642
642
 
643
643
  if (! $(')')) return;
644
644
 
645
- if (name) { return new(tree.Call)(name, args) }
645
+ if (name) { return new(tree.Call)(name, args, index) }
646
646
  },
647
647
  arguments: function () {
648
648
  var args = [], arg;
@@ -1231,6 +1231,78 @@ if (typeof(window) !== 'undefined') {
1231
1231
  };
1232
1232
  }
1233
1233
 
1234
+ (function (tree) {
1235
+
1236
+ tree.Alpha = function (val) {
1237
+ this.value = val;
1238
+ };
1239
+ tree.Alpha.prototype = {
1240
+ toCSS: function () {
1241
+ return "alpha(opacity=" +
1242
+ (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
1243
+ },
1244
+ eval: function () { return this }
1245
+ };
1246
+
1247
+ })(require('less/tree'));
1248
+ (function (tree) {
1249
+
1250
+ tree.Anonymous = function (string) {
1251
+ this.value = string.value || string;
1252
+ };
1253
+ tree.Anonymous.prototype = {
1254
+ toCSS: function () {
1255
+ return this.value;
1256
+ },
1257
+ eval: function () { return this }
1258
+ };
1259
+
1260
+ })(require('less/tree'));
1261
+ (function (tree) {
1262
+
1263
+ //
1264
+ // A function call node.
1265
+ //
1266
+ tree.Call = function (name, args, index) {
1267
+ this.name = name;
1268
+ this.args = args;
1269
+ this.index = index;
1270
+ };
1271
+ tree.Call.prototype = {
1272
+ //
1273
+ // When evaluating a function call,
1274
+ // we either find the function in `tree.functions` [1],
1275
+ // in which case we call it, passing the evaluated arguments,
1276
+ // or we simply print it out as it appeared originally [2].
1277
+ //
1278
+ // The *functions.js* file contains the built-in functions.
1279
+ //
1280
+ // The reason why we evaluate the arguments, is in the case where
1281
+ // we try to pass a variable to a function, like: `saturate(@color)`.
1282
+ // The function should receive the value, not the variable.
1283
+ //
1284
+ eval: function (env) {
1285
+ var args = this.args.map(function (a) { return a.eval(env) });
1286
+
1287
+ if (this.name in tree.functions) { // 1.
1288
+ try {
1289
+ return tree.functions[this.name].apply(tree.functions, args);
1290
+ } catch (e) {
1291
+ throw { message: "error evaluating function `" + this.name + "`",
1292
+ index: this.index };
1293
+ }
1294
+ } else { // 2.
1295
+ return new(tree.Anonymous)(this.name +
1296
+ "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")");
1297
+ }
1298
+ },
1299
+
1300
+ toCSS: function (env) {
1301
+ return this.eval(env).toCSS();
1302
+ }
1303
+ };
1304
+
1305
+ })(require('less/tree'));
1234
1306
  (function (tree) {
1235
1307
  //
1236
1308
  // RGB Colors - #ff0014, #eee
@@ -1331,66 +1403,15 @@ tree.Color.prototype = {
1331
1403
  })(require('less/tree'));
1332
1404
  (function (tree) {
1333
1405
 
1334
- tree.Directive = function (name, value) {
1335
- this.name = name;
1336
- if (Array.isArray(value)) {
1337
- this.ruleset = new(tree.Ruleset)([], value);
1338
- } else {
1339
- this.value = value;
1340
- }
1406
+ tree.Comment = function (value, silent) {
1407
+ this.value = value;
1408
+ this.silent = !!silent;
1341
1409
  };
1342
- tree.Directive.prototype = {
1343
- toCSS: function (ctx, env) {
1344
- if (this.ruleset) {
1345
- this.ruleset.root = true;
1346
- return this.name + (env.compress ? '{' : ' {\n ') +
1347
- this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') +
1348
- (env.compress ? '}': '\n}\n');
1349
- } else {
1350
- return this.name + ' ' + this.value.toCSS() + ';\n';
1351
- }
1352
- },
1353
- eval: function (env) {
1354
- env.frames.unshift(this);
1355
- this.ruleset = this.ruleset && this.ruleset.eval(env);
1356
- env.frames.shift();
1357
- return this;
1410
+ tree.Comment.prototype = {
1411
+ toCSS: function (env) {
1412
+ return env.compress ? '' : this.value;
1358
1413
  },
1359
- variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
1360
- find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
1361
- rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
1362
- };
1363
-
1364
- })(require('less/tree'));
1365
- (function (tree) {
1366
-
1367
- tree.Operation = function (op, operands) {
1368
- this.op = op.trim();
1369
- this.operands = operands;
1370
- };
1371
- tree.Operation.prototype.eval = function (env) {
1372
- var a = this.operands[0].eval(env),
1373
- b = this.operands[1].eval(env),
1374
- temp;
1375
-
1376
- if (a instanceof tree.Dimension && b instanceof tree.Color) {
1377
- if (this.op === '*' || this.op === '+') {
1378
- temp = b, b = a, a = temp;
1379
- } else {
1380
- throw { name: "OperationError",
1381
- message: "Can't substract or divide a color from a number" };
1382
- }
1383
- }
1384
- return a.operate(this.op, b);
1385
- };
1386
-
1387
- tree.operate = function (op, a, b) {
1388
- switch (op) {
1389
- case '+': return a + b;
1390
- case '-': return a - b;
1391
- case '*': return a * b;
1392
- case '/': return a / b;
1393
- }
1414
+ eval: function () { return this }
1394
1415
  };
1395
1416
 
1396
1417
  })(require('less/tree'));
@@ -1430,273 +1451,364 @@ tree.Dimension.prototype = {
1430
1451
  })(require('less/tree'));
1431
1452
  (function (tree) {
1432
1453
 
1433
- tree.Keyword = function (value) { this.value = value };
1434
- tree.Keyword.prototype = {
1435
- eval: function () { return this },
1436
- toCSS: function () { return this.value }
1454
+ tree.Directive = function (name, value) {
1455
+ this.name = name;
1456
+ if (Array.isArray(value)) {
1457
+ this.ruleset = new(tree.Ruleset)([], value);
1458
+ } else {
1459
+ this.value = value;
1460
+ }
1461
+ };
1462
+ tree.Directive.prototype = {
1463
+ toCSS: function (ctx, env) {
1464
+ if (this.ruleset) {
1465
+ this.ruleset.root = true;
1466
+ return this.name + (env.compress ? '{' : ' {\n ') +
1467
+ this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') +
1468
+ (env.compress ? '}': '\n}\n');
1469
+ } else {
1470
+ return this.name + ' ' + this.value.toCSS() + ';\n';
1471
+ }
1472
+ },
1473
+ eval: function (env) {
1474
+ env.frames.unshift(this);
1475
+ this.ruleset = this.ruleset && this.ruleset.eval(env);
1476
+ env.frames.shift();
1477
+ return this;
1478
+ },
1479
+ variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
1480
+ find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
1481
+ rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
1437
1482
  };
1438
1483
 
1439
1484
  })(require('less/tree'));
1440
1485
  (function (tree) {
1441
1486
 
1442
- tree.Variable = function (name, index) { this.name = name, this.index = index };
1443
- tree.Variable.prototype = {
1444
- eval: function (env) {
1445
- var variable, v, name = this.name;
1446
-
1447
- if (name.indexOf('@@') == 0) {
1448
- name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
1449
- }
1487
+ tree.Element = function (combinator, value) {
1488
+ this.combinator = combinator instanceof tree.Combinator ?
1489
+ combinator : new(tree.Combinator)(combinator);
1490
+ this.value = value.trim();
1491
+ };
1492
+ tree.Element.prototype.toCSS = function (env) {
1493
+ return this.combinator.toCSS(env || {}) + this.value;
1494
+ };
1450
1495
 
1451
- if (variable = tree.find(env.frames, function (frame) {
1452
- if (v = frame.variable(name)) {
1453
- return v.value.eval(env);
1454
- }
1455
- })) { return variable }
1456
- else {
1457
- throw { message: "variable " + name + " is undefined",
1458
- index: this.index };
1459
- }
1496
+ tree.Combinator = function (value) {
1497
+ if (value === ' ') {
1498
+ this.value = ' ';
1499
+ } else {
1500
+ this.value = value ? value.trim() : "";
1460
1501
  }
1461
1502
  };
1503
+ tree.Combinator.prototype.toCSS = function (env) {
1504
+ return {
1505
+ '' : '',
1506
+ ' ' : ' ',
1507
+ '&' : '',
1508
+ ':' : ' :',
1509
+ '::': '::',
1510
+ '+' : env.compress ? '+' : ' + ',
1511
+ '~' : env.compress ? '~' : ' ~ ',
1512
+ '>' : env.compress ? '>' : ' > '
1513
+ }[this.value];
1514
+ };
1462
1515
 
1463
1516
  })(require('less/tree'));
1464
1517
  (function (tree) {
1465
1518
 
1466
- tree.Ruleset = function (selectors, rules) {
1467
- this.selectors = selectors;
1468
- this.rules = rules;
1469
- this._lookups = {};
1470
- };
1471
- tree.Ruleset.prototype = {
1519
+ tree.Expression = function (value) { this.value = value };
1520
+ tree.Expression.prototype = {
1472
1521
  eval: function (env) {
1473
- var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
1522
+ if (this.value.length > 1) {
1523
+ return new(tree.Expression)(this.value.map(function (e) {
1524
+ return e.eval(env);
1525
+ }));
1526
+ } else if (this.value.length === 1) {
1527
+ return this.value[0].eval(env);
1528
+ } else {
1529
+ return this;
1530
+ }
1531
+ },
1532
+ toCSS: function (env) {
1533
+ return this.value.map(function (e) {
1534
+ return e.toCSS(env);
1535
+ }).join(' ');
1536
+ }
1537
+ };
1474
1538
 
1475
- ruleset.root = this.root;
1539
+ })(require('less/tree'));
1540
+ (function (tree) {
1541
+ //
1542
+ // CSS @import node
1543
+ //
1544
+ // The general strategy here is that we don't want to wait
1545
+ // for the parsing to be completed, before we start importing
1546
+ // the file. That's because in the context of a browser,
1547
+ // most of the time will be spent waiting for the server to respond.
1548
+ //
1549
+ // On creation, we push the import path to our import queue, though
1550
+ // `import,push`, we also pass it a callback, which it'll call once
1551
+ // the file has been fetched, and parsed.
1552
+ //
1553
+ tree.Import = function (path, imports) {
1554
+ var that = this;
1476
1555
 
1477
- // push the current ruleset to the frames stack
1478
- env.frames.unshift(ruleset);
1556
+ this._path = path;
1479
1557
 
1480
- // Evaluate imports
1481
- if (ruleset.root) {
1482
- for (var i = 0; i < ruleset.rules.length; i++) {
1483
- if (ruleset.rules[i] instanceof tree.Import) {
1484
- Array.prototype.splice
1485
- .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
1486
- }
1487
- }
1488
- }
1558
+ // The '.less' extension is optional
1559
+ if (path instanceof tree.Quoted) {
1560
+ this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less';
1561
+ } else {
1562
+ this.path = path.value.value || path.value;
1563
+ }
1489
1564
 
1490
- // Store the frames around mixin definitions,
1491
- // so they can be evaluated like closures when the time comes.
1492
- for (var i = 0; i < ruleset.rules.length; i++) {
1493
- if (ruleset.rules[i] instanceof tree.mixin.Definition) {
1494
- ruleset.rules[i].frames = env.frames.slice(0);
1495
- }
1496
- }
1565
+ this.css = /css$/.test(this.path);
1497
1566
 
1498
- // Evaluate mixin calls.
1499
- for (var i = 0; i < ruleset.rules.length; i++) {
1500
- if (ruleset.rules[i] instanceof tree.mixin.Call) {
1501
- Array.prototype.splice
1502
- .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
1567
+ // Only pre-compile .less files
1568
+ if (! this.css) {
1569
+ imports.push(this.path, function (root) {
1570
+ if (! root) {
1571
+ throw new(Error)("Error parsing " + that.path);
1503
1572
  }
1573
+ that.root = root;
1574
+ });
1575
+ }
1576
+ };
1577
+
1578
+ //
1579
+ // The actual import node doesn't return anything, when converted to CSS.
1580
+ // The reason is that it's used at the evaluation stage, so that the rules
1581
+ // it imports can be treated like any other rules.
1582
+ //
1583
+ // In `eval`, we make sure all Import nodes get evaluated, recursively, so
1584
+ // we end up with a flat structure, which can easily be imported in the parent
1585
+ // ruleset.
1586
+ //
1587
+ tree.Import.prototype = {
1588
+ toCSS: function () {
1589
+ if (this.css) {
1590
+ return "@import " + this._path.toCSS() + ';\n';
1591
+ } else {
1592
+ return "";
1504
1593
  }
1594
+ },
1595
+ eval: function (env) {
1596
+ var ruleset;
1505
1597
 
1506
- // Evaluate everything else
1507
- for (var i = 0, rule; i < ruleset.rules.length; i++) {
1508
- rule = ruleset.rules[i];
1598
+ if (this.css) {
1599
+ return this;
1600
+ } else {
1601
+ ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
1509
1602
 
1510
- if (! (rule instanceof tree.mixin.Definition)) {
1511
- ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
1603
+ for (var i = 0; i < ruleset.rules.length; i++) {
1604
+ if (ruleset.rules[i] instanceof tree.Import) {
1605
+ Array.prototype
1606
+ .splice
1607
+ .apply(ruleset.rules,
1608
+ [i, 1].concat(ruleset.rules[i].eval(env)));
1609
+ }
1512
1610
  }
1611
+ return ruleset.rules;
1513
1612
  }
1613
+ }
1614
+ };
1514
1615
 
1515
- // Pop the stack
1516
- env.frames.shift();
1616
+ })(require('less/tree'));
1617
+ (function (tree) {
1517
1618
 
1518
- return ruleset;
1519
- },
1520
- match: function (args) {
1521
- return !args || args.length === 0;
1522
- },
1523
- variables: function () {
1524
- if (this._variables) { return this._variables }
1525
- else {
1526
- return this._variables = this.rules.reduce(function (hash, r) {
1527
- if (r instanceof tree.Rule && r.variable === true) {
1528
- hash[r.name] = r;
1619
+ tree.JavaScript = function (string, index, escaped) {
1620
+ this.escaped = escaped;
1621
+ this.expression = string;
1622
+ this.index = index;
1623
+ };
1624
+ tree.JavaScript.prototype = {
1625
+ eval: function (env) {
1626
+ var result,
1627
+ that = this,
1628
+ context = {};
1629
+
1630
+ var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
1631
+ return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
1632
+ });
1633
+
1634
+ try {
1635
+ expression = new(Function)('return (' + expression + ')');
1636
+ } catch (e) {
1637
+ throw { message: "JavaScript evaluation error: `" + expression + "`" ,
1638
+ index: this.index };
1639
+ }
1640
+
1641
+ for (var k in env.frames[0].variables()) {
1642
+ context[k.slice(1)] = {
1643
+ value: env.frames[0].variables()[k].value,
1644
+ toJS: function () {
1645
+ return this.value.eval(env).toCSS();
1529
1646
  }
1530
- return hash;
1531
- }, {});
1647
+ };
1532
1648
  }
1533
- },
1534
- variable: function (name) {
1535
- return this.variables()[name];
1536
- },
1537
- rulesets: function () {
1538
- if (this._rulesets) { return this._rulesets }
1539
- else {
1540
- return this._rulesets = this.rules.filter(function (r) {
1541
- return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
1542
- });
1649
+
1650
+ try {
1651
+ result = expression.call(context);
1652
+ } catch (e) {
1653
+ throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
1654
+ index: this.index };
1543
1655
  }
1544
- },
1545
- find: function (selector, self) {
1546
- self = self || this;
1547
- var rules = [], rule, match,
1548
- key = selector.toCSS();
1656
+ if (typeof(result) === 'string') {
1657
+ return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
1658
+ } else if (Array.isArray(result)) {
1659
+ return new(tree.Anonymous)(result.join(', '));
1660
+ } else {
1661
+ return new(tree.Anonymous)(result);
1662
+ }
1663
+ }
1664
+ };
1549
1665
 
1550
- if (key in this._lookups) { return this._lookups[key] }
1666
+ })(require('less/tree'));
1551
1667
 
1552
- this.rulesets().forEach(function (rule) {
1553
- if (rule !== self) {
1554
- for (var j = 0; j < rule.selectors.length; j++) {
1555
- if (match = selector.match(rule.selectors[j])) {
1556
- if (selector.elements.length > 1) {
1557
- Array.prototype.push.apply(rules, rule.find(
1558
- new(tree.Selector)(selector.elements.slice(1)), self));
1559
- } else {
1560
- rules.push(rule);
1561
- }
1562
- break;
1563
- }
1564
- }
1565
- }
1566
- });
1567
- return this._lookups[key] = rules;
1568
- },
1569
- //
1570
- // Entry point for code generation
1571
- //
1572
- // `context` holds an array of arrays.
1573
- //
1574
- toCSS: function (context, env) {
1575
- var css = [], // The CSS output
1576
- rules = [], // node.Rule instances
1577
- rulesets = [], // node.Ruleset instances
1578
- paths = [], // Current selectors
1579
- selector, // The fully rendered selector
1580
- rule;
1668
+ (function (tree) {
1581
1669
 
1582
- if (! this.root) {
1583
- if (context.length === 0) {
1584
- paths = this.selectors.map(function (s) { return [s] });
1585
- } else {
1586
- for (var s = 0; s < this.selectors.length; s++) {
1587
- for (var c = 0; c < context.length; c++) {
1588
- paths.push(context[c].concat([this.selectors[s]]));
1589
- }
1590
- }
1591
- }
1592
- }
1670
+ tree.Keyword = function (value) { this.value = value };
1671
+ tree.Keyword.prototype = {
1672
+ eval: function () { return this },
1673
+ toCSS: function () { return this.value }
1674
+ };
1593
1675
 
1594
- // Compile rules and rulesets
1595
- for (var i = 0; i < this.rules.length; i++) {
1596
- rule = this.rules[i];
1676
+ })(require('less/tree'));
1677
+ (function (tree) {
1597
1678
 
1598
- if (rule.rules || (rule instanceof tree.Directive)) {
1599
- rulesets.push(rule.toCSS(paths, env));
1600
- } else if (rule instanceof tree.Comment) {
1601
- if (!rule.silent) {
1602
- if (this.root) {
1603
- rulesets.push(rule.toCSS(env));
1604
- } else {
1605
- rules.push(rule.toCSS(env));
1679
+ tree.mixin = {};
1680
+ tree.mixin.Call = function (elements, args, index) {
1681
+ this.selector = new(tree.Selector)(elements);
1682
+ this.arguments = args;
1683
+ this.index = index;
1684
+ };
1685
+ tree.mixin.Call.prototype = {
1686
+ eval: function (env) {
1687
+ var mixins, args, rules = [], match = false;
1688
+
1689
+ for (var i = 0; i < env.frames.length; i++) {
1690
+ if ((mixins = env.frames[i].find(this.selector)).length > 0) {
1691
+ args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
1692
+ for (var m = 0; m < mixins.length; m++) {
1693
+ if (mixins[m].match(args, env)) {
1694
+ try {
1695
+ Array.prototype.push.apply(
1696
+ rules, mixins[m].eval(env, this.arguments).rules);
1697
+ match = true;
1698
+ } catch (e) {
1699
+ throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
1700
+ }
1606
1701
  }
1607
1702
  }
1608
- } else {
1609
- if (rule.toCSS && !rule.variable) {
1610
- rules.push(rule.toCSS(env));
1611
- } else if (rule.value && !rule.variable) {
1612
- rules.push(rule.value.toString());
1703
+ if (match) {
1704
+ return rules;
1705
+ } else {
1706
+ throw { message: 'No matching definition was found for `' +
1707
+ this.selector.toCSS().trim() + '(' +
1708
+ this.arguments.map(function (a) {
1709
+ return a.toCSS();
1710
+ }).join(', ') + ")`",
1711
+ index: this.index };
1613
1712
  }
1614
1713
  }
1615
- }
1714
+ }
1715
+ throw { message: this.selector.toCSS().trim() + " is undefined",
1716
+ index: this.index };
1717
+ }
1718
+ };
1616
1719
 
1617
- rulesets = rulesets.join('');
1720
+ tree.mixin.Definition = function (name, params, rules) {
1721
+ this.name = name;
1722
+ this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
1723
+ this.params = params;
1724
+ this.arity = params.length;
1725
+ this.rules = rules;
1726
+ this._lookups = {};
1727
+ this.required = params.reduce(function (count, p) {
1728
+ if (!p.name || (p.name && !p.value)) { return count + 1 }
1729
+ else { return count }
1730
+ }, 0);
1731
+ this.parent = tree.Ruleset.prototype;
1732
+ this.frames = [];
1733
+ };
1734
+ tree.mixin.Definition.prototype = {
1735
+ toCSS: function () { return "" },
1736
+ variable: function (name) { return this.parent.variable.call(this, name) },
1737
+ variables: function () { return this.parent.variables.call(this) },
1738
+ find: function () { return this.parent.find.apply(this, arguments) },
1739
+ rulesets: function () { return this.parent.rulesets.apply(this) },
1618
1740
 
1619
- // If this is the root node, we don't render
1620
- // a selector, or {}.
1621
- // Otherwise, only output if this ruleset has rules.
1622
- if (this.root) {
1623
- css.push(rules.join(env.compress ? '' : '\n'));
1624
- } else {
1625
- if (rules.length > 0) {
1626
- selector = paths.map(function (p) {
1627
- return p.map(function (s) {
1628
- return s.toCSS(env);
1629
- }).join('').trim();
1630
- }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
1631
- css.push(selector,
1632
- (env.compress ? '{' : ' {\n ') +
1633
- rules.join(env.compress ? '' : '\n ') +
1634
- (env.compress ? '}' : '\n}\n'));
1741
+ eval: function (env, args) {
1742
+ var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
1743
+
1744
+ for (var i = 0, val; i < this.params.length; i++) {
1745
+ if (this.params[i].name) {
1746
+ if (val = (args && args[i]) || this.params[i].value) {
1747
+ frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env)));
1748
+ } else {
1749
+ throw { message: "wrong number of arguments for " + this.name +
1750
+ ' (' + args.length + ' for ' + this.arity + ')' };
1751
+ }
1635
1752
  }
1636
1753
  }
1637
- css.push(rulesets);
1754
+ for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
1755
+ _arguments.push(args[i] || this.params[i].value);
1756
+ }
1757
+ frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
1638
1758
 
1639
- return css.join('') + (env.compress ? '\n' : '');
1640
- }
1641
- };
1642
- })(require('less/tree'));
1643
- (function (tree) {
1759
+ return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
1760
+ frames: [this, frame].concat(this.frames, env.frames)
1761
+ });
1762
+ },
1763
+ match: function (args, env) {
1764
+ var argsLength = (args && args.length) || 0, len;
1644
1765
 
1645
- tree.Element = function (combinator, value) {
1646
- this.combinator = combinator instanceof tree.Combinator ?
1647
- combinator : new(tree.Combinator)(combinator);
1648
- this.value = value.trim();
1649
- };
1650
- tree.Element.prototype.toCSS = function (env) {
1651
- return this.combinator.toCSS(env || {}) + this.value;
1652
- };
1766
+ if (argsLength < this.required) { return false }
1767
+ if ((this.required > 0) && (argsLength > this.params.length)) { return false }
1653
1768
 
1654
- tree.Combinator = function (value) {
1655
- if (value === ' ') {
1656
- this.value = ' ';
1657
- } else {
1658
- this.value = value ? value.trim() : "";
1769
+ len = Math.min(argsLength, this.arity);
1770
+
1771
+ for (var i = 0; i < len; i++) {
1772
+ if (!this.params[i].name) {
1773
+ if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
1774
+ return false;
1775
+ }
1776
+ }
1777
+ }
1778
+ return true;
1659
1779
  }
1660
1780
  };
1661
- tree.Combinator.prototype.toCSS = function (env) {
1662
- return {
1663
- '' : '',
1664
- ' ' : ' ',
1665
- '&' : '',
1666
- ':' : ' :',
1667
- '::': '::',
1668
- '+' : env.compress ? '+' : ' + ',
1669
- '~' : env.compress ? '~' : ' ~ ',
1670
- '>' : env.compress ? '>' : ' > '
1671
- }[this.value];
1672
- };
1673
1781
 
1674
1782
  })(require('less/tree'));
1675
1783
  (function (tree) {
1676
1784
 
1677
- tree.Selector = function (elements) {
1678
- this.elements = elements;
1679
- if (this.elements[0].combinator.value === "") {
1680
- this.elements[0].combinator.value = ' ';
1681
- }
1682
- };
1683
- tree.Selector.prototype.match = function (other) {
1684
- if (this.elements[0].value === other.elements[0].value) {
1685
- return true;
1686
- } else {
1687
- return false;
1688
- }
1785
+ tree.Operation = function (op, operands) {
1786
+ this.op = op.trim();
1787
+ this.operands = operands;
1689
1788
  };
1690
- tree.Selector.prototype.toCSS = function (env) {
1691
- if (this._css) { return this._css }
1789
+ tree.Operation.prototype.eval = function (env) {
1790
+ var a = this.operands[0].eval(env),
1791
+ b = this.operands[1].eval(env),
1792
+ temp;
1692
1793
 
1693
- return this._css = this.elements.map(function (e) {
1694
- if (typeof(e) === 'string') {
1695
- return ' ' + e.trim();
1794
+ if (a instanceof tree.Dimension && b instanceof tree.Color) {
1795
+ if (this.op === '*' || this.op === '+') {
1796
+ temp = b, b = a, a = temp;
1696
1797
  } else {
1697
- return e.toCSS(env);
1798
+ throw { name: "OperationError",
1799
+ message: "Can't substract or divide a color from a number" };
1698
1800
  }
1699
- }).join('');
1801
+ }
1802
+ return a.operate(this.op, b);
1803
+ };
1804
+
1805
+ tree.operate = function (op, a, b) {
1806
+ switch (op) {
1807
+ case '+': return a + b;
1808
+ case '-': return a - b;
1809
+ case '*': return a * b;
1810
+ case '/': return a / b;
1811
+ }
1700
1812
  };
1701
1813
 
1702
1814
  })(require('less/tree'));
@@ -1731,29 +1843,6 @@ tree.Quoted.prototype = {
1731
1843
  })(require('less/tree'));
1732
1844
  (function (tree) {
1733
1845
 
1734
- tree.Expression = function (value) { this.value = value };
1735
- tree.Expression.prototype = {
1736
- eval: function (env) {
1737
- if (this.value.length > 1) {
1738
- return new(tree.Expression)(this.value.map(function (e) {
1739
- return e.eval(env);
1740
- }));
1741
- } else if (this.value.length === 1) {
1742
- return this.value[0].eval(env);
1743
- } else {
1744
- return this;
1745
- }
1746
- },
1747
- toCSS: function (env) {
1748
- return this.value.map(function (e) {
1749
- return e.toCSS(env);
1750
- }).join(' ');
1751
- }
1752
- };
1753
-
1754
- })(require('less/tree'));
1755
- (function (tree) {
1756
-
1757
1846
  tree.Rule = function (name, value, important, index) {
1758
1847
  this.name = name;
1759
1848
  this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
@@ -1792,288 +1881,233 @@ tree.Shorthand.prototype = {
1792
1881
  })(require('less/tree'));
1793
1882
  (function (tree) {
1794
1883
 
1795
- //
1796
- // A function call node.
1797
- //
1798
- tree.Call = function (name, args) {
1799
- this.name = name;
1800
- this.args = args;
1884
+ tree.Ruleset = function (selectors, rules) {
1885
+ this.selectors = selectors;
1886
+ this.rules = rules;
1887
+ this._lookups = {};
1801
1888
  };
1802
- tree.Call.prototype = {
1803
- //
1804
- // When evaluating a function call,
1805
- // we either find the function in `tree.functions` [1],
1806
- // in which case we call it, passing the evaluated arguments,
1807
- // or we simply print it out as it appeared originally [2].
1808
- //
1809
- // The *functions.js* file contains the built-in functions.
1810
- //
1811
- // The reason why we evaluate the arguments, is in the case where
1812
- // we try to pass a variable to a function, like: `saturate(@color)`.
1813
- // The function should receive the value, not the variable.
1814
- //
1889
+ tree.Ruleset.prototype = {
1815
1890
  eval: function (env) {
1816
- var args = this.args.map(function (a) { return a.eval(env) });
1817
-
1818
- if (this.name in tree.functions) { // 1.
1819
- return tree.functions[this.name].apply(tree.functions, args);
1820
- } else { // 2.
1821
- return new(tree.Anonymous)(this.name +
1822
- "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")");
1823
- }
1824
- },
1891
+ var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
1825
1892
 
1826
- toCSS: function (env) {
1827
- return this.eval(env).toCSS();
1828
- }
1829
- };
1893
+ ruleset.root = this.root;
1830
1894
 
1831
- })(require('less/tree'));
1832
- (function (tree) {
1895
+ // push the current ruleset to the frames stack
1896
+ env.frames.unshift(ruleset);
1833
1897
 
1834
- tree.URL = function (val, paths) {
1835
- if (val.data) {
1836
- this.attrs = val;
1837
- } else {
1838
- // Add the base path if the URL is relative and we are in the browser
1839
- if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') {
1840
- val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
1898
+ // Evaluate imports
1899
+ if (ruleset.root) {
1900
+ for (var i = 0; i < ruleset.rules.length; i++) {
1901
+ if (ruleset.rules[i] instanceof tree.Import) {
1902
+ Array.prototype.splice
1903
+ .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
1904
+ }
1905
+ }
1841
1906
  }
1842
- this.value = val;
1843
- this.paths = paths;
1844
- }
1845
- };
1846
- tree.URL.prototype = {
1847
- toCSS: function () {
1848
- return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data
1849
- : this.value.toCSS()) + ")";
1850
- },
1851
- eval: function (ctx) {
1852
- return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths);
1853
- }
1854
- };
1855
-
1856
- })(require('less/tree'));
1857
- (function (tree) {
1858
-
1859
- tree.Alpha = function (val) {
1860
- this.value = val;
1861
- };
1862
- tree.Alpha.prototype = {
1863
- toCSS: function () {
1864
- return "alpha(opacity=" +
1865
- (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
1866
- },
1867
- eval: function () { return this }
1868
- };
1869
-
1870
- })(require('less/tree'));
1871
- (function (tree) {
1872
- //
1873
- // CSS @import node
1874
- //
1875
- // The general strategy here is that we don't want to wait
1876
- // for the parsing to be completed, before we start importing
1877
- // the file. That's because in the context of a browser,
1878
- // most of the time will be spent waiting for the server to respond.
1879
- //
1880
- // On creation, we push the import path to our import queue, though
1881
- // `import,push`, we also pass it a callback, which it'll call once
1882
- // the file has been fetched, and parsed.
1883
- //
1884
- tree.Import = function (path, imports) {
1885
- var that = this;
1886
-
1887
- this._path = path;
1888
-
1889
- // The '.less' extension is optional
1890
- if (path instanceof tree.Quoted) {
1891
- this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less';
1892
- } else {
1893
- this.path = path.value.value || path.value;
1894
- }
1895
-
1896
- this.css = /css$/.test(this.path);
1897
1907
 
1898
- // Only pre-compile .less files
1899
- if (! this.css) {
1900
- imports.push(this.path, function (root) {
1901
- if (! root) {
1902
- throw new(Error)("Error parsing " + that.path);
1908
+ // Store the frames around mixin definitions,
1909
+ // so they can be evaluated like closures when the time comes.
1910
+ for (var i = 0; i < ruleset.rules.length; i++) {
1911
+ if (ruleset.rules[i] instanceof tree.mixin.Definition) {
1912
+ ruleset.rules[i].frames = env.frames.slice(0);
1903
1913
  }
1904
- that.root = root;
1905
- });
1906
- }
1907
- };
1914
+ }
1908
1915
 
1909
- //
1910
- // The actual import node doesn't return anything, when converted to CSS.
1911
- // The reason is that it's used at the evaluation stage, so that the rules
1912
- // it imports can be treated like any other rules.
1913
- //
1914
- // In `eval`, we make sure all Import nodes get evaluated, recursively, so
1915
- // we end up with a flat structure, which can easily be imported in the parent
1916
- // ruleset.
1917
- //
1918
- tree.Import.prototype = {
1919
- toCSS: function () {
1920
- if (this.css) {
1921
- return "@import " + this._path.toCSS() + ';\n';
1922
- } else {
1923
- return "";
1916
+ // Evaluate mixin calls.
1917
+ for (var i = 0; i < ruleset.rules.length; i++) {
1918
+ if (ruleset.rules[i] instanceof tree.mixin.Call) {
1919
+ Array.prototype.splice
1920
+ .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
1921
+ }
1924
1922
  }
1925
- },
1926
- eval: function (env) {
1927
- var ruleset;
1928
1923
 
1929
- if (this.css) {
1930
- return this;
1931
- } else {
1932
- ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
1924
+ // Evaluate everything else
1925
+ for (var i = 0, rule; i < ruleset.rules.length; i++) {
1926
+ rule = ruleset.rules[i];
1933
1927
 
1934
- for (var i = 0; i < ruleset.rules.length; i++) {
1935
- if (ruleset.rules[i] instanceof tree.Import) {
1936
- Array.prototype
1937
- .splice
1938
- .apply(ruleset.rules,
1939
- [i, 1].concat(ruleset.rules[i].eval(env)));
1940
- }
1928
+ if (! (rule instanceof tree.mixin.Definition)) {
1929
+ ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
1941
1930
  }
1942
- return ruleset.rules;
1943
1931
  }
1944
- }
1945
- };
1946
1932
 
1947
- })(require('less/tree'));
1948
- (function (tree) {
1933
+ // Pop the stack
1934
+ env.frames.shift();
1949
1935
 
1950
- tree.mixin = {};
1951
- tree.mixin.Call = function (elements, args, index) {
1952
- this.selector = new(tree.Selector)(elements);
1953
- this.arguments = args;
1954
- this.index = index;
1955
- };
1956
- tree.mixin.Call.prototype = {
1957
- eval: function (env) {
1958
- var mixins, rules = [], match = false;
1936
+ return ruleset;
1937
+ },
1938
+ match: function (args) {
1939
+ return !args || args.length === 0;
1940
+ },
1941
+ variables: function () {
1942
+ if (this._variables) { return this._variables }
1943
+ else {
1944
+ return this._variables = this.rules.reduce(function (hash, r) {
1945
+ if (r instanceof tree.Rule && r.variable === true) {
1946
+ hash[r.name] = r;
1947
+ }
1948
+ return hash;
1949
+ }, {});
1950
+ }
1951
+ },
1952
+ variable: function (name) {
1953
+ return this.variables()[name];
1954
+ },
1955
+ rulesets: function () {
1956
+ if (this._rulesets) { return this._rulesets }
1957
+ else {
1958
+ return this._rulesets = this.rules.filter(function (r) {
1959
+ return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
1960
+ });
1961
+ }
1962
+ },
1963
+ find: function (selector, self) {
1964
+ self = self || this;
1965
+ var rules = [], rule, match,
1966
+ key = selector.toCSS();
1959
1967
 
1960
- for (var i = 0; i < env.frames.length; i++) {
1961
- if ((mixins = env.frames[i].find(this.selector)).length > 0) {
1962
- for (var m = 0; m < mixins.length; m++) {
1963
- if (mixins[m].match(this.arguments, env)) {
1964
- try {
1965
- Array.prototype.push.apply(
1966
- rules, mixins[m].eval(env, this.arguments).rules);
1967
- match = true;
1968
- } catch (e) {
1969
- throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
1968
+ if (key in this._lookups) { return this._lookups[key] }
1969
+
1970
+ this.rulesets().forEach(function (rule) {
1971
+ if (rule !== self) {
1972
+ for (var j = 0; j < rule.selectors.length; j++) {
1973
+ if (match = selector.match(rule.selectors[j])) {
1974
+ if (selector.elements.length > 1) {
1975
+ Array.prototype.push.apply(rules, rule.find(
1976
+ new(tree.Selector)(selector.elements.slice(1)), self));
1977
+ } else {
1978
+ rules.push(rule);
1970
1979
  }
1980
+ break;
1971
1981
  }
1972
1982
  }
1973
- if (match) {
1974
- return rules;
1975
- } else {
1976
- throw { message: 'No matching definition was found for `' +
1977
- this.selector.toCSS().trim() + '(' +
1978
- this.arguments.map(function (a) {
1979
- return a.toCSS();
1980
- }).join(', ') + ")`",
1981
- index: this.index };
1982
- }
1983
1983
  }
1984
- }
1985
- throw { message: this.selector.toCSS().trim() + " is undefined",
1986
- index: this.index };
1987
- }
1988
- };
1989
-
1990
- tree.mixin.Definition = function (name, params, rules) {
1991
- this.name = name;
1992
- this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
1993
- this.params = params;
1994
- this.arity = params.length;
1995
- this.rules = rules;
1996
- this._lookups = {};
1997
- this.required = params.reduce(function (count, p) {
1998
- if (!p.name || (p.name && !p.value)) { return count + 1 }
1999
- else { return count }
2000
- }, 0);
2001
- this.parent = tree.Ruleset.prototype;
2002
- this.frames = [];
2003
- };
2004
- tree.mixin.Definition.prototype = {
2005
- toCSS: function () { return "" },
2006
- variable: function (name) { return this.parent.variable.call(this, name) },
2007
- variables: function () { return this.parent.variables.call(this) },
2008
- find: function () { return this.parent.find.apply(this, arguments) },
2009
- rulesets: function () { return this.parent.rulesets.apply(this) },
2010
-
2011
- eval: function (env, args) {
2012
- var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
1984
+ });
1985
+ return this._lookups[key] = rules;
1986
+ },
1987
+ //
1988
+ // Entry point for code generation
1989
+ //
1990
+ // `context` holds an array of arrays.
1991
+ //
1992
+ toCSS: function (context, env) {
1993
+ var css = [], // The CSS output
1994
+ rules = [], // node.Rule instances
1995
+ rulesets = [], // node.Ruleset instances
1996
+ paths = [], // Current selectors
1997
+ selector, // The fully rendered selector
1998
+ rule;
2013
1999
 
2014
- for (var i = 0, val; i < this.params.length; i++) {
2015
- if (this.params[i].name) {
2016
- if (val = (args && args[i]) || this.params[i].value) {
2017
- frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env)));
2018
- } else {
2019
- throw { message: "wrong number of arguments for " + this.name +
2020
- ' (' + args.length + ' for ' + this.arity + ')' };
2000
+ if (! this.root) {
2001
+ if (context.length === 0) {
2002
+ paths = this.selectors.map(function (s) { return [s] });
2003
+ } else {
2004
+ for (var s = 0; s < this.selectors.length; s++) {
2005
+ for (var c = 0; c < context.length; c++) {
2006
+ paths.push(context[c].concat([this.selectors[s]]));
2007
+ }
2021
2008
  }
2022
2009
  }
2023
2010
  }
2024
- for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
2025
- _arguments.push(args[i] || this.params[i].value);
2026
- }
2027
- frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
2028
2011
 
2029
- return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
2030
- frames: [this, frame].concat(this.frames, env.frames)
2031
- });
2032
- },
2033
- match: function (args, env) {
2034
- var argsLength = (args && args.length) || 0, len;
2012
+ // Compile rules and rulesets
2013
+ for (var i = 0; i < this.rules.length; i++) {
2014
+ rule = this.rules[i];
2035
2015
 
2036
- if (argsLength < this.required) { return false }
2037
- if ((this.required > 0) && (argsLength > this.params.length)) { return false }
2016
+ if (rule.rules || (rule instanceof tree.Directive)) {
2017
+ rulesets.push(rule.toCSS(paths, env));
2018
+ } else if (rule instanceof tree.Comment) {
2019
+ if (!rule.silent) {
2020
+ if (this.root) {
2021
+ rulesets.push(rule.toCSS(env));
2022
+ } else {
2023
+ rules.push(rule.toCSS(env));
2024
+ }
2025
+ }
2026
+ } else {
2027
+ if (rule.toCSS && !rule.variable) {
2028
+ rules.push(rule.toCSS(env));
2029
+ } else if (rule.value && !rule.variable) {
2030
+ rules.push(rule.value.toString());
2031
+ }
2032
+ }
2033
+ }
2038
2034
 
2039
- len = Math.min(argsLength, this.arity);
2035
+ rulesets = rulesets.join('');
2040
2036
 
2041
- for (var i = 0; i < len; i++) {
2042
- if (!this.params[i].name) {
2043
- if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
2044
- return false;
2045
- }
2037
+ // If this is the root node, we don't render
2038
+ // a selector, or {}.
2039
+ // Otherwise, only output if this ruleset has rules.
2040
+ if (this.root) {
2041
+ css.push(rules.join(env.compress ? '' : '\n'));
2042
+ } else {
2043
+ if (rules.length > 0) {
2044
+ selector = paths.map(function (p) {
2045
+ return p.map(function (s) {
2046
+ return s.toCSS(env);
2047
+ }).join('').trim();
2048
+ }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
2049
+ css.push(selector,
2050
+ (env.compress ? '{' : ' {\n ') +
2051
+ rules.join(env.compress ? '' : '\n ') +
2052
+ (env.compress ? '}' : '\n}\n'));
2046
2053
  }
2047
2054
  }
2048
- return true;
2055
+ css.push(rulesets);
2056
+
2057
+ return css.join('') + (env.compress ? '\n' : '');
2049
2058
  }
2050
2059
  };
2051
-
2052
2060
  })(require('less/tree'));
2053
2061
  (function (tree) {
2054
2062
 
2055
- tree.Comment = function (value, silent) {
2056
- this.value = value;
2057
- this.silent = !!silent;
2063
+ tree.Selector = function (elements) {
2064
+ this.elements = elements;
2065
+ if (this.elements[0].combinator.value === "") {
2066
+ this.elements[0].combinator.value = ' ';
2067
+ }
2058
2068
  };
2059
- tree.Comment.prototype = {
2060
- toCSS: function (env) {
2061
- return env.compress ? '' : this.value;
2062
- },
2063
- eval: function () { return this }
2069
+ tree.Selector.prototype.match = function (other) {
2070
+ if (this.elements[0].value === other.elements[0].value) {
2071
+ return true;
2072
+ } else {
2073
+ return false;
2074
+ }
2075
+ };
2076
+ tree.Selector.prototype.toCSS = function (env) {
2077
+ if (this._css) { return this._css }
2078
+
2079
+ return this._css = this.elements.map(function (e) {
2080
+ if (typeof(e) === 'string') {
2081
+ return ' ' + e.trim();
2082
+ } else {
2083
+ return e.toCSS(env);
2084
+ }
2085
+ }).join('');
2064
2086
  };
2065
2087
 
2066
2088
  })(require('less/tree'));
2067
2089
  (function (tree) {
2068
2090
 
2069
- tree.Anonymous = function (string) {
2070
- this.value = string.value || string;
2091
+ tree.URL = function (val, paths) {
2092
+ if (val.data) {
2093
+ this.attrs = val;
2094
+ } else {
2095
+ // Add the base path if the URL is relative and we are in the browser
2096
+ if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') {
2097
+ val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
2098
+ }
2099
+ this.value = val;
2100
+ this.paths = paths;
2101
+ }
2071
2102
  };
2072
- tree.Anonymous.prototype = {
2103
+ tree.URL.prototype = {
2073
2104
  toCSS: function () {
2074
- return this.value;
2105
+ return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data
2106
+ : this.value.toCSS()) + ")";
2075
2107
  },
2076
- eval: function () { return this }
2108
+ eval: function (ctx) {
2109
+ return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths);
2110
+ }
2077
2111
  };
2078
2112
 
2079
2113
  })(require('less/tree'));
@@ -2103,55 +2137,28 @@ tree.Value.prototype = {
2103
2137
  })(require('less/tree'));
2104
2138
  (function (tree) {
2105
2139
 
2106
- tree.JavaScript = function (string, index, escaped) {
2107
- this.escaped = escaped;
2108
- this.expression = string;
2109
- this.index = index;
2110
- };
2111
- tree.JavaScript.prototype = {
2140
+ tree.Variable = function (name, index) { this.name = name, this.index = index };
2141
+ tree.Variable.prototype = {
2112
2142
  eval: function (env) {
2113
- var result,
2114
- that = this,
2115
- context = {};
2116
-
2117
- var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
2118
- return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
2119
- });
2120
-
2121
- try {
2122
- expression = new(Function)('return (' + expression + ')');
2123
- } catch (e) {
2124
- throw { message: "JavaScript evaluation error: `" + expression + "`" ,
2125
- index: this.index };
2126
- }
2143
+ var variable, v, name = this.name;
2127
2144
 
2128
- for (var k in env.frames[0].variables()) {
2129
- context[k.slice(1)] = {
2130
- value: env.frames[0].variables()[k].value,
2131
- toJS: function () {
2132
- return this.value.eval(env).toCSS();
2133
- }
2134
- };
2145
+ if (name.indexOf('@@') == 0) {
2146
+ name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
2135
2147
  }
2136
2148
 
2137
- try {
2138
- result = expression.call(context);
2139
- } catch (e) {
2140
- throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
2149
+ if (variable = tree.find(env.frames, function (frame) {
2150
+ if (v = frame.variable(name)) {
2151
+ return v.value.eval(env);
2152
+ }
2153
+ })) { return variable }
2154
+ else {
2155
+ throw { message: "variable " + name + " is undefined",
2141
2156
  index: this.index };
2142
2157
  }
2143
- if (typeof(result) === 'string') {
2144
- return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
2145
- } else if (Array.isArray(result)) {
2146
- return new(tree.Anonymous)(result.join(', '));
2147
- } else {
2148
- return new(tree.Anonymous)(result);
2149
- }
2150
2158
  }
2151
2159
  };
2152
2160
 
2153
2161
  })(require('less/tree'));
2154
-
2155
2162
  require('less/tree').find = function (obj, fun) {
2156
2163
  for (var i = 0, r; i < obj.length; i++) {
2157
2164
  if (r = fun.call(obj, obj[i])) { return r }