UNPKG

486 kBJavaScriptView Raw
1/**
2 * Less - Leaner CSS v4.1.1
3 * http://lesscss.org
4 *
5 * Copyright (c) 2009-2021, Alexis Sellier <self@cloudhead.net>
6 * Licensed under the Apache-2.0 License.
7 *
8 * @license Apache-2.0
9 */
10
11(function (global, factory) {
12 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
13 typeof define === 'function' && define.amd ? define(factory) :
14 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.less = factory());
15}(this, (function () { 'use strict';
16
17 // Export a new default each time
18 function defaultOptions () {
19 return {
20 /* Inline Javascript - @plugin still allowed */
21 javascriptEnabled: false,
22 /* Outputs a makefile import dependency list to stdout. */
23 depends: false,
24 /* (DEPRECATED) Compress using less built-in compression.
25 * This does an okay job but does not utilise all the tricks of
26 * dedicated css compression. */
27 compress: false,
28 /* Runs the less parser and just reports errors without any output. */
29 lint: false,
30 /* Sets available include paths.
31 * If the file in an @import rule does not exist at that exact location,
32 * less will look for it at the location(s) passed to this option.
33 * You might use this for instance to specify a path to a library which
34 * you want to be referenced simply and relatively in the less files. */
35 paths: [],
36 /* color output in the terminal */
37 color: true,
38 /* The strictImports controls whether the compiler will allow an @import inside of either
39 * @media blocks or (a later addition) other selector blocks.
40 * See: https://github.com/less/less.js/issues/656 */
41 strictImports: false,
42 /* Allow Imports from Insecure HTTPS Hosts */
43 insecure: false,
44 /* Allows you to add a path to every generated import and url in your css.
45 * This does not affect less import statements that are processed, just ones
46 * that are left in the output css. */
47 rootpath: '',
48 /* By default URLs are kept as-is, so if you import a file in a sub-directory
49 * that references an image, exactly the same URL will be output in the css.
50 * This option allows you to re-write URL's in imported files so that the
51 * URL is always relative to the base imported file */
52 rewriteUrls: false,
53 /* How to process math
54 * 0 always - eagerly try to solve all operations
55 * 1 parens-division - require parens for division "/"
56 * 2 parens | strict - require parens for all operations
57 * 3 strict-legacy - legacy strict behavior (super-strict)
58 */
59 math: 1,
60 /* Without this option, less attempts to guess at the output unit when it does maths. */
61 strictUnits: false,
62 /* Effectively the declaration is put at the top of your base Less file,
63 * meaning it can be used but it also can be overridden if this variable
64 * is defined in the file. */
65 globalVars: null,
66 /* As opposed to the global variable option, this puts the declaration at the
67 * end of your base file, meaning it will override anything defined in your Less file. */
68 modifyVars: null,
69 /* This option allows you to specify a argument to go on to every URL. */
70 urlArgs: ''
71 };
72 }
73
74 function extractId(href) {
75 return href.replace(/^[a-z-]+:\/+?[^\/]+/, '') // Remove protocol & domain
76 .replace(/[\?\&]livereload=\w+/, '') // Remove LiveReload cachebuster
77 .replace(/^\//, '') // Remove root /
78 .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension
79 .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
80 .replace(/\./g, ':'); // Replace dots with colons(for valid id)
81 }
82 function addDataAttr(options, tag) {
83 for (var opt in tag.dataset) {
84 if (tag.dataset.hasOwnProperty(opt)) {
85 if (opt === 'env' || opt === 'dumpLineNumbers' || opt === 'rootpath' || opt === 'errorReporting') {
86 options[opt] = tag.dataset[opt];
87 }
88 else {
89 try {
90 options[opt] = JSON.parse(tag.dataset[opt]);
91 }
92 catch (_) { }
93 }
94 }
95 }
96 }
97
98 var browser = {
99 createCSS: function (document, styles, sheet) {
100 // Strip the query-string
101 var href = sheet.href || '';
102 // If there is no title set, use the filename, minus the extension
103 var id = "less:" + (sheet.title || extractId(href));
104 // If this has already been inserted into the DOM, we may need to replace it
105 var oldStyleNode = document.getElementById(id);
106 var keepOldStyleNode = false;
107 // Create a new stylesheet node for insertion or (if necessary) replacement
108 var styleNode = document.createElement('style');
109 styleNode.setAttribute('type', 'text/css');
110 if (sheet.media) {
111 styleNode.setAttribute('media', sheet.media);
112 }
113 styleNode.id = id;
114 if (!styleNode.styleSheet) {
115 styleNode.appendChild(document.createTextNode(styles));
116 // If new contents match contents of oldStyleNode, don't replace oldStyleNode
117 keepOldStyleNode = (oldStyleNode !== null && oldStyleNode.childNodes.length > 0 && styleNode.childNodes.length > 0 &&
118 oldStyleNode.firstChild.nodeValue === styleNode.firstChild.nodeValue);
119 }
120 var head = document.getElementsByTagName('head')[0];
121 // If there is no oldStyleNode, just append; otherwise, only append if we need
122 // to replace oldStyleNode with an updated stylesheet
123 if (oldStyleNode === null || keepOldStyleNode === false) {
124 var nextEl = sheet && sheet.nextSibling || null;
125 if (nextEl) {
126 nextEl.parentNode.insertBefore(styleNode, nextEl);
127 }
128 else {
129 head.appendChild(styleNode);
130 }
131 }
132 if (oldStyleNode && keepOldStyleNode === false) {
133 oldStyleNode.parentNode.removeChild(oldStyleNode);
134 }
135 // For IE.
136 // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash.
137 // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head
138 if (styleNode.styleSheet) {
139 try {
140 styleNode.styleSheet.cssText = styles;
141 }
142 catch (e) {
143 throw new Error('Couldn\'t reassign styleSheet.cssText.');
144 }
145 }
146 },
147 currentScript: function (window) {
148 var document = window.document;
149 return document.currentScript || (function () {
150 var scripts = document.getElementsByTagName('script');
151 return scripts[scripts.length - 1];
152 })();
153 }
154 };
155
156 var addDefaultOptions = (function (window, options) {
157 // use options from the current script tag data attribues
158 addDataAttr(options, browser.currentScript(window));
159 if (options.isFileProtocol === undefined) {
160 options.isFileProtocol = /^(file|(chrome|safari)(-extension)?|resource|qrc|app):/.test(window.location.protocol);
161 }
162 // Load styles asynchronously (default: false)
163 //
164 // This is set to `false` by default, so that the body
165 // doesn't start loading before the stylesheets are parsed.
166 // Setting this to `true` can result in flickering.
167 //
168 options.async = options.async || false;
169 options.fileAsync = options.fileAsync || false;
170 // Interval between watch polls
171 options.poll = options.poll || (options.isFileProtocol ? 1000 : 1500);
172 options.env = options.env || (window.location.hostname == '127.0.0.1' ||
173 window.location.hostname == '0.0.0.0' ||
174 window.location.hostname == 'localhost' ||
175 (window.location.port &&
176 window.location.port.length > 0) ||
177 options.isFileProtocol ? 'development'
178 : 'production');
179 var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(window.location.hash);
180 if (dumpLineNumbers) {
181 options.dumpLineNumbers = dumpLineNumbers[1];
182 }
183 if (options.useFileCache === undefined) {
184 options.useFileCache = true;
185 }
186 if (options.onReady === undefined) {
187 options.onReady = true;
188 }
189 if (options.relativeUrls) {
190 options.rewriteUrls = 'all';
191 }
192 });
193
194 var logger = {
195 error: function (msg) {
196 this._fireEvent('error', msg);
197 },
198 warn: function (msg) {
199 this._fireEvent('warn', msg);
200 },
201 info: function (msg) {
202 this._fireEvent('info', msg);
203 },
204 debug: function (msg) {
205 this._fireEvent('debug', msg);
206 },
207 addListener: function (listener) {
208 this._listeners.push(listener);
209 },
210 removeListener: function (listener) {
211 for (var i = 0; i < this._listeners.length; i++) {
212 if (this._listeners[i] === listener) {
213 this._listeners.splice(i, 1);
214 return;
215 }
216 }
217 },
218 _fireEvent: function (type, msg) {
219 for (var i = 0; i < this._listeners.length; i++) {
220 var logFunction = this._listeners[i][type];
221 if (logFunction) {
222 logFunction(msg);
223 }
224 }
225 },
226 _listeners: []
227 };
228
229 /**
230 * @todo Document why this abstraction exists, and the relationship between
231 * environment, file managers, and plugin manager
232 */
233 var Environment = /** @class */ (function () {
234 function Environment(externalEnvironment, fileManagers) {
235 this.fileManagers = fileManagers || [];
236 externalEnvironment = externalEnvironment || {};
237 var optionalFunctions = ['encodeBase64', 'mimeLookup', 'charsetLookup', 'getSourceMapGenerator'];
238 var requiredFunctions = [];
239 var functions = requiredFunctions.concat(optionalFunctions);
240 for (var i = 0; i < functions.length; i++) {
241 var propName = functions[i];
242 var environmentFunc = externalEnvironment[propName];
243 if (environmentFunc) {
244 this[propName] = environmentFunc.bind(externalEnvironment);
245 }
246 else if (i < requiredFunctions.length) {
247 this.warn("missing required function in environment - " + propName);
248 }
249 }
250 }
251 Environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) {
252 if (!filename) {
253 logger.warn('getFileManager called with no filename.. Please report this issue. continuing.');
254 }
255 if (currentDirectory == null) {
256 logger.warn('getFileManager called with null directory.. Please report this issue. continuing.');
257 }
258 var fileManagers = this.fileManagers;
259 if (options.pluginManager) {
260 fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers());
261 }
262 for (var i = fileManagers.length - 1; i >= 0; i--) {
263 var fileManager = fileManagers[i];
264 if (fileManager[isSync ? 'supportsSync' : 'supports'](filename, currentDirectory, options, environment)) {
265 return fileManager;
266 }
267 }
268 return null;
269 };
270 Environment.prototype.addFileManager = function (fileManager) {
271 this.fileManagers.push(fileManager);
272 };
273 Environment.prototype.clearFileManagers = function () {
274 this.fileManagers = [];
275 };
276 return Environment;
277 }());
278
279 var colors = {
280 'aliceblue': '#f0f8ff',
281 'antiquewhite': '#faebd7',
282 'aqua': '#00ffff',
283 'aquamarine': '#7fffd4',
284 'azure': '#f0ffff',
285 'beige': '#f5f5dc',
286 'bisque': '#ffe4c4',
287 'black': '#000000',
288 'blanchedalmond': '#ffebcd',
289 'blue': '#0000ff',
290 'blueviolet': '#8a2be2',
291 'brown': '#a52a2a',
292 'burlywood': '#deb887',
293 'cadetblue': '#5f9ea0',
294 'chartreuse': '#7fff00',
295 'chocolate': '#d2691e',
296 'coral': '#ff7f50',
297 'cornflowerblue': '#6495ed',
298 'cornsilk': '#fff8dc',
299 'crimson': '#dc143c',
300 'cyan': '#00ffff',
301 'darkblue': '#00008b',
302 'darkcyan': '#008b8b',
303 'darkgoldenrod': '#b8860b',
304 'darkgray': '#a9a9a9',
305 'darkgrey': '#a9a9a9',
306 'darkgreen': '#006400',
307 'darkkhaki': '#bdb76b',
308 'darkmagenta': '#8b008b',
309 'darkolivegreen': '#556b2f',
310 'darkorange': '#ff8c00',
311 'darkorchid': '#9932cc',
312 'darkred': '#8b0000',
313 'darksalmon': '#e9967a',
314 'darkseagreen': '#8fbc8f',
315 'darkslateblue': '#483d8b',
316 'darkslategray': '#2f4f4f',
317 'darkslategrey': '#2f4f4f',
318 'darkturquoise': '#00ced1',
319 'darkviolet': '#9400d3',
320 'deeppink': '#ff1493',
321 'deepskyblue': '#00bfff',
322 'dimgray': '#696969',
323 'dimgrey': '#696969',
324 'dodgerblue': '#1e90ff',
325 'firebrick': '#b22222',
326 'floralwhite': '#fffaf0',
327 'forestgreen': '#228b22',
328 'fuchsia': '#ff00ff',
329 'gainsboro': '#dcdcdc',
330 'ghostwhite': '#f8f8ff',
331 'gold': '#ffd700',
332 'goldenrod': '#daa520',
333 'gray': '#808080',
334 'grey': '#808080',
335 'green': '#008000',
336 'greenyellow': '#adff2f',
337 'honeydew': '#f0fff0',
338 'hotpink': '#ff69b4',
339 'indianred': '#cd5c5c',
340 'indigo': '#4b0082',
341 'ivory': '#fffff0',
342 'khaki': '#f0e68c',
343 'lavender': '#e6e6fa',
344 'lavenderblush': '#fff0f5',
345 'lawngreen': '#7cfc00',
346 'lemonchiffon': '#fffacd',
347 'lightblue': '#add8e6',
348 'lightcoral': '#f08080',
349 'lightcyan': '#e0ffff',
350 'lightgoldenrodyellow': '#fafad2',
351 'lightgray': '#d3d3d3',
352 'lightgrey': '#d3d3d3',
353 'lightgreen': '#90ee90',
354 'lightpink': '#ffb6c1',
355 'lightsalmon': '#ffa07a',
356 'lightseagreen': '#20b2aa',
357 'lightskyblue': '#87cefa',
358 'lightslategray': '#778899',
359 'lightslategrey': '#778899',
360 'lightsteelblue': '#b0c4de',
361 'lightyellow': '#ffffe0',
362 'lime': '#00ff00',
363 'limegreen': '#32cd32',
364 'linen': '#faf0e6',
365 'magenta': '#ff00ff',
366 'maroon': '#800000',
367 'mediumaquamarine': '#66cdaa',
368 'mediumblue': '#0000cd',
369 'mediumorchid': '#ba55d3',
370 'mediumpurple': '#9370d8',
371 'mediumseagreen': '#3cb371',
372 'mediumslateblue': '#7b68ee',
373 'mediumspringgreen': '#00fa9a',
374 'mediumturquoise': '#48d1cc',
375 'mediumvioletred': '#c71585',
376 'midnightblue': '#191970',
377 'mintcream': '#f5fffa',
378 'mistyrose': '#ffe4e1',
379 'moccasin': '#ffe4b5',
380 'navajowhite': '#ffdead',
381 'navy': '#000080',
382 'oldlace': '#fdf5e6',
383 'olive': '#808000',
384 'olivedrab': '#6b8e23',
385 'orange': '#ffa500',
386 'orangered': '#ff4500',
387 'orchid': '#da70d6',
388 'palegoldenrod': '#eee8aa',
389 'palegreen': '#98fb98',
390 'paleturquoise': '#afeeee',
391 'palevioletred': '#d87093',
392 'papayawhip': '#ffefd5',
393 'peachpuff': '#ffdab9',
394 'peru': '#cd853f',
395 'pink': '#ffc0cb',
396 'plum': '#dda0dd',
397 'powderblue': '#b0e0e6',
398 'purple': '#800080',
399 'rebeccapurple': '#663399',
400 'red': '#ff0000',
401 'rosybrown': '#bc8f8f',
402 'royalblue': '#4169e1',
403 'saddlebrown': '#8b4513',
404 'salmon': '#fa8072',
405 'sandybrown': '#f4a460',
406 'seagreen': '#2e8b57',
407 'seashell': '#fff5ee',
408 'sienna': '#a0522d',
409 'silver': '#c0c0c0',
410 'skyblue': '#87ceeb',
411 'slateblue': '#6a5acd',
412 'slategray': '#708090',
413 'slategrey': '#708090',
414 'snow': '#fffafa',
415 'springgreen': '#00ff7f',
416 'steelblue': '#4682b4',
417 'tan': '#d2b48c',
418 'teal': '#008080',
419 'thistle': '#d8bfd8',
420 'tomato': '#ff6347',
421 'turquoise': '#40e0d0',
422 'violet': '#ee82ee',
423 'wheat': '#f5deb3',
424 'white': '#ffffff',
425 'whitesmoke': '#f5f5f5',
426 'yellow': '#ffff00',
427 'yellowgreen': '#9acd32'
428 };
429
430 var unitConversions = {
431 length: {
432 'm': 1,
433 'cm': 0.01,
434 'mm': 0.001,
435 'in': 0.0254,
436 'px': 0.0254 / 96,
437 'pt': 0.0254 / 72,
438 'pc': 0.0254 / 72 * 12
439 },
440 duration: {
441 's': 1,
442 'ms': 0.001
443 },
444 angle: {
445 'rad': 1 / (2 * Math.PI),
446 'deg': 1 / 360,
447 'grad': 1 / 400,
448 'turn': 1
449 }
450 };
451
452 var data = { colors: colors, unitConversions: unitConversions };
453
454 /**
455 * The reason why Node is a class and other nodes simply do not extend
456 * from Node (since we're transpiling) is due to this issue:
457 *
458 * https://github.com/less/less.js/issues/3434
459 */
460 var Node = /** @class */ (function () {
461 function Node() {
462 this.parent = null;
463 this.visibilityBlocks = undefined;
464 this.nodeVisible = undefined;
465 this.rootNode = null;
466 this.parsed = null;
467 var self = this;
468 Object.defineProperty(this, 'currentFileInfo', {
469 get: function () { return self.fileInfo(); }
470 });
471 Object.defineProperty(this, 'index', {
472 get: function () { return self.getIndex(); }
473 });
474 }
475 Node.prototype.setParent = function (nodes, parent) {
476 function set(node) {
477 if (node && node instanceof Node) {
478 node.parent = parent;
479 }
480 }
481 if (Array.isArray(nodes)) {
482 nodes.forEach(set);
483 }
484 else {
485 set(nodes);
486 }
487 };
488 Node.prototype.getIndex = function () {
489 return this._index || (this.parent && this.parent.getIndex()) || 0;
490 };
491 Node.prototype.fileInfo = function () {
492 return this._fileInfo || (this.parent && this.parent.fileInfo()) || {};
493 };
494 Node.prototype.isRulesetLike = function () { return false; };
495 Node.prototype.toCSS = function (context) {
496 var strs = [];
497 this.genCSS(context, {
498 add: function (chunk, fileInfo, index) {
499 strs.push(chunk);
500 },
501 isEmpty: function () {
502 return strs.length === 0;
503 }
504 });
505 return strs.join('');
506 };
507 Node.prototype.genCSS = function (context, output) {
508 output.add(this.value);
509 };
510 Node.prototype.accept = function (visitor) {
511 this.value = visitor.visit(this.value);
512 };
513 Node.prototype.eval = function () { return this; };
514 Node.prototype._operate = function (context, op, a, b) {
515 switch (op) {
516 case '+': return a + b;
517 case '-': return a - b;
518 case '*': return a * b;
519 case '/': return a / b;
520 }
521 };
522 Node.prototype.fround = function (context, value) {
523 var precision = context && context.numPrecision;
524 // add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999...) are properly rounded:
525 return (precision) ? Number((value + 2e-16).toFixed(precision)) : value;
526 };
527 Node.compare = function (a, b) {
528 /* returns:
529 -1: a < b
530 0: a = b
531 1: a > b
532 and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */
533 if ((a.compare) &&
534 // for "symmetric results" force toCSS-based comparison
535 // of Quoted or Anonymous if either value is one of those
536 !(b.type === 'Quoted' || b.type === 'Anonymous')) {
537 return a.compare(b);
538 }
539 else if (b.compare) {
540 return -b.compare(a);
541 }
542 else if (a.type !== b.type) {
543 return undefined;
544 }
545 a = a.value;
546 b = b.value;
547 if (!Array.isArray(a)) {
548 return a === b ? 0 : undefined;
549 }
550 if (a.length !== b.length) {
551 return undefined;
552 }
553 for (var i = 0; i < a.length; i++) {
554 if (Node.compare(a[i], b[i]) !== 0) {
555 return undefined;
556 }
557 }
558 return 0;
559 };
560 Node.numericCompare = function (a, b) {
561 return a < b ? -1
562 : a === b ? 0
563 : a > b ? 1 : undefined;
564 };
565 // Returns true if this node represents root of ast imported by reference
566 Node.prototype.blocksVisibility = function () {
567 if (this.visibilityBlocks == null) {
568 this.visibilityBlocks = 0;
569 }
570 return this.visibilityBlocks !== 0;
571 };
572 Node.prototype.addVisibilityBlock = function () {
573 if (this.visibilityBlocks == null) {
574 this.visibilityBlocks = 0;
575 }
576 this.visibilityBlocks = this.visibilityBlocks + 1;
577 };
578 Node.prototype.removeVisibilityBlock = function () {
579 if (this.visibilityBlocks == null) {
580 this.visibilityBlocks = 0;
581 }
582 this.visibilityBlocks = this.visibilityBlocks - 1;
583 };
584 // Turns on node visibility - if called node will be shown in output regardless
585 // of whether it comes from import by reference or not
586 Node.prototype.ensureVisibility = function () {
587 this.nodeVisible = true;
588 };
589 // Turns off node visibility - if called node will NOT be shown in output regardless
590 // of whether it comes from import by reference or not
591 Node.prototype.ensureInvisibility = function () {
592 this.nodeVisible = false;
593 };
594 // return values:
595 // false - the node must not be visible
596 // true - the node must be visible
597 // undefined or null - the node has the same visibility as its parent
598 Node.prototype.isVisible = function () {
599 return this.nodeVisible;
600 };
601 Node.prototype.visibilityInfo = function () {
602 return {
603 visibilityBlocks: this.visibilityBlocks,
604 nodeVisible: this.nodeVisible
605 };
606 };
607 Node.prototype.copyVisibilityInfo = function (info) {
608 if (!info) {
609 return;
610 }
611 this.visibilityBlocks = info.visibilityBlocks;
612 this.nodeVisible = info.nodeVisible;
613 };
614 return Node;
615 }());
616
617 //
618 // RGB Colors - #ff0014, #eee
619 //
620 var Color = function (rgb, a, originalForm) {
621 var self = this;
622 //
623 // The end goal here, is to parse the arguments
624 // into an integer triplet, such as `128, 255, 0`
625 //
626 // This facilitates operations and conversions.
627 //
628 if (Array.isArray(rgb)) {
629 this.rgb = rgb;
630 }
631 else if (rgb.length >= 6) {
632 this.rgb = [];
633 rgb.match(/.{2}/g).map(function (c, i) {
634 if (i < 3) {
635 self.rgb.push(parseInt(c, 16));
636 }
637 else {
638 self.alpha = (parseInt(c, 16)) / 255;
639 }
640 });
641 }
642 else {
643 this.rgb = [];
644 rgb.split('').map(function (c, i) {
645 if (i < 3) {
646 self.rgb.push(parseInt(c + c, 16));
647 }
648 else {
649 self.alpha = (parseInt(c + c, 16)) / 255;
650 }
651 });
652 }
653 this.alpha = this.alpha || (typeof a === 'number' ? a : 1);
654 if (typeof originalForm !== 'undefined') {
655 this.value = originalForm;
656 }
657 };
658 Color.prototype = Object.assign(new Node(), {
659 type: 'Color',
660 luma: function () {
661 var r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255;
662 r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
663 g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
664 b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
665 return 0.2126 * r + 0.7152 * g + 0.0722 * b;
666 },
667 genCSS: function (context, output) {
668 output.add(this.toCSS(context));
669 },
670 toCSS: function (context, doNotCompress) {
671 var compress = context && context.compress && !doNotCompress;
672 var color;
673 var alpha;
674 var colorFunction;
675 var args = [];
676 // `value` is set if this color was originally
677 // converted from a named color string so we need
678 // to respect this and try to output named color too.
679 alpha = this.fround(context, this.alpha);
680 if (this.value) {
681 if (this.value.indexOf('rgb') === 0) {
682 if (alpha < 1) {
683 colorFunction = 'rgba';
684 }
685 }
686 else if (this.value.indexOf('hsl') === 0) {
687 if (alpha < 1) {
688 colorFunction = 'hsla';
689 }
690 else {
691 colorFunction = 'hsl';
692 }
693 }
694 else {
695 return this.value;
696 }
697 }
698 else {
699 if (alpha < 1) {
700 colorFunction = 'rgba';
701 }
702 }
703 switch (colorFunction) {
704 case 'rgba':
705 args = this.rgb.map(function (c) {
706 return clamp(Math.round(c), 255);
707 }).concat(clamp(alpha, 1));
708 break;
709 case 'hsla':
710 args.push(clamp(alpha, 1));
711 case 'hsl':
712 color = this.toHSL();
713 args = [
714 this.fround(context, color.h),
715 this.fround(context, color.s * 100) + "%",
716 this.fround(context, color.l * 100) + "%"
717 ].concat(args);
718 }
719 if (colorFunction) {
720 // Values are capped between `0` and `255`, rounded and zero-padded.
721 return colorFunction + "(" + args.join("," + (compress ? '' : ' ')) + ")";
722 }
723 color = this.toRGB();
724 if (compress) {
725 var splitcolor = color.split('');
726 // Convert color to short format
727 if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
728 color = "#" + splitcolor[1] + splitcolor[3] + splitcolor[5];
729 }
730 }
731 return color;
732 },
733 //
734 // Operations have to be done per-channel, if not,
735 // channels will spill onto each other. Once we have
736 // our result, in the form of an integer triplet,
737 // we create a new Color node to hold the result.
738 //
739 operate: function (context, op, other) {
740 var rgb = new Array(3);
741 var alpha = this.alpha * (1 - other.alpha) + other.alpha;
742 for (var c = 0; c < 3; c++) {
743 rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]);
744 }
745 return new Color(rgb, alpha);
746 },
747 toRGB: function () {
748 return toHex(this.rgb);
749 },
750 toHSL: function () {
751 var r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255, a = this.alpha;
752 var max = Math.max(r, g, b), min = Math.min(r, g, b);
753 var h;
754 var s;
755 var l = (max + min) / 2;
756 var d = max - min;
757 if (max === min) {
758 h = s = 0;
759 }
760 else {
761 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
762 switch (max) {
763 case r:
764 h = (g - b) / d + (g < b ? 6 : 0);
765 break;
766 case g:
767 h = (b - r) / d + 2;
768 break;
769 case b:
770 h = (r - g) / d + 4;
771 break;
772 }
773 h /= 6;
774 }
775 return { h: h * 360, s: s, l: l, a: a };
776 },
777 // Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
778 toHSV: function () {
779 var r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255, a = this.alpha;
780 var max = Math.max(r, g, b), min = Math.min(r, g, b);
781 var h;
782 var s;
783 var v = max;
784 var d = max - min;
785 if (max === 0) {
786 s = 0;
787 }
788 else {
789 s = d / max;
790 }
791 if (max === min) {
792 h = 0;
793 }
794 else {
795 switch (max) {
796 case r:
797 h = (g - b) / d + (g < b ? 6 : 0);
798 break;
799 case g:
800 h = (b - r) / d + 2;
801 break;
802 case b:
803 h = (r - g) / d + 4;
804 break;
805 }
806 h /= 6;
807 }
808 return { h: h * 360, s: s, v: v, a: a };
809 },
810 toARGB: function () {
811 return toHex([this.alpha * 255].concat(this.rgb));
812 },
813 compare: function (x) {
814 return (x.rgb &&
815 x.rgb[0] === this.rgb[0] &&
816 x.rgb[1] === this.rgb[1] &&
817 x.rgb[2] === this.rgb[2] &&
818 x.alpha === this.alpha) ? 0 : undefined;
819 }
820 });
821 Color.fromKeyword = function (keyword) {
822 var c;
823 var key = keyword.toLowerCase();
824 if (colors.hasOwnProperty(key)) {
825 c = new Color(colors[key].slice(1));
826 }
827 else if (key === 'transparent') {
828 c = new Color([0, 0, 0], 0);
829 }
830 if (c) {
831 c.value = keyword;
832 return c;
833 }
834 };
835 function clamp(v, max) {
836 return Math.min(Math.max(v, 0), max);
837 }
838 function toHex(v) {
839 return "#" + v.map(function (c) {
840 c = clamp(Math.round(c), 255);
841 return (c < 16 ? '0' : '') + c.toString(16);
842 }).join('');
843 }
844
845 var Paren = function (node) {
846 this.value = node;
847 };
848 Paren.prototype = Object.assign(new Node(), {
849 type: 'Paren',
850 genCSS: function (context, output) {
851 output.add('(');
852 this.value.genCSS(context, output);
853 output.add(')');
854 },
855 eval: function (context) {
856 return new Paren(this.value.eval(context));
857 }
858 });
859
860 var _noSpaceCombinators = {
861 '': true,
862 ' ': true,
863 '|': true
864 };
865 var Combinator = function (value) {
866 if (value === ' ') {
867 this.value = ' ';
868 this.emptyOrWhitespace = true;
869 }
870 else {
871 this.value = value ? value.trim() : '';
872 this.emptyOrWhitespace = this.value === '';
873 }
874 };
875 Combinator.prototype = Object.assign(new Node(), {
876 type: 'Combinator',
877 genCSS: function (context, output) {
878 var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
879 output.add(spaceOrEmpty + this.value + spaceOrEmpty);
880 }
881 });
882
883 var Element = function (combinator, value, isVariable, index, currentFileInfo, visibilityInfo) {
884 this.combinator = combinator instanceof Combinator ?
885 combinator : new Combinator(combinator);
886 if (typeof value === 'string') {
887 this.value = value.trim();
888 }
889 else if (value) {
890 this.value = value;
891 }
892 else {
893 this.value = '';
894 }
895 this.isVariable = isVariable;
896 this._index = index;
897 this._fileInfo = currentFileInfo;
898 this.copyVisibilityInfo(visibilityInfo);
899 this.setParent(this.combinator, this);
900 };
901 Element.prototype = Object.assign(new Node(), {
902 type: 'Element',
903 accept: function (visitor) {
904 var value = this.value;
905 this.combinator = visitor.visit(this.combinator);
906 if (typeof value === 'object') {
907 this.value = visitor.visit(value);
908 }
909 },
910 eval: function (context) {
911 return new Element(this.combinator, this.value.eval ? this.value.eval(context) : this.value, this.isVariable, this.getIndex(), this.fileInfo(), this.visibilityInfo());
912 },
913 clone: function () {
914 return new Element(this.combinator, this.value, this.isVariable, this.getIndex(), this.fileInfo(), this.visibilityInfo());
915 },
916 genCSS: function (context, output) {
917 output.add(this.toCSS(context), this.fileInfo(), this.getIndex());
918 },
919 toCSS: function (context) {
920 context = context || {};
921 var value = this.value;
922 var firstSelector = context.firstSelector;
923 if (value instanceof Paren) {
924 // selector in parens should not be affected by outer selector
925 // flags (breaks only interpolated selectors - see #1973)
926 context.firstSelector = true;
927 }
928 value = value.toCSS ? value.toCSS(context) : value;
929 context.firstSelector = firstSelector;
930 if (value === '' && this.combinator.value.charAt(0) === '&') {
931 return '';
932 }
933 else {
934 return this.combinator.toCSS(context) + value;
935 }
936 }
937 });
938
939 var Math$1 = {
940 ALWAYS: 0,
941 PARENS_DIVISION: 1,
942 PARENS: 2
943 // removed - STRICT_LEGACY: 3
944 };
945 var RewriteUrls = {
946 OFF: 0,
947 LOCAL: 1,
948 ALL: 2
949 };
950
951 /**
952 * Returns the object type of the given payload
953 *
954 * @param {*} payload
955 * @returns {string}
956 */
957 function getType(payload) {
958 return Object.prototype.toString.call(payload).slice(8, -1);
959 }
960 /**
961 * Returns whether the payload is a plain JavaScript object (excluding special classes or objects with other prototypes)
962 *
963 * @param {*} payload
964 * @returns {payload is Record<string, any>}
965 */
966 function isPlainObject(payload) {
967 if (getType(payload) !== 'Object')
968 return false;
969 return payload.constructor === Object && Object.getPrototypeOf(payload) === Object.prototype;
970 }
971 /**
972 * Returns whether the payload is an array
973 *
974 * @param {any} payload
975 * @returns {payload is any[]}
976 */
977 function isArray(payload) {
978 return getType(payload) === 'Array';
979 }
980
981 /*! *****************************************************************************
982 Copyright (c) Microsoft Corporation. All rights reserved.
983 Licensed under the Apache License, Version 2.0 (the "License"); you may not use
984 this file except in compliance with the License. You may obtain a copy of the
985 License at http://www.apache.org/licenses/LICENSE-2.0
986
987 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
988 KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
989 WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
990 MERCHANTABLITY OR NON-INFRINGEMENT.
991
992 See the Apache Version 2.0 License for specific language governing permissions
993 and limitations under the License.
994 ***************************************************************************** */
995
996 function __spreadArrays() {
997 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
998 for (var r = Array(s), k = 0, i = 0; i < il; i++)
999 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
1000 r[k] = a[j];
1001 return r;
1002 }
1003
1004 function assignProp(carry, key, newVal, originalObject, includeNonenumerable) {
1005 var propType = {}.propertyIsEnumerable.call(originalObject, key)
1006 ? 'enumerable'
1007 : 'nonenumerable';
1008 if (propType === 'enumerable')
1009 carry[key] = newVal;
1010 if (includeNonenumerable && propType === 'nonenumerable') {
1011 Object.defineProperty(carry, key, {
1012 value: newVal,
1013 enumerable: false,
1014 writable: true,
1015 configurable: true,
1016 });
1017 }
1018 }
1019 /**
1020 * Copy (clone) an object and all its props recursively to get rid of any prop referenced of the original object. Arrays are also cloned, however objects inside arrays are still linked.
1021 *
1022 * @export
1023 * @template T
1024 * @param {T} target Target can be anything
1025 * @param {Options} [options={}] Options can be `props` or `nonenumerable`
1026 * @returns {T} the target with replaced values
1027 * @export
1028 */
1029 function copy(target, options) {
1030 if (options === void 0) { options = {}; }
1031 if (isArray(target))
1032 return target.map(function (i) { return copy(i, options); });
1033 if (!isPlainObject(target))
1034 return target;
1035 var props = Object.getOwnPropertyNames(target);
1036 var symbols = Object.getOwnPropertySymbols(target);
1037 return __spreadArrays(props, symbols).reduce(function (carry, key) {
1038 if (isArray(options.props) && !options.props.includes(key)) {
1039 return carry;
1040 }
1041 var val = target[key];
1042 var newVal = copy(val, options);
1043 assignProp(carry, key, newVal, target, options.nonenumerable);
1044 return carry;
1045 }, {});
1046 }
1047
1048 /* jshint proto: true */
1049 function getLocation(index, inputStream) {
1050 var n = index + 1;
1051 var line = null;
1052 var column = -1;
1053 while (--n >= 0 && inputStream.charAt(n) !== '\n') {
1054 column++;
1055 }
1056 if (typeof index === 'number') {
1057 line = (inputStream.slice(0, index).match(/\n/g) || '').length;
1058 }
1059 return {
1060 line: line,
1061 column: column
1062 };
1063 }
1064 function copyArray(arr) {
1065 var i;
1066 var length = arr.length;
1067 var copy = new Array(length);
1068 for (i = 0; i < length; i++) {
1069 copy[i] = arr[i];
1070 }
1071 return copy;
1072 }
1073 function clone(obj) {
1074 var cloned = {};
1075 for (var prop in obj) {
1076 if (obj.hasOwnProperty(prop)) {
1077 cloned[prop] = obj[prop];
1078 }
1079 }
1080 return cloned;
1081 }
1082 function defaults(obj1, obj2) {
1083 var newObj = obj2 || {};
1084 if (!obj2._defaults) {
1085 newObj = {};
1086 var defaults_1 = copy(obj1);
1087 newObj._defaults = defaults_1;
1088 var cloned = obj2 ? copy(obj2) : {};
1089 Object.assign(newObj, defaults_1, cloned);
1090 }
1091 return newObj;
1092 }
1093 function copyOptions(obj1, obj2) {
1094 if (obj2 && obj2._defaults) {
1095 return obj2;
1096 }
1097 var opts = defaults(obj1, obj2);
1098 if (opts.strictMath) {
1099 opts.math = Math$1.PARENS;
1100 }
1101 // Back compat with changed relativeUrls option
1102 if (opts.relativeUrls) {
1103 opts.rewriteUrls = RewriteUrls.ALL;
1104 }
1105 if (typeof opts.math === 'string') {
1106 switch (opts.math.toLowerCase()) {
1107 case 'always':
1108 opts.math = Math$1.ALWAYS;
1109 break;
1110 case 'parens-division':
1111 opts.math = Math$1.PARENS_DIVISION;
1112 break;
1113 case 'strict':
1114 case 'parens':
1115 opts.math = Math$1.PARENS;
1116 break;
1117 default:
1118 opts.math = Math$1.PARENS;
1119 }
1120 }
1121 if (typeof opts.rewriteUrls === 'string') {
1122 switch (opts.rewriteUrls.toLowerCase()) {
1123 case 'off':
1124 opts.rewriteUrls = RewriteUrls.OFF;
1125 break;
1126 case 'local':
1127 opts.rewriteUrls = RewriteUrls.LOCAL;
1128 break;
1129 case 'all':
1130 opts.rewriteUrls = RewriteUrls.ALL;
1131 break;
1132 }
1133 }
1134 return opts;
1135 }
1136 function merge(obj1, obj2) {
1137 for (var prop in obj2) {
1138 if (obj2.hasOwnProperty(prop)) {
1139 obj1[prop] = obj2[prop];
1140 }
1141 }
1142 return obj1;
1143 }
1144 function flattenArray(arr, result) {
1145 if (result === void 0) { result = []; }
1146 for (var i = 0, length_1 = arr.length; i < length_1; i++) {
1147 var value = arr[i];
1148 if (Array.isArray(value)) {
1149 flattenArray(value, result);
1150 }
1151 else {
1152 if (value !== undefined) {
1153 result.push(value);
1154 }
1155 }
1156 }
1157 return result;
1158 }
1159
1160 var utils = /*#__PURE__*/Object.freeze({
1161 __proto__: null,
1162 getLocation: getLocation,
1163 copyArray: copyArray,
1164 clone: clone,
1165 defaults: defaults,
1166 copyOptions: copyOptions,
1167 merge: merge,
1168 flattenArray: flattenArray
1169 });
1170
1171 var anonymousFunc = /(<anonymous>|Function):(\d+):(\d+)/;
1172 /**
1173 * This is a centralized class of any error that could be thrown internally (mostly by the parser).
1174 * Besides standard .message it keeps some additional data like a path to the file where the error
1175 * occurred along with line and column numbers.
1176 *
1177 * @class
1178 * @extends Error
1179 * @type {module.LessError}
1180 *
1181 * @prop {string} type
1182 * @prop {string} filename
1183 * @prop {number} index
1184 * @prop {number} line
1185 * @prop {number} column
1186 * @prop {number} callLine
1187 * @prop {number} callExtract
1188 * @prop {string[]} extract
1189 *
1190 * @param {Object} e - An error object to wrap around or just a descriptive object
1191 * @param {Object} fileContentMap - An object with file contents in 'contents' property (like importManager) @todo - move to fileManager?
1192 * @param {string} [currentFilename]
1193 */
1194 var LessError = function (e, fileContentMap, currentFilename) {
1195 Error.call(this);
1196 var filename = e.filename || currentFilename;
1197 this.message = e.message;
1198 this.stack = e.stack;
1199 if (fileContentMap && filename) {
1200 var input = fileContentMap.contents[filename];
1201 var loc = getLocation(e.index, input);
1202 var line = loc.line;
1203 var col = loc.column;
1204 var callLine = e.call && getLocation(e.call, input).line;
1205 var lines = input ? input.split('\n') : '';
1206 this.type = e.type || 'Syntax';
1207 this.filename = filename;
1208 this.index = e.index;
1209 this.line = typeof line === 'number' ? line + 1 : null;
1210 this.column = col;
1211 if (!this.line && this.stack) {
1212 var found = this.stack.match(anonymousFunc);
1213 /**
1214 * We have to figure out how this environment stringifies anonymous functions
1215 * so we can correctly map plugin errors.
1216 *
1217 * Note, in Node 8, the output of anonymous funcs varied based on parameters
1218 * being present or not, so we inject dummy params.
1219 */
1220 var func = new Function('a', 'throw new Error()');
1221 var lineAdjust = 0;
1222 try {
1223 func();
1224 }
1225 catch (e) {
1226 var match = e.stack.match(anonymousFunc);
1227 var line = parseInt(match[2]);
1228 lineAdjust = 1 - line;
1229 }
1230 if (found) {
1231 if (found[2]) {
1232 this.line = parseInt(found[2]) + lineAdjust;
1233 }
1234 if (found[3]) {
1235 this.column = parseInt(found[3]);
1236 }
1237 }
1238 }
1239 this.callLine = callLine + 1;
1240 this.callExtract = lines[callLine];
1241 this.extract = [
1242 lines[this.line - 2],
1243 lines[this.line - 1],
1244 lines[this.line]
1245 ];
1246 }
1247 };
1248 if (typeof Object.create === 'undefined') {
1249 var F = function () { };
1250 F.prototype = Error.prototype;
1251 LessError.prototype = new F();
1252 }
1253 else {
1254 LessError.prototype = Object.create(Error.prototype);
1255 }
1256 LessError.prototype.constructor = LessError;
1257 /**
1258 * An overridden version of the default Object.prototype.toString
1259 * which uses additional information to create a helpful message.
1260 *
1261 * @param {Object} options
1262 * @returns {string}
1263 */
1264 LessError.prototype.toString = function (options) {
1265 options = options || {};
1266 var message = '';
1267 var extract = this.extract || [];
1268 var error = [];
1269 var stylize = function (str) { return str; };
1270 if (options.stylize) {
1271 var type = typeof options.stylize;
1272 if (type !== 'function') {
1273 throw Error("options.stylize should be a function, got a " + type + "!");
1274 }
1275 stylize = options.stylize;
1276 }
1277 if (this.line !== null) {
1278 if (typeof extract[0] === 'string') {
1279 error.push(stylize(this.line - 1 + " " + extract[0], 'grey'));
1280 }
1281 if (typeof extract[1] === 'string') {
1282 var errorTxt = this.line + " ";
1283 if (extract[1]) {
1284 errorTxt += extract[1].slice(0, this.column) +
1285 stylize(stylize(stylize(extract[1].substr(this.column, 1), 'bold') +
1286 extract[1].slice(this.column + 1), 'red'), 'inverse');
1287 }
1288 error.push(errorTxt);
1289 }
1290 if (typeof extract[2] === 'string') {
1291 error.push(stylize(this.line + 1 + " " + extract[2], 'grey'));
1292 }
1293 error = error.join('\n') + stylize('', 'reset') + "\n";
1294 }
1295 message += stylize(this.type + "Error: " + this.message, 'red');
1296 if (this.filename) {
1297 message += stylize(' in ', 'red') + this.filename;
1298 }
1299 if (this.line) {
1300 message += stylize(" on line " + this.line + ", column " + (this.column + 1) + ":", 'grey');
1301 }
1302 message += "\n" + error;
1303 if (this.callLine) {
1304 message += stylize('from ', 'red') + (this.filename || '') + "/n";
1305 message += stylize(this.callLine, 'grey') + " " + this.callExtract + "/n";
1306 }
1307 return message;
1308 };
1309
1310 var Selector = function (elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
1311 this.extendList = extendList;
1312 this.condition = condition;
1313 this.evaldCondition = !condition;
1314 this._index = index;
1315 this._fileInfo = currentFileInfo;
1316 this.elements = this.getElements(elements);
1317 this.mixinElements_ = undefined;
1318 this.copyVisibilityInfo(visibilityInfo);
1319 this.setParent(this.elements, this);
1320 };
1321 Selector.prototype = Object.assign(new Node(), {
1322 type: 'Selector',
1323 accept: function (visitor) {
1324 if (this.elements) {
1325 this.elements = visitor.visitArray(this.elements);
1326 }
1327 if (this.extendList) {
1328 this.extendList = visitor.visitArray(this.extendList);
1329 }
1330 if (this.condition) {
1331 this.condition = visitor.visit(this.condition);
1332 }
1333 },
1334 createDerived: function (elements, extendList, evaldCondition) {
1335 elements = this.getElements(elements);
1336 var newSelector = new Selector(elements, extendList || this.extendList, null, this.getIndex(), this.fileInfo(), this.visibilityInfo());
1337 newSelector.evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
1338 newSelector.mediaEmpty = this.mediaEmpty;
1339 return newSelector;
1340 },
1341 getElements: function (els) {
1342 if (!els) {
1343 return [new Element('', '&', false, this._index, this._fileInfo)];
1344 }
1345 if (typeof els === 'string') {
1346 this.parse.parseNode(els, ['selector'], this._index, this._fileInfo, function (err, result) {
1347 if (err) {
1348 throw new LessError({
1349 index: err.index,
1350 message: err.message
1351 }, this.parse.imports, this._fileInfo.filename);
1352 }
1353 els = result[0].elements;
1354 });
1355 }
1356 return els;
1357 },
1358 createEmptySelectors: function () {
1359 var el = new Element('', '&', false, this._index, this._fileInfo), sels = [new Selector([el], null, null, this._index, this._fileInfo)];
1360 sels[0].mediaEmpty = true;
1361 return sels;
1362 },
1363 match: function (other) {
1364 var elements = this.elements;
1365 var len = elements.length;
1366 var olen;
1367 var i;
1368 other = other.mixinElements();
1369 olen = other.length;
1370 if (olen === 0 || len < olen) {
1371 return 0;
1372 }
1373 else {
1374 for (i = 0; i < olen; i++) {
1375 if (elements[i].value !== other[i]) {
1376 return 0;
1377 }
1378 }
1379 }
1380 return olen; // return number of matched elements
1381 },
1382 mixinElements: function () {
1383 if (this.mixinElements_) {
1384 return this.mixinElements_;
1385 }
1386 var elements = this.elements.map(function (v) {
1387 return v.combinator.value + (v.value.value || v.value);
1388 }).join('').match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
1389 if (elements) {
1390 if (elements[0] === '&') {
1391 elements.shift();
1392 }
1393 }
1394 else {
1395 elements = [];
1396 }
1397 return (this.mixinElements_ = elements);
1398 },
1399 isJustParentSelector: function () {
1400 return !this.mediaEmpty &&
1401 this.elements.length === 1 &&
1402 this.elements[0].value === '&' &&
1403 (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
1404 },
1405 eval: function (context) {
1406 var evaldCondition = this.condition && this.condition.eval(context);
1407 var elements = this.elements;
1408 var extendList = this.extendList;
1409 elements = elements && elements.map(function (e) { return e.eval(context); });
1410 extendList = extendList && extendList.map(function (extend) { return extend.eval(context); });
1411 return this.createDerived(elements, extendList, evaldCondition);
1412 },
1413 genCSS: function (context, output) {
1414 var i, element;
1415 if ((!context || !context.firstSelector) && this.elements[0].combinator.value === '') {
1416 output.add(' ', this.fileInfo(), this.getIndex());
1417 }
1418 for (i = 0; i < this.elements.length; i++) {
1419 element = this.elements[i];
1420 element.genCSS(context, output);
1421 }
1422 },
1423 getIsOutput: function () {
1424 return this.evaldCondition;
1425 }
1426 });
1427
1428 var Value = function (value) {
1429 if (!value) {
1430 throw new Error('Value requires an array argument');
1431 }
1432 if (!Array.isArray(value)) {
1433 this.value = [value];
1434 }
1435 else {
1436 this.value = value;
1437 }
1438 };
1439 Value.prototype = Object.assign(new Node(), {
1440 type: 'Value',
1441 accept: function (visitor) {
1442 if (this.value) {
1443 this.value = visitor.visitArray(this.value);
1444 }
1445 },
1446 eval: function (context) {
1447 if (this.value.length === 1) {
1448 return this.value[0].eval(context);
1449 }
1450 else {
1451 return new Value(this.value.map(function (v) {
1452 return v.eval(context);
1453 }));
1454 }
1455 },
1456 genCSS: function (context, output) {
1457 var i;
1458 for (i = 0; i < this.value.length; i++) {
1459 this.value[i].genCSS(context, output);
1460 if (i + 1 < this.value.length) {
1461 output.add((context && context.compress) ? ',' : ', ');
1462 }
1463 }
1464 }
1465 });
1466
1467 var Keyword = function (value) {
1468 this.value = value;
1469 };
1470 Keyword.prototype = Object.assign(new Node(), {
1471 type: 'Keyword',
1472 genCSS: function (context, output) {
1473 if (this.value === '%') {
1474 throw { type: 'Syntax', message: 'Invalid % without number' };
1475 }
1476 output.add(this.value);
1477 }
1478 });
1479 Keyword.True = new Keyword('true');
1480 Keyword.False = new Keyword('false');
1481
1482 var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike, visibilityInfo) {
1483 this.value = value;
1484 this._index = index;
1485 this._fileInfo = currentFileInfo;
1486 this.mapLines = mapLines;
1487 this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
1488 this.allowRoot = true;
1489 this.copyVisibilityInfo(visibilityInfo);
1490 };
1491 Anonymous.prototype = Object.assign(new Node(), {
1492 type: 'Anonymous',
1493 eval: function () {
1494 return new Anonymous(this.value, this._index, this._fileInfo, this.mapLines, this.rulesetLike, this.visibilityInfo());
1495 },
1496 compare: function (other) {
1497 return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
1498 },
1499 isRulesetLike: function () {
1500 return this.rulesetLike;
1501 },
1502 genCSS: function (context, output) {
1503 this.nodeVisible = Boolean(this.value);
1504 if (this.nodeVisible) {
1505 output.add(this.value, this._fileInfo, this._index, this.mapLines);
1506 }
1507 }
1508 });
1509
1510 var MATH = Math$1;
1511 function evalName(context, name) {
1512 var value = '';
1513 var i;
1514 var n = name.length;
1515 var output = { add: function (s) { value += s; } };
1516 for (i = 0; i < n; i++) {
1517 name[i].eval(context).genCSS(context, output);
1518 }
1519 return value;
1520 }
1521 var Declaration = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
1522 this.name = name;
1523 this.value = (value instanceof Node) ? value : new Value([value ? new Anonymous(value) : null]);
1524 this.important = important ? " " + important.trim() : '';
1525 this.merge = merge;
1526 this._index = index;
1527 this._fileInfo = currentFileInfo;
1528 this.inline = inline || false;
1529 this.variable = (variable !== undefined) ? variable
1530 : (name.charAt && (name.charAt(0) === '@'));
1531 this.allowRoot = true;
1532 this.setParent(this.value, this);
1533 };
1534 Declaration.prototype = Object.assign(new Node(), {
1535 type: 'Declaration',
1536 genCSS: function (context, output) {
1537 output.add(this.name + (context.compress ? ':' : ': '), this.fileInfo(), this.getIndex());
1538 try {
1539 this.value.genCSS(context, output);
1540 }
1541 catch (e) {
1542 e.index = this._index;
1543 e.filename = this._fileInfo.filename;
1544 throw e;
1545 }
1546 output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? '' : ';'), this._fileInfo, this._index);
1547 },
1548 eval: function (context) {
1549 var mathBypass = false, prevMath, name = this.name, evaldValue, variable = this.variable;
1550 if (typeof name !== 'string') {
1551 // expand 'primitive' name directly to get
1552 // things faster (~10% for benchmark.less):
1553 name = (name.length === 1) && (name[0] instanceof Keyword) ?
1554 name[0].value : evalName(context, name);
1555 variable = false; // never treat expanded interpolation as new variable name
1556 }
1557 // @todo remove when parens-division is default
1558 if (name === 'font' && context.math === MATH.ALWAYS) {
1559 mathBypass = true;
1560 prevMath = context.math;
1561 context.math = MATH.PARENS_DIVISION;
1562 }
1563 try {
1564 context.importantScope.push({});
1565 evaldValue = this.value.eval(context);
1566 if (!this.variable && evaldValue.type === 'DetachedRuleset') {
1567 throw { message: 'Rulesets cannot be evaluated on a property.',
1568 index: this.getIndex(), filename: this.fileInfo().filename };
1569 }
1570 var important = this.important;
1571 var importantResult = context.importantScope.pop();
1572 if (!important && importantResult.important) {
1573 important = importantResult.important;
1574 }
1575 return new Declaration(name, evaldValue, important, this.merge, this.getIndex(), this.fileInfo(), this.inline, variable);
1576 }
1577 catch (e) {
1578 if (typeof e.index !== 'number') {
1579 e.index = this.getIndex();
1580 e.filename = this.fileInfo().filename;
1581 }
1582 throw e;
1583 }
1584 finally {
1585 if (mathBypass) {
1586 context.math = prevMath;
1587 }
1588 }
1589 },
1590 makeImportant: function () {
1591 return new Declaration(this.name, this.value, '!important', this.merge, this.getIndex(), this.fileInfo(), this.inline);
1592 }
1593 });
1594
1595 var debugInfo = /** @class */ (function () {
1596 function debugInfo(context, ctx, lineSeparator) {
1597 var result = '';
1598 if (context.dumpLineNumbers && !context.compress) {
1599 switch (context.dumpLineNumbers) {
1600 case 'comments':
1601 result = debugInfo.asComment(ctx);
1602 break;
1603 case 'mediaquery':
1604 result = debugInfo.asMediaQuery(ctx);
1605 break;
1606 case 'all':
1607 result = debugInfo.asComment(ctx) + (lineSeparator || '') + debugInfo.asMediaQuery(ctx);
1608 break;
1609 }
1610 }
1611 return result;
1612 }
1613 debugInfo.asComment = function (ctx) {
1614 return "/* line " + ctx.debugInfo.lineNumber + ", " + ctx.debugInfo.fileName + " */\n";
1615 };
1616 debugInfo.asMediaQuery = function (ctx) {
1617 var filenameWithProtocol = ctx.debugInfo.fileName;
1618 if (!/^[a-z]+:\/\//i.test(filenameWithProtocol)) {
1619 filenameWithProtocol = "file://" + filenameWithProtocol;
1620 }
1621 return "@media -sass-debug-info{filename{font-family:" + filenameWithProtocol.replace(/([.:\/\\])/g, function (a) {
1622 if (a == '\\') {
1623 a = '\/';
1624 }
1625 return "\\" + a;
1626 }) + "}line{font-family:\\00003" + ctx.debugInfo.lineNumber + "}}\n";
1627 };
1628 return debugInfo;
1629 }());
1630
1631 var Comment = function (value, isLineComment, index, currentFileInfo) {
1632 this.value = value;
1633 this.isLineComment = isLineComment;
1634 this._index = index;
1635 this._fileInfo = currentFileInfo;
1636 this.allowRoot = true;
1637 };
1638 Comment.prototype = Object.assign(new Node(), {
1639 type: 'Comment',
1640 genCSS: function (context, output) {
1641 if (this.debugInfo) {
1642 output.add(debugInfo(context, this), this.fileInfo(), this.getIndex());
1643 }
1644 output.add(this.value);
1645 },
1646 isSilent: function (context) {
1647 var isCompressed = context.compress && this.value[2] !== '!';
1648 return this.isLineComment || isCompressed;
1649 }
1650 });
1651
1652 var contexts = {};
1653 var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
1654 if (!original) {
1655 return;
1656 }
1657 for (var i = 0; i < propertiesToCopy.length; i++) {
1658 if (original.hasOwnProperty(propertiesToCopy[i])) {
1659 destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
1660 }
1661 }
1662 };
1663 /*
1664 parse is used whilst parsing
1665 */
1666 var parseCopyProperties = [
1667 // options
1668 'paths',
1669 'rewriteUrls',
1670 'rootpath',
1671 'strictImports',
1672 'insecure',
1673 'dumpLineNumbers',
1674 'compress',
1675 'syncImport',
1676 'chunkInput',
1677 'mime',
1678 'useFileCache',
1679 // context
1680 'processImports',
1681 // Used by the import manager to stop multiple import visitors being created.
1682 'pluginManager' // Used as the plugin manager for the session
1683 ];
1684 contexts.Parse = function (options) {
1685 copyFromOriginal(options, this, parseCopyProperties);
1686 if (typeof this.paths === 'string') {
1687 this.paths = [this.paths];
1688 }
1689 };
1690 var evalCopyProperties = [
1691 'paths',
1692 'compress',
1693 'math',
1694 'strictUnits',
1695 'sourceMap',
1696 'importMultiple',
1697 'urlArgs',
1698 'javascriptEnabled',
1699 'pluginManager',
1700 'importantScope',
1701 'rewriteUrls' // option - whether to adjust URL's to be relative
1702 ];
1703 contexts.Eval = function (options, frames) {
1704 copyFromOriginal(options, this, evalCopyProperties);
1705 if (typeof this.paths === 'string') {
1706 this.paths = [this.paths];
1707 }
1708 this.frames = frames || [];
1709 this.importantScope = this.importantScope || [];
1710 };
1711 contexts.Eval.prototype.enterCalc = function () {
1712 if (!this.calcStack) {
1713 this.calcStack = [];
1714 }
1715 this.calcStack.push(true);
1716 this.inCalc = true;
1717 };
1718 contexts.Eval.prototype.exitCalc = function () {
1719 this.calcStack.pop();
1720 if (!this.calcStack.length) {
1721 this.inCalc = false;
1722 }
1723 };
1724 contexts.Eval.prototype.inParenthesis = function () {
1725 if (!this.parensStack) {
1726 this.parensStack = [];
1727 }
1728 this.parensStack.push(true);
1729 };
1730 contexts.Eval.prototype.outOfParenthesis = function () {
1731 this.parensStack.pop();
1732 };
1733 contexts.Eval.prototype.inCalc = false;
1734 contexts.Eval.prototype.mathOn = true;
1735 contexts.Eval.prototype.isMathOn = function (op) {
1736 if (!this.mathOn) {
1737 return false;
1738 }
1739 if (op === '/' && this.math !== Math$1.ALWAYS && (!this.parensStack || !this.parensStack.length)) {
1740 return false;
1741 }
1742 if (this.math > Math$1.PARENS_DIVISION) {
1743 return this.parensStack && this.parensStack.length;
1744 }
1745 return true;
1746 };
1747 contexts.Eval.prototype.pathRequiresRewrite = function (path) {
1748 var isRelative = this.rewriteUrls === RewriteUrls.LOCAL ? isPathLocalRelative : isPathRelative;
1749 return isRelative(path);
1750 };
1751 contexts.Eval.prototype.rewritePath = function (path, rootpath) {
1752 var newPath;
1753 rootpath = rootpath || '';
1754 newPath = this.normalizePath(rootpath + path);
1755 // If a path was explicit relative and the rootpath was not an absolute path
1756 // we must ensure that the new path is also explicit relative.
1757 if (isPathLocalRelative(path) &&
1758 isPathRelative(rootpath) &&
1759 isPathLocalRelative(newPath) === false) {
1760 newPath = "./" + newPath;
1761 }
1762 return newPath;
1763 };
1764 contexts.Eval.prototype.normalizePath = function (path) {
1765 var segments = path.split('/').reverse();
1766 var segment;
1767 path = [];
1768 while (segments.length !== 0) {
1769 segment = segments.pop();
1770 switch (segment) {
1771 case '.':
1772 break;
1773 case '..':
1774 if ((path.length === 0) || (path[path.length - 1] === '..')) {
1775 path.push(segment);
1776 }
1777 else {
1778 path.pop();
1779 }
1780 break;
1781 default:
1782 path.push(segment);
1783 break;
1784 }
1785 }
1786 return path.join('/');
1787 };
1788 function isPathRelative(path) {
1789 return !/^(?:[a-z-]+:|\/|#)/i.test(path);
1790 }
1791 function isPathLocalRelative(path) {
1792 return path.charAt(0) === '.';
1793 }
1794 // todo - do the same for the toCSS ?
1795
1796 function makeRegistry(base) {
1797 return {
1798 _data: {},
1799 add: function (name, func) {
1800 // precautionary case conversion, as later querying of
1801 // the registry by function-caller uses lower case as well.
1802 name = name.toLowerCase();
1803 if (this._data.hasOwnProperty(name)) ;
1804 this._data[name] = func;
1805 },
1806 addMultiple: function (functions) {
1807 var _this = this;
1808 Object.keys(functions).forEach(function (name) {
1809 _this.add(name, functions[name]);
1810 });
1811 },
1812 get: function (name) {
1813 return this._data[name] || (base && base.get(name));
1814 },
1815 getLocalFunctions: function () {
1816 return this._data;
1817 },
1818 inherit: function () {
1819 return makeRegistry(this);
1820 },
1821 create: function (base) {
1822 return makeRegistry(base);
1823 }
1824 };
1825 }
1826 var functionRegistry = makeRegistry(null);
1827
1828 var defaultFunc = {
1829 eval: function () {
1830 var v = this.value_;
1831 var e = this.error_;
1832 if (e) {
1833 throw e;
1834 }
1835 if (v != null) {
1836 return v ? Keyword.True : Keyword.False;
1837 }
1838 },
1839 value: function (v) {
1840 this.value_ = v;
1841 },
1842 error: function (e) {
1843 this.error_ = e;
1844 },
1845 reset: function () {
1846 this.value_ = this.error_ = null;
1847 }
1848 };
1849
1850 var Ruleset = function (selectors, rules, strictImports, visibilityInfo) {
1851 this.selectors = selectors;
1852 this.rules = rules;
1853 this._lookups = {};
1854 this._variables = null;
1855 this._properties = null;
1856 this.strictImports = strictImports;
1857 this.copyVisibilityInfo(visibilityInfo);
1858 this.allowRoot = true;
1859 this.setParent(this.selectors, this);
1860 this.setParent(this.rules, this);
1861 };
1862 Ruleset.prototype = Object.assign(new Node(), {
1863 type: 'Ruleset',
1864 isRuleset: true,
1865 isRulesetLike: function () { return true; },
1866 accept: function (visitor) {
1867 if (this.paths) {
1868 this.paths = visitor.visitArray(this.paths, true);
1869 }
1870 else if (this.selectors) {
1871 this.selectors = visitor.visitArray(this.selectors);
1872 }
1873 if (this.rules && this.rules.length) {
1874 this.rules = visitor.visitArray(this.rules);
1875 }
1876 },
1877 eval: function (context) {
1878 var selectors;
1879 var selCnt;
1880 var selector;
1881 var i;
1882 var hasVariable;
1883 var hasOnePassingSelector = false;
1884 if (this.selectors && (selCnt = this.selectors.length)) {
1885 selectors = new Array(selCnt);
1886 defaultFunc.error({
1887 type: 'Syntax',
1888 message: 'it is currently only allowed in parametric mixin guards,'
1889 });
1890 for (i = 0; i < selCnt; i++) {
1891 selector = this.selectors[i].eval(context);
1892 for (var j = 0; j < selector.elements.length; j++) {
1893 if (selector.elements[j].isVariable) {
1894 hasVariable = true;
1895 break;
1896 }
1897 }
1898 selectors[i] = selector;
1899 if (selector.evaldCondition) {
1900 hasOnePassingSelector = true;
1901 }
1902 }
1903 if (hasVariable) {
1904 var toParseSelectors = new Array(selCnt);
1905 for (i = 0; i < selCnt; i++) {
1906 selector = selectors[i];
1907 toParseSelectors[i] = selector.toCSS(context);
1908 }
1909 this.parse.parseNode(toParseSelectors.join(','), ["selectors"], selectors[0].getIndex(), selectors[0].fileInfo(), function (err, result) {
1910 if (result) {
1911 selectors = flattenArray(result);
1912 }
1913 });
1914 }
1915 defaultFunc.reset();
1916 }
1917 else {
1918 hasOnePassingSelector = true;
1919 }
1920 var rules = this.rules ? copyArray(this.rules) : null;
1921 var ruleset = new Ruleset(selectors, rules, this.strictImports, this.visibilityInfo());
1922 var rule;
1923 var subRule;
1924 ruleset.originalRuleset = this;
1925 ruleset.root = this.root;
1926 ruleset.firstRoot = this.firstRoot;
1927 ruleset.allowImports = this.allowImports;
1928 if (this.debugInfo) {
1929 ruleset.debugInfo = this.debugInfo;
1930 }
1931 if (!hasOnePassingSelector) {
1932 rules.length = 0;
1933 }
1934 // inherit a function registry from the frames stack when possible;
1935 // otherwise from the global registry
1936 ruleset.functionRegistry = (function (frames) {
1937 var i = 0;
1938 var n = frames.length;
1939 var found;
1940 for (; i !== n; ++i) {
1941 found = frames[i].functionRegistry;
1942 if (found) {
1943 return found;
1944 }
1945 }
1946 return functionRegistry;
1947 }(context.frames)).inherit();
1948 // push the current ruleset to the frames stack
1949 var ctxFrames = context.frames;
1950 ctxFrames.unshift(ruleset);
1951 // currrent selectors
1952 var ctxSelectors = context.selectors;
1953 if (!ctxSelectors) {
1954 context.selectors = ctxSelectors = [];
1955 }
1956 ctxSelectors.unshift(this.selectors);
1957 // Evaluate imports
1958 if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
1959 ruleset.evalImports(context);
1960 }
1961 // Store the frames around mixin definitions,
1962 // so they can be evaluated like closures when the time comes.
1963 var rsRules = ruleset.rules;
1964 for (i = 0; (rule = rsRules[i]); i++) {
1965 if (rule.evalFirst) {
1966 rsRules[i] = rule.eval(context);
1967 }
1968 }
1969 var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
1970 // Evaluate mixin calls.
1971 for (i = 0; (rule = rsRules[i]); i++) {
1972 if (rule.type === 'MixinCall') {
1973 /* jshint loopfunc:true */
1974 rules = rule.eval(context).filter(function (r) {
1975 if ((r instanceof Declaration) && r.variable) {
1976 // do not pollute the scope if the variable is
1977 // already there. consider returning false here
1978 // but we need a way to "return" variable from mixins
1979 return !(ruleset.variable(r.name));
1980 }
1981 return true;
1982 });
1983 rsRules.splice.apply(rsRules, [i, 1].concat(rules));
1984 i += rules.length - 1;
1985 ruleset.resetCache();
1986 }
1987 else if (rule.type === 'VariableCall') {
1988 /* jshint loopfunc:true */
1989 rules = rule.eval(context).rules.filter(function (r) {
1990 if ((r instanceof Declaration) && r.variable) {
1991 // do not pollute the scope at all
1992 return false;
1993 }
1994 return true;
1995 });
1996 rsRules.splice.apply(rsRules, [i, 1].concat(rules));
1997 i += rules.length - 1;
1998 ruleset.resetCache();
1999 }
2000 }
2001 // Evaluate everything else
2002 for (i = 0; (rule = rsRules[i]); i++) {
2003 if (!rule.evalFirst) {
2004 rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
2005 }
2006 }
2007 // Evaluate everything else
2008 for (i = 0; (rule = rsRules[i]); i++) {
2009 // for rulesets, check if it is a css guard and can be removed
2010 if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
2011 // check if it can be folded in (e.g. & where)
2012 if (rule.selectors[0] && rule.selectors[0].isJustParentSelector()) {
2013 rsRules.splice(i--, 1);
2014 for (var j = 0; (subRule = rule.rules[j]); j++) {
2015 if (subRule instanceof Node) {
2016 subRule.copyVisibilityInfo(rule.visibilityInfo());
2017 if (!(subRule instanceof Declaration) || !subRule.variable) {
2018 rsRules.splice(++i, 0, subRule);
2019 }
2020 }
2021 }
2022 }
2023 }
2024 }
2025 // Pop the stack
2026 ctxFrames.shift();
2027 ctxSelectors.shift();
2028 if (context.mediaBlocks) {
2029 for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) {
2030 context.mediaBlocks[i].bubbleSelectors(selectors);
2031 }
2032 }
2033 return ruleset;
2034 },
2035 evalImports: function (context) {
2036 var rules = this.rules;
2037 var i;
2038 var importRules;
2039 if (!rules) {
2040 return;
2041 }
2042 for (i = 0; i < rules.length; i++) {
2043 if (rules[i].type === 'Import') {
2044 importRules = rules[i].eval(context);
2045 if (importRules && (importRules.length || importRules.length === 0)) {
2046 rules.splice.apply(rules, [i, 1].concat(importRules));
2047 i += importRules.length - 1;
2048 }
2049 else {
2050 rules.splice(i, 1, importRules);
2051 }
2052 this.resetCache();
2053 }
2054 }
2055 },
2056 makeImportant: function () {
2057 var result = new Ruleset(this.selectors, this.rules.map(function (r) {
2058 if (r.makeImportant) {
2059 return r.makeImportant();
2060 }
2061 else {
2062 return r;
2063 }
2064 }), this.strictImports, this.visibilityInfo());
2065 return result;
2066 },
2067 matchArgs: function (args) {
2068 return !args || args.length === 0;
2069 },
2070 // lets you call a css selector with a guard
2071 matchCondition: function (args, context) {
2072 var lastSelector = this.selectors[this.selectors.length - 1];
2073 if (!lastSelector.evaldCondition) {
2074 return false;
2075 }
2076 if (lastSelector.condition &&
2077 !lastSelector.condition.eval(new contexts.Eval(context, context.frames))) {
2078 return false;
2079 }
2080 return true;
2081 },
2082 resetCache: function () {
2083 this._rulesets = null;
2084 this._variables = null;
2085 this._properties = null;
2086 this._lookups = {};
2087 },
2088 variables: function () {
2089 if (!this._variables) {
2090 this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
2091 if (r instanceof Declaration && r.variable === true) {
2092 hash[r.name] = r;
2093 }
2094 // when evaluating variables in an import statement, imports have not been eval'd
2095 // so we need to go inside import statements.
2096 // guard against root being a string (in the case of inlined less)
2097 if (r.type === 'Import' && r.root && r.root.variables) {
2098 var vars = r.root.variables();
2099 for (var name_1 in vars) {
2100 if (vars.hasOwnProperty(name_1)) {
2101 hash[name_1] = r.root.variable(name_1);
2102 }
2103 }
2104 }
2105 return hash;
2106 }, {});
2107 }
2108 return this._variables;
2109 },
2110 properties: function () {
2111 if (!this._properties) {
2112 this._properties = !this.rules ? {} : this.rules.reduce(function (hash, r) {
2113 if (r instanceof Declaration && r.variable !== true) {
2114 var name_2 = (r.name.length === 1) && (r.name[0] instanceof Keyword) ?
2115 r.name[0].value : r.name;
2116 // Properties don't overwrite as they can merge
2117 if (!hash["$" + name_2]) {
2118 hash["$" + name_2] = [r];
2119 }
2120 else {
2121 hash["$" + name_2].push(r);
2122 }
2123 }
2124 return hash;
2125 }, {});
2126 }
2127 return this._properties;
2128 },
2129 variable: function (name) {
2130 var decl = this.variables()[name];
2131 if (decl) {
2132 return this.parseValue(decl);
2133 }
2134 },
2135 property: function (name) {
2136 var decl = this.properties()[name];
2137 if (decl) {
2138 return this.parseValue(decl);
2139 }
2140 },
2141 lastDeclaration: function () {
2142 for (var i = this.rules.length; i > 0; i--) {
2143 var decl = this.rules[i - 1];
2144 if (decl instanceof Declaration) {
2145 return this.parseValue(decl);
2146 }
2147 }
2148 },
2149 parseValue: function (toParse) {
2150 var self = this;
2151 function transformDeclaration(decl) {
2152 if (decl.value instanceof Anonymous && !decl.parsed) {
2153 if (typeof decl.value.value === 'string') {
2154 this.parse.parseNode(decl.value.value, ['value', 'important'], decl.value.getIndex(), decl.fileInfo(), function (err, result) {
2155 if (err) {
2156 decl.parsed = true;
2157 }
2158 if (result) {
2159 decl.value = result[0];
2160 decl.important = result[1] || '';
2161 decl.parsed = true;
2162 }
2163 });
2164 }
2165 else {
2166 decl.parsed = true;
2167 }
2168 return decl;
2169 }
2170 else {
2171 return decl;
2172 }
2173 }
2174 if (!Array.isArray(toParse)) {
2175 return transformDeclaration.call(self, toParse);
2176 }
2177 else {
2178 var nodes_1 = [];
2179 toParse.forEach(function (n) {
2180 nodes_1.push(transformDeclaration.call(self, n));
2181 });
2182 return nodes_1;
2183 }
2184 },
2185 rulesets: function () {
2186 if (!this.rules) {
2187 return [];
2188 }
2189 var filtRules = [];
2190 var rules = this.rules;
2191 var i;
2192 var rule;
2193 for (i = 0; (rule = rules[i]); i++) {
2194 if (rule.isRuleset) {
2195 filtRules.push(rule);
2196 }
2197 }
2198 return filtRules;
2199 },
2200 prependRule: function (rule) {
2201 var rules = this.rules;
2202 if (rules) {
2203 rules.unshift(rule);
2204 }
2205 else {
2206 this.rules = [rule];
2207 }
2208 this.setParent(rule, this);
2209 },
2210 find: function (selector, self, filter) {
2211 self = self || this;
2212 var rules = [];
2213 var match;
2214 var foundMixins;
2215 var key = selector.toCSS();
2216 if (key in this._lookups) {
2217 return this._lookups[key];
2218 }
2219 this.rulesets().forEach(function (rule) {
2220 if (rule !== self) {
2221 for (var j = 0; j < rule.selectors.length; j++) {
2222 match = selector.match(rule.selectors[j]);
2223 if (match) {
2224 if (selector.elements.length > match) {
2225 if (!filter || filter(rule)) {
2226 foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter);
2227 for (var i = 0; i < foundMixins.length; ++i) {
2228 foundMixins[i].path.push(rule);
2229 }
2230 Array.prototype.push.apply(rules, foundMixins);
2231 }
2232 }
2233 else {
2234 rules.push({ rule: rule, path: [] });
2235 }
2236 break;
2237 }
2238 }
2239 }
2240 });
2241 this._lookups[key] = rules;
2242 return rules;
2243 },
2244 genCSS: function (context, output) {
2245 var i;
2246 var j;
2247 var charsetRuleNodes = [];
2248 var ruleNodes = [];
2249 var // Line number debugging
2250 debugInfo$1;
2251 var rule;
2252 var path;
2253 context.tabLevel = (context.tabLevel || 0);
2254 if (!this.root) {
2255 context.tabLevel++;
2256 }
2257 var tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(' ');
2258 var tabSetStr = context.compress ? '' : Array(context.tabLevel).join(' ');
2259 var sep;
2260 var charsetNodeIndex = 0;
2261 var importNodeIndex = 0;
2262 for (i = 0; (rule = this.rules[i]); i++) {
2263 if (rule instanceof Comment) {
2264 if (importNodeIndex === i) {
2265 importNodeIndex++;
2266 }
2267 ruleNodes.push(rule);
2268 }
2269 else if (rule.isCharset && rule.isCharset()) {
2270 ruleNodes.splice(charsetNodeIndex, 0, rule);
2271 charsetNodeIndex++;
2272 importNodeIndex++;
2273 }
2274 else if (rule.type === 'Import') {
2275 ruleNodes.splice(importNodeIndex, 0, rule);
2276 importNodeIndex++;
2277 }
2278 else {
2279 ruleNodes.push(rule);
2280 }
2281 }
2282 ruleNodes = charsetRuleNodes.concat(ruleNodes);
2283 // If this is the root node, we don't render
2284 // a selector, or {}.
2285 if (!this.root) {
2286 debugInfo$1 = debugInfo(context, this, tabSetStr);
2287 if (debugInfo$1) {
2288 output.add(debugInfo$1);
2289 output.add(tabSetStr);
2290 }
2291 var paths = this.paths;
2292 var pathCnt = paths.length;
2293 var pathSubCnt = void 0;
2294 sep = context.compress ? ',' : (",\n" + tabSetStr);
2295 for (i = 0; i < pathCnt; i++) {
2296 path = paths[i];
2297 if (!(pathSubCnt = path.length)) {
2298 continue;
2299 }
2300 if (i > 0) {
2301 output.add(sep);
2302 }
2303 context.firstSelector = true;
2304 path[0].genCSS(context, output);
2305 context.firstSelector = false;
2306 for (j = 1; j < pathSubCnt; j++) {
2307 path[j].genCSS(context, output);
2308 }
2309 }
2310 output.add((context.compress ? '{' : ' {\n') + tabRuleStr);
2311 }
2312 // Compile rules and rulesets
2313 for (i = 0; (rule = ruleNodes[i]); i++) {
2314 if (i + 1 === ruleNodes.length) {
2315 context.lastRule = true;
2316 }
2317 var currentLastRule = context.lastRule;
2318 if (rule.isRulesetLike(rule)) {
2319 context.lastRule = false;
2320 }
2321 if (rule.genCSS) {
2322 rule.genCSS(context, output);
2323 }
2324 else if (rule.value) {
2325 output.add(rule.value.toString());
2326 }
2327 context.lastRule = currentLastRule;
2328 if (!context.lastRule && rule.isVisible()) {
2329 output.add(context.compress ? '' : ("\n" + tabRuleStr));
2330 }
2331 else {
2332 context.lastRule = false;
2333 }
2334 }
2335 if (!this.root) {
2336 output.add((context.compress ? '}' : "\n" + tabSetStr + "}"));
2337 context.tabLevel--;
2338 }
2339 if (!output.isEmpty() && !context.compress && this.firstRoot) {
2340 output.add('\n');
2341 }
2342 },
2343 joinSelectors: function (paths, context, selectors) {
2344 for (var s = 0; s < selectors.length; s++) {
2345 this.joinSelector(paths, context, selectors[s]);
2346 }
2347 },
2348 joinSelector: function (paths, context, selector) {
2349 function createParenthesis(elementsToPak, originalElement) {
2350 var replacementParen, j;
2351 if (elementsToPak.length === 0) {
2352 replacementParen = new Paren(elementsToPak[0]);
2353 }
2354 else {
2355 var insideParent = new Array(elementsToPak.length);
2356 for (j = 0; j < elementsToPak.length; j++) {
2357 insideParent[j] = new Element(null, elementsToPak[j], originalElement.isVariable, originalElement._index, originalElement._fileInfo);
2358 }
2359 replacementParen = new Paren(new Selector(insideParent));
2360 }
2361 return replacementParen;
2362 }
2363 function createSelector(containedElement, originalElement) {
2364 var element, selector;
2365 element = new Element(null, containedElement, originalElement.isVariable, originalElement._index, originalElement._fileInfo);
2366 selector = new Selector([element]);
2367 return selector;
2368 }
2369 // joins selector path from `beginningPath` with selector path in `addPath`
2370 // `replacedElement` contains element that is being replaced by `addPath`
2371 // returns concatenated path
2372 function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) {
2373 var newSelectorPath, lastSelector, newJoinedSelector;
2374 // our new selector path
2375 newSelectorPath = [];
2376 // construct the joined selector - if & is the first thing this will be empty,
2377 // if not newJoinedSelector will be the last set of elements in the selector
2378 if (beginningPath.length > 0) {
2379 newSelectorPath = copyArray(beginningPath);
2380 lastSelector = newSelectorPath.pop();
2381 newJoinedSelector = originalSelector.createDerived(copyArray(lastSelector.elements));
2382 }
2383 else {
2384 newJoinedSelector = originalSelector.createDerived([]);
2385 }
2386 if (addPath.length > 0) {
2387 // /deep/ is a CSS4 selector - (removed, so should deprecate)
2388 // that is valid without anything in front of it
2389 // so if the & does not have a combinator that is "" or " " then
2390 // and there is a combinator on the parent, then grab that.
2391 // this also allows + a { & .b { .a & { ... though not sure why you would want to do that
2392 var combinator = replacedElement.combinator;
2393 var parentEl = addPath[0].elements[0];
2394 if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) {
2395 combinator = parentEl.combinator;
2396 }
2397 // join the elements so far with the first part of the parent
2398 newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement.isVariable, replacedElement._index, replacedElement._fileInfo));
2399 newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
2400 }
2401 // now add the joined selector - but only if it is not empty
2402 if (newJoinedSelector.elements.length !== 0) {
2403 newSelectorPath.push(newJoinedSelector);
2404 }
2405 // put together the parent selectors after the join (e.g. the rest of the parent)
2406 if (addPath.length > 1) {
2407 var restOfPath = addPath.slice(1);
2408 restOfPath = restOfPath.map(function (selector) {
2409 return selector.createDerived(selector.elements, []);
2410 });
2411 newSelectorPath = newSelectorPath.concat(restOfPath);
2412 }
2413 return newSelectorPath;
2414 }
2415 // joins selector path from `beginningPath` with every selector path in `addPaths` array
2416 // `replacedElement` contains element that is being replaced by `addPath`
2417 // returns array with all concatenated paths
2418 function addAllReplacementsIntoPath(beginningPath, addPaths, replacedElement, originalSelector, result) {
2419 var j;
2420 for (j = 0; j < beginningPath.length; j++) {
2421 var newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector);
2422 result.push(newSelectorPath);
2423 }
2424 return result;
2425 }
2426 function mergeElementsOnToSelectors(elements, selectors) {
2427 var i, sel;
2428 if (elements.length === 0) {
2429 return;
2430 }
2431 if (selectors.length === 0) {
2432 selectors.push([new Selector(elements)]);
2433 return;
2434 }
2435 for (i = 0; (sel = selectors[i]); i++) {
2436 // if the previous thing in sel is a parent this needs to join on to it
2437 if (sel.length > 0) {
2438 sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
2439 }
2440 else {
2441 sel.push(new Selector(elements));
2442 }
2443 }
2444 }
2445 // replace all parent selectors inside `inSelector` by content of `context` array
2446 // resulting selectors are returned inside `paths` array
2447 // returns true if `inSelector` contained at least one parent selector
2448 function replaceParentSelector(paths, context, inSelector) {
2449 // The paths are [[Selector]]
2450 // The first list is a list of comma separated selectors
2451 // The inner list is a list of inheritance separated selectors
2452 // e.g.
2453 // .a, .b {
2454 // .c {
2455 // }
2456 // }
2457 // == [[.a] [.c]] [[.b] [.c]]
2458 //
2459 var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector;
2460 function findNestedSelector(element) {
2461 var maybeSelector;
2462 if (!(element.value instanceof Paren)) {
2463 return null;
2464 }
2465 maybeSelector = element.value.value;
2466 if (!(maybeSelector instanceof Selector)) {
2467 return null;
2468 }
2469 return maybeSelector;
2470 }
2471 // the elements from the current selector so far
2472 currentElements = [];
2473 // the current list of new selectors to add to the path.
2474 // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
2475 // by the parents
2476 newSelectors = [
2477 []
2478 ];
2479 for (i = 0; (el = inSelector.elements[i]); i++) {
2480 // non parent reference elements just get added
2481 if (el.value !== '&') {
2482 var nestedSelector = findNestedSelector(el);
2483 if (nestedSelector != null) {
2484 // merge the current list of non parent selector elements
2485 // on to the current list of selectors to add
2486 mergeElementsOnToSelectors(currentElements, newSelectors);
2487 var nestedPaths = [];
2488 var replaced = void 0;
2489 var replacedNewSelectors = [];
2490 replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
2491 hadParentSelector = hadParentSelector || replaced;
2492 // the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
2493 for (k = 0; k < nestedPaths.length; k++) {
2494 var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
2495 addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
2496 }
2497 newSelectors = replacedNewSelectors;
2498 currentElements = [];
2499 }
2500 else {
2501 currentElements.push(el);
2502 }
2503 }
2504 else {
2505 hadParentSelector = true;
2506 // the new list of selectors to add
2507 selectorsMultiplied = [];
2508 // merge the current list of non parent selector elements
2509 // on to the current list of selectors to add
2510 mergeElementsOnToSelectors(currentElements, newSelectors);
2511 // loop through our current selectors
2512 for (j = 0; j < newSelectors.length; j++) {
2513 sel = newSelectors[j];
2514 // if we don't have any parent paths, the & might be in a mixin so that it can be used
2515 // whether there are parents or not
2516 if (context.length === 0) {
2517 // the combinator used on el should now be applied to the next element instead so that
2518 // it is not lost
2519 if (sel.length > 0) {
2520 sel[0].elements.push(new Element(el.combinator, '', el.isVariable, el._index, el._fileInfo));
2521 }
2522 selectorsMultiplied.push(sel);
2523 }
2524 else {
2525 // and the parent selectors
2526 for (k = 0; k < context.length; k++) {
2527 // We need to put the current selectors
2528 // then join the last selector's elements on to the parents selectors
2529 var newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector);
2530 // add that to our new set of selectors
2531 selectorsMultiplied.push(newSelectorPath);
2532 }
2533 }
2534 }
2535 // our new selectors has been multiplied, so reset the state
2536 newSelectors = selectorsMultiplied;
2537 currentElements = [];
2538 }
2539 }
2540 // if we have any elements left over (e.g. .a& .b == .b)
2541 // add them on to all the current selectors
2542 mergeElementsOnToSelectors(currentElements, newSelectors);
2543 for (i = 0; i < newSelectors.length; i++) {
2544 length = newSelectors[i].length;
2545 if (length > 0) {
2546 paths.push(newSelectors[i]);
2547 lastSelector = newSelectors[i][length - 1];
2548 newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
2549 }
2550 }
2551 return hadParentSelector;
2552 }
2553 function deriveSelector(visibilityInfo, deriveFrom) {
2554 var newSelector = deriveFrom.createDerived(deriveFrom.elements, deriveFrom.extendList, deriveFrom.evaldCondition);
2555 newSelector.copyVisibilityInfo(visibilityInfo);
2556 return newSelector;
2557 }
2558 // joinSelector code follows
2559 var i, newPaths, hadParentSelector;
2560 newPaths = [];
2561 hadParentSelector = replaceParentSelector(newPaths, context, selector);
2562 if (!hadParentSelector) {
2563 if (context.length > 0) {
2564 newPaths = [];
2565 for (i = 0; i < context.length; i++) {
2566 var concatenated = context[i].map(deriveSelector.bind(this, selector.visibilityInfo()));
2567 concatenated.push(selector);
2568 newPaths.push(concatenated);
2569 }
2570 }
2571 else {
2572 newPaths = [[selector]];
2573 }
2574 }
2575 for (i = 0; i < newPaths.length; i++) {
2576 paths.push(newPaths[i]);
2577 }
2578 }
2579 });
2580
2581 var AtRule = function (name, value, rules, index, currentFileInfo, debugInfo, isRooted, visibilityInfo) {
2582 var i;
2583 this.name = name;
2584 this.value = (value instanceof Node) ? value : (value ? new Anonymous(value) : value);
2585 if (rules) {
2586 if (Array.isArray(rules)) {
2587 this.rules = rules;
2588 }
2589 else {
2590 this.rules = [rules];
2591 this.rules[0].selectors = (new Selector([], null, null, index, currentFileInfo)).createEmptySelectors();
2592 }
2593 for (i = 0; i < this.rules.length; i++) {
2594 this.rules[i].allowImports = true;
2595 }
2596 this.setParent(this.rules, this);
2597 }
2598 this._index = index;
2599 this._fileInfo = currentFileInfo;
2600 this.debugInfo = debugInfo;
2601 this.isRooted = isRooted || false;
2602 this.copyVisibilityInfo(visibilityInfo);
2603 this.allowRoot = true;
2604 };
2605 AtRule.prototype = Object.assign(new Node(), {
2606 type: 'AtRule',
2607 accept: function (visitor) {
2608 var value = this.value, rules = this.rules;
2609 if (rules) {
2610 this.rules = visitor.visitArray(rules);
2611 }
2612 if (value) {
2613 this.value = visitor.visit(value);
2614 }
2615 },
2616 isRulesetLike: function () {
2617 return this.rules || !this.isCharset();
2618 },
2619 isCharset: function () {
2620 return '@charset' === this.name;
2621 },
2622 genCSS: function (context, output) {
2623 var value = this.value, rules = this.rules;
2624 output.add(this.name, this.fileInfo(), this.getIndex());
2625 if (value) {
2626 output.add(' ');
2627 value.genCSS(context, output);
2628 }
2629 if (rules) {
2630 this.outputRuleset(context, output, rules);
2631 }
2632 else {
2633 output.add(';');
2634 }
2635 },
2636 eval: function (context) {
2637 var mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules;
2638 // media stored inside other atrule should not bubble over it
2639 // backpup media bubbling information
2640 mediaPathBackup = context.mediaPath;
2641 mediaBlocksBackup = context.mediaBlocks;
2642 // deleted media bubbling information
2643 context.mediaPath = [];
2644 context.mediaBlocks = [];
2645 if (value) {
2646 value = value.eval(context);
2647 }
2648 if (rules) {
2649 // assuming that there is only one rule at this point - that is how parser constructs the rule
2650 rules = [rules[0].eval(context)];
2651 rules[0].root = true;
2652 }
2653 // restore media bubbling information
2654 context.mediaPath = mediaPathBackup;
2655 context.mediaBlocks = mediaBlocksBackup;
2656 return new AtRule(this.name, value, rules, this.getIndex(), this.fileInfo(), this.debugInfo, this.isRooted, this.visibilityInfo());
2657 },
2658 variable: function (name) {
2659 if (this.rules) {
2660 // assuming that there is only one rule at this point - that is how parser constructs the rule
2661 return Ruleset.prototype.variable.call(this.rules[0], name);
2662 }
2663 },
2664 find: function () {
2665 if (this.rules) {
2666 // assuming that there is only one rule at this point - that is how parser constructs the rule
2667 return Ruleset.prototype.find.apply(this.rules[0], arguments);
2668 }
2669 },
2670 rulesets: function () {
2671 if (this.rules) {
2672 // assuming that there is only one rule at this point - that is how parser constructs the rule
2673 return Ruleset.prototype.rulesets.apply(this.rules[0]);
2674 }
2675 },
2676 outputRuleset: function (context, output, rules) {
2677 var ruleCnt = rules.length;
2678 var i;
2679 context.tabLevel = (context.tabLevel | 0) + 1;
2680 // Compressed
2681 if (context.compress) {
2682 output.add('{');
2683 for (i = 0; i < ruleCnt; i++) {
2684 rules[i].genCSS(context, output);
2685 }
2686 output.add('}');
2687 context.tabLevel--;
2688 return;
2689 }
2690 // Non-compressed
2691 var tabSetStr = "\n" + Array(context.tabLevel).join(' '), tabRuleStr = tabSetStr + " ";
2692 if (!ruleCnt) {
2693 output.add(" {" + tabSetStr + "}");
2694 }
2695 else {
2696 output.add(" {" + tabRuleStr);
2697 rules[0].genCSS(context, output);
2698 for (i = 1; i < ruleCnt; i++) {
2699 output.add(tabRuleStr);
2700 rules[i].genCSS(context, output);
2701 }
2702 output.add(tabSetStr + "}");
2703 }
2704 context.tabLevel--;
2705 }
2706 });
2707
2708 var DetachedRuleset = function (ruleset, frames) {
2709 this.ruleset = ruleset;
2710 this.frames = frames;
2711 this.setParent(this.ruleset, this);
2712 };
2713 DetachedRuleset.prototype = Object.assign(new Node(), {
2714 type: 'DetachedRuleset',
2715 evalFirst: true,
2716 accept: function (visitor) {
2717 this.ruleset = visitor.visit(this.ruleset);
2718 },
2719 eval: function (context) {
2720 var frames = this.frames || copyArray(context.frames);
2721 return new DetachedRuleset(this.ruleset, frames);
2722 },
2723 callEval: function (context) {
2724 return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context);
2725 }
2726 });
2727
2728 var Unit = function (numerator, denominator, backupUnit) {
2729 this.numerator = numerator ? copyArray(numerator).sort() : [];
2730 this.denominator = denominator ? copyArray(denominator).sort() : [];
2731 if (backupUnit) {
2732 this.backupUnit = backupUnit;
2733 }
2734 else if (numerator && numerator.length) {
2735 this.backupUnit = numerator[0];
2736 }
2737 };
2738 Unit.prototype = Object.assign(new Node(), {
2739 type: 'Unit',
2740 clone: function () {
2741 return new Unit(copyArray(this.numerator), copyArray(this.denominator), this.backupUnit);
2742 },
2743 genCSS: function (context, output) {
2744 // Dimension checks the unit is singular and throws an error if in strict math mode.
2745 var strictUnits = context && context.strictUnits;
2746 if (this.numerator.length === 1) {
2747 output.add(this.numerator[0]); // the ideal situation
2748 }
2749 else if (!strictUnits && this.backupUnit) {
2750 output.add(this.backupUnit);
2751 }
2752 else if (!strictUnits && this.denominator.length) {
2753 output.add(this.denominator[0]);
2754 }
2755 },
2756 toString: function () {
2757 var i, returnStr = this.numerator.join('*');
2758 for (i = 0; i < this.denominator.length; i++) {
2759 returnStr += "/" + this.denominator[i];
2760 }
2761 return returnStr;
2762 },
2763 compare: function (other) {
2764 return this.is(other.toString()) ? 0 : undefined;
2765 },
2766 is: function (unitString) {
2767 return this.toString().toUpperCase() === unitString.toUpperCase();
2768 },
2769 isLength: function () {
2770 return RegExp('^(px|em|ex|ch|rem|in|cm|mm|pc|pt|ex|vw|vh|vmin|vmax)$', 'gi').test(this.toCSS());
2771 },
2772 isEmpty: function () {
2773 return this.numerator.length === 0 && this.denominator.length === 0;
2774 },
2775 isSingular: function () {
2776 return this.numerator.length <= 1 && this.denominator.length === 0;
2777 },
2778 map: function (callback) {
2779 var i;
2780 for (i = 0; i < this.numerator.length; i++) {
2781 this.numerator[i] = callback(this.numerator[i], false);
2782 }
2783 for (i = 0; i < this.denominator.length; i++) {
2784 this.denominator[i] = callback(this.denominator[i], true);
2785 }
2786 },
2787 usedUnits: function () {
2788 var group;
2789 var result = {};
2790 var mapUnit;
2791 var groupName;
2792 mapUnit = function (atomicUnit) {
2793 /* jshint loopfunc:true */
2794 if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
2795 result[groupName] = atomicUnit;
2796 }
2797 return atomicUnit;
2798 };
2799 for (groupName in unitConversions) {
2800 if (unitConversions.hasOwnProperty(groupName)) {
2801 group = unitConversions[groupName];
2802 this.map(mapUnit);
2803 }
2804 }
2805 return result;
2806 },
2807 cancel: function () {
2808 var counter = {};
2809 var atomicUnit;
2810 var i;
2811 for (i = 0; i < this.numerator.length; i++) {
2812 atomicUnit = this.numerator[i];
2813 counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
2814 }
2815 for (i = 0; i < this.denominator.length; i++) {
2816 atomicUnit = this.denominator[i];
2817 counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
2818 }
2819 this.numerator = [];
2820 this.denominator = [];
2821 for (atomicUnit in counter) {
2822 if (counter.hasOwnProperty(atomicUnit)) {
2823 var count = counter[atomicUnit];
2824 if (count > 0) {
2825 for (i = 0; i < count; i++) {
2826 this.numerator.push(atomicUnit);
2827 }
2828 }
2829 else if (count < 0) {
2830 for (i = 0; i < -count; i++) {
2831 this.denominator.push(atomicUnit);
2832 }
2833 }
2834 }
2835 }
2836 this.numerator.sort();
2837 this.denominator.sort();
2838 }
2839 });
2840
2841 //
2842 // A number with a unit
2843 //
2844 var Dimension = function (value, unit) {
2845 this.value = parseFloat(value);
2846 if (isNaN(this.value)) {
2847 throw new Error('Dimension is not a number.');
2848 }
2849 this.unit = (unit && unit instanceof Unit) ? unit :
2850 new Unit(unit ? [unit] : undefined);
2851 this.setParent(this.unit, this);
2852 };
2853 Dimension.prototype = Object.assign(new Node(), {
2854 type: 'Dimension',
2855 accept: function (visitor) {
2856 this.unit = visitor.visit(this.unit);
2857 },
2858 eval: function (context) {
2859 return this;
2860 },
2861 toColor: function () {
2862 return new Color([this.value, this.value, this.value]);
2863 },
2864 genCSS: function (context, output) {
2865 if ((context && context.strictUnits) && !this.unit.isSingular()) {
2866 throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: " + this.unit.toString());
2867 }
2868 var value = this.fround(context, this.value);
2869 var strValue = String(value);
2870 if (value !== 0 && value < 0.000001 && value > -0.000001) {
2871 // would be output 1e-6 etc.
2872 strValue = value.toFixed(20).replace(/0+$/, '');
2873 }
2874 if (context && context.compress) {
2875 // Zero values doesn't need a unit
2876 if (value === 0 && this.unit.isLength()) {
2877 output.add(strValue);
2878 return;
2879 }
2880 // Float values doesn't need a leading zero
2881 if (value > 0 && value < 1) {
2882 strValue = (strValue).substr(1);
2883 }
2884 }
2885 output.add(strValue);
2886 this.unit.genCSS(context, output);
2887 },
2888 // In an operation between two Dimensions,
2889 // we default to the first Dimension's unit,
2890 // so `1px + 2` will yield `3px`.
2891 operate: function (context, op, other) {
2892 /* jshint noempty:false */
2893 var value = this._operate(context, op, this.value, other.value);
2894 var unit = this.unit.clone();
2895 if (op === '+' || op === '-') {
2896 if (unit.numerator.length === 0 && unit.denominator.length === 0) {
2897 unit = other.unit.clone();
2898 if (this.unit.backupUnit) {
2899 unit.backupUnit = this.unit.backupUnit;
2900 }
2901 }
2902 else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) ;
2903 else {
2904 other = other.convertTo(this.unit.usedUnits());
2905 if (context.strictUnits && other.unit.toString() !== unit.toString()) {
2906 throw new Error("Incompatible units. Change the units or use the unit function. "
2907 + ("Bad units: '" + unit.toString() + "' and '" + other.unit.toString() + "'."));
2908 }
2909 value = this._operate(context, op, this.value, other.value);
2910 }
2911 }
2912 else if (op === '*') {
2913 unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
2914 unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
2915 unit.cancel();
2916 }
2917 else if (op === '/') {
2918 unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
2919 unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
2920 unit.cancel();
2921 }
2922 return new Dimension(value, unit);
2923 },
2924 compare: function (other) {
2925 var a, b;
2926 if (!(other instanceof Dimension)) {
2927 return undefined;
2928 }
2929 if (this.unit.isEmpty() || other.unit.isEmpty()) {
2930 a = this;
2931 b = other;
2932 }
2933 else {
2934 a = this.unify();
2935 b = other.unify();
2936 if (a.unit.compare(b.unit) !== 0) {
2937 return undefined;
2938 }
2939 }
2940 return Node.numericCompare(a.value, b.value);
2941 },
2942 unify: function () {
2943 return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
2944 },
2945 convertTo: function (conversions) {
2946 var value = this.value;
2947 var unit = this.unit.clone();
2948 var i;
2949 var groupName;
2950 var group;
2951 var targetUnit;
2952 var derivedConversions = {};
2953 var applyUnit;
2954 if (typeof conversions === 'string') {
2955 for (i in unitConversions) {
2956 if (unitConversions[i].hasOwnProperty(conversions)) {
2957 derivedConversions = {};
2958 derivedConversions[i] = conversions;
2959 }
2960 }
2961 conversions = derivedConversions;
2962 }
2963 applyUnit = function (atomicUnit, denominator) {
2964 /* jshint loopfunc:true */
2965 if (group.hasOwnProperty(atomicUnit)) {
2966 if (denominator) {
2967 value = value / (group[atomicUnit] / group[targetUnit]);
2968 }
2969 else {
2970 value = value * (group[atomicUnit] / group[targetUnit]);
2971 }
2972 return targetUnit;
2973 }
2974 return atomicUnit;
2975 };
2976 for (groupName in conversions) {
2977 if (conversions.hasOwnProperty(groupName)) {
2978 targetUnit = conversions[groupName];
2979 group = unitConversions[groupName];
2980 unit.map(applyUnit);
2981 }
2982 }
2983 unit.cancel();
2984 return new Dimension(value, unit);
2985 }
2986 });
2987
2988 var MATH$1 = Math$1;
2989 var Operation = function (op, operands, isSpaced) {
2990 this.op = op.trim();
2991 this.operands = operands;
2992 this.isSpaced = isSpaced;
2993 };
2994 Operation.prototype = Object.assign(new Node(), {
2995 type: 'Operation',
2996 accept: function (visitor) {
2997 this.operands = visitor.visitArray(this.operands);
2998 },
2999 eval: function (context) {
3000 var a = this.operands[0].eval(context), b = this.operands[1].eval(context), op;
3001 if (context.isMathOn(this.op)) {
3002 op = this.op === './' ? '/' : this.op;
3003 if (a instanceof Dimension && b instanceof Color) {
3004 a = a.toColor();
3005 }
3006 if (b instanceof Dimension && a instanceof Color) {
3007 b = b.toColor();
3008 }
3009 if (!a.operate || !b.operate) {
3010 if ((a instanceof Operation || b instanceof Operation)
3011 && a.op === '/' && context.math === MATH$1.PARENS_DIVISION) {
3012 return new Operation(this.op, [a, b], this.isSpaced);
3013 }
3014 throw { type: 'Operation',
3015 message: 'Operation on an invalid type' };
3016 }
3017 return a.operate(context, op, b);
3018 }
3019 else {
3020 return new Operation(this.op, [a, b], this.isSpaced);
3021 }
3022 },
3023 genCSS: function (context, output) {
3024 this.operands[0].genCSS(context, output);
3025 if (this.isSpaced) {
3026 output.add(' ');
3027 }
3028 output.add(this.op);
3029 if (this.isSpaced) {
3030 output.add(' ');
3031 }
3032 this.operands[1].genCSS(context, output);
3033 }
3034 });
3035
3036 /*! *****************************************************************************
3037 Copyright (c) Microsoft Corporation.
3038
3039 Permission to use, copy, modify, and/or distribute this software for any
3040 purpose with or without fee is hereby granted.
3041
3042 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
3043 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
3044 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
3045 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
3046 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
3047 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
3048 PERFORMANCE OF THIS SOFTWARE.
3049 ***************************************************************************** */
3050
3051 function __spreadArrays$1() {
3052 for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
3053 for (var r = Array(s), k = 0, i = 0; i < il; i++)
3054 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
3055 r[k] = a[j];
3056 return r;
3057 }
3058
3059 var Expression = function (value, noSpacing) {
3060 this.value = value;
3061 this.noSpacing = noSpacing;
3062 if (!value) {
3063 throw new Error('Expression requires an array parameter');
3064 }
3065 };
3066 Expression.prototype = Object.assign(new Node(), {
3067 type: 'Expression',
3068 accept: function (visitor) {
3069 this.value = visitor.visitArray(this.value);
3070 },
3071 eval: function (context) {
3072 var returnValue;
3073 var mathOn = context.isMathOn();
3074 var inParenthesis = this.parens;
3075 var doubleParen = false;
3076 if (inParenthesis) {
3077 context.inParenthesis();
3078 }
3079 if (this.value.length > 1) {
3080 returnValue = new Expression(this.value.map(function (e) {
3081 if (!e.eval) {
3082 return e;
3083 }
3084 return e.eval(context);
3085 }), this.noSpacing);
3086 }
3087 else if (this.value.length === 1) {
3088 if (this.value[0].parens && !this.value[0].parensInOp && !context.inCalc) {
3089 doubleParen = true;
3090 }
3091 returnValue = this.value[0].eval(context);
3092 }
3093 else {
3094 returnValue = this;
3095 }
3096 if (inParenthesis) {
3097 context.outOfParenthesis();
3098 }
3099 if (this.parens && this.parensInOp && !mathOn && !doubleParen
3100 && (!(returnValue instanceof Dimension))) {
3101 returnValue = new Paren(returnValue);
3102 }
3103 return returnValue;
3104 },
3105 genCSS: function (context, output) {
3106 for (var i = 0; i < this.value.length; i++) {
3107 this.value[i].genCSS(context, output);
3108 if (!this.noSpacing && i + 1 < this.value.length) {
3109 output.add(' ');
3110 }
3111 }
3112 },
3113 throwAwayComments: function () {
3114 this.value = this.value.filter(function (v) {
3115 return !(v instanceof Comment);
3116 });
3117 }
3118 });
3119
3120 var functionCaller = /** @class */ (function () {
3121 function functionCaller(name, context, index, currentFileInfo) {
3122 this.name = name.toLowerCase();
3123 this.index = index;
3124 this.context = context;
3125 this.currentFileInfo = currentFileInfo;
3126 this.func = context.frames[0].functionRegistry.get(this.name);
3127 }
3128 functionCaller.prototype.isValid = function () {
3129 return Boolean(this.func);
3130 };
3131 functionCaller.prototype.call = function (args) {
3132 var _this = this;
3133 if (!(Array.isArray(args))) {
3134 args = [args];
3135 }
3136 var evalArgs = this.func.evalArgs;
3137 if (evalArgs !== false) {
3138 args = args.map(function (a) { return a.eval(_this.context); });
3139 }
3140 var commentFilter = function (item) { return !(item.type === 'Comment'); };
3141 // This code is terrible and should be replaced as per this issue...
3142 // https://github.com/less/less.js/issues/2477
3143 args = args
3144 .filter(commentFilter)
3145 .map(function (item) {
3146 if (item.type === 'Expression') {
3147 var subNodes = item.value.filter(commentFilter);
3148 if (subNodes.length === 1) {
3149 return subNodes[0];
3150 }
3151 else {
3152 return new Expression(subNodes);
3153 }
3154 }
3155 return item;
3156 });
3157 if (evalArgs === false) {
3158 return this.func.apply(this, __spreadArrays$1([this.context], args));
3159 }
3160 return this.func.apply(this, args);
3161 };
3162 return functionCaller;
3163 }());
3164
3165 //
3166 // A function call node.
3167 //
3168 var Call = function (name, args, index, currentFileInfo) {
3169 this.name = name;
3170 this.args = args;
3171 this.calc = name === 'calc';
3172 this._index = index;
3173 this._fileInfo = currentFileInfo;
3174 };
3175 Call.prototype = Object.assign(new Node(), {
3176 type: 'Call',
3177 accept: function (visitor) {
3178 if (this.args) {
3179 this.args = visitor.visitArray(this.args);
3180 }
3181 },
3182 //
3183 // When evaluating a function call,
3184 // we either find the function in the functionRegistry,
3185 // in which case we call it, passing the evaluated arguments,
3186 // if this returns null or we cannot find the function, we
3187 // simply print it out as it appeared originally [2].
3188 //
3189 // The reason why we evaluate the arguments, is in the case where
3190 // we try to pass a variable to a function, like: `saturate(@color)`.
3191 // The function should receive the value, not the variable.
3192 //
3193 eval: function (context) {
3194 var _this = this;
3195 /**
3196 * Turn off math for calc(), and switch back on for evaluating nested functions
3197 */
3198 var currentMathContext = context.mathOn;
3199 context.mathOn = !this.calc;
3200 if (this.calc || context.inCalc) {
3201 context.enterCalc();
3202 }
3203 var exitCalc = function () {
3204 if (_this.calc || context.inCalc) {
3205 context.exitCalc();
3206 }
3207 context.mathOn = currentMathContext;
3208 };
3209 var result;
3210 var funcCaller = new functionCaller(this.name, context, this.getIndex(), this.fileInfo());
3211 if (funcCaller.isValid()) {
3212 try {
3213 result = funcCaller.call(this.args);
3214 exitCalc();
3215 }
3216 catch (e) {
3217 if (e.hasOwnProperty('line') && e.hasOwnProperty('column')) {
3218 throw e;
3219 }
3220 throw {
3221 type: e.type || 'Runtime',
3222 message: "Error evaluating function `" + this.name + "`" + (e.message ? ": " + e.message : ''),
3223 index: this.getIndex(),
3224 filename: this.fileInfo().filename,
3225 line: e.lineNumber,
3226 column: e.columnNumber
3227 };
3228 }
3229 }
3230 if (result !== null && result !== undefined) {
3231 // Results that that are not nodes are cast as Anonymous nodes
3232 // Falsy values or booleans are returned as empty nodes
3233 if (!(result instanceof Node)) {
3234 if (!result || result === true) {
3235 result = new Anonymous(null);
3236 }
3237 else {
3238 result = new Anonymous(result.toString());
3239 }
3240 }
3241 result._index = this._index;
3242 result._fileInfo = this._fileInfo;
3243 return result;
3244 }
3245 var args = this.args.map(function (a) { return a.eval(context); });
3246 exitCalc();
3247 return new Call(this.name, args, this.getIndex(), this.fileInfo());
3248 },
3249 genCSS: function (context, output) {
3250 output.add(this.name + "(", this.fileInfo(), this.getIndex());
3251 for (var i = 0; i < this.args.length; i++) {
3252 this.args[i].genCSS(context, output);
3253 if (i + 1 < this.args.length) {
3254 output.add(', ');
3255 }
3256 }
3257 output.add(')');
3258 }
3259 });
3260
3261 var Variable = function (name, index, currentFileInfo) {
3262 this.name = name;
3263 this._index = index;
3264 this._fileInfo = currentFileInfo;
3265 };
3266 Variable.prototype = Object.assign(new Node(), {
3267 type: 'Variable',
3268 eval: function (context) {
3269 var variable, name = this.name;
3270 if (name.indexOf('@@') === 0) {
3271 name = "@" + new Variable(name.slice(1), this.getIndex(), this.fileInfo()).eval(context).value;
3272 }
3273 if (this.evaluating) {
3274 throw { type: 'Name', message: "Recursive variable definition for " + name, filename: this.fileInfo().filename,
3275 index: this.getIndex() };
3276 }
3277 this.evaluating = true;
3278 variable = this.find(context.frames, function (frame) {
3279 var v = frame.variable(name);
3280 if (v) {
3281 if (v.important) {
3282 var importantScope = context.importantScope[context.importantScope.length - 1];
3283 importantScope.important = v.important;
3284 }
3285 // If in calc, wrap vars in a function call to cascade evaluate args first
3286 if (context.inCalc) {
3287 return (new Call('_SELF', [v.value])).eval(context);
3288 }
3289 else {
3290 return v.value.eval(context);
3291 }
3292 }
3293 });
3294 if (variable) {
3295 this.evaluating = false;
3296 return variable;
3297 }
3298 else {
3299 throw { type: 'Name', message: "variable " + name + " is undefined", filename: this.fileInfo().filename,
3300 index: this.getIndex() };
3301 }
3302 },
3303 find: function (obj, fun) {
3304 for (var i = 0, r = void 0; i < obj.length; i++) {
3305 r = fun.call(obj, obj[i]);
3306 if (r) {
3307 return r;
3308 }
3309 }
3310 return null;
3311 }
3312 });
3313
3314 var Property = function (name, index, currentFileInfo) {
3315 this.name = name;
3316 this._index = index;
3317 this._fileInfo = currentFileInfo;
3318 };
3319 Property.prototype = Object.assign(new Node(), {
3320 type: 'Property',
3321 eval: function (context) {
3322 var property;
3323 var name = this.name;
3324 // TODO: shorten this reference
3325 var mergeRules = context.pluginManager.less.visitors.ToCSSVisitor.prototype._mergeRules;
3326 if (this.evaluating) {
3327 throw { type: 'Name', message: "Recursive property reference for " + name, filename: this.fileInfo().filename,
3328 index: this.getIndex() };
3329 }
3330 this.evaluating = true;
3331 property = this.find(context.frames, function (frame) {
3332 var v;
3333 var vArr = frame.property(name);
3334 if (vArr) {
3335 for (var i = 0; i < vArr.length; i++) {
3336 v = vArr[i];
3337 vArr[i] = new Declaration(v.name, v.value, v.important, v.merge, v.index, v.currentFileInfo, v.inline, v.variable);
3338 }
3339 mergeRules(vArr);
3340 v = vArr[vArr.length - 1];
3341 if (v.important) {
3342 var importantScope = context.importantScope[context.importantScope.length - 1];
3343 importantScope.important = v.important;
3344 }
3345 v = v.value.eval(context);
3346 return v;
3347 }
3348 });
3349 if (property) {
3350 this.evaluating = false;
3351 return property;
3352 }
3353 else {
3354 throw { type: 'Name', message: "Property '" + name + "' is undefined", filename: this.currentFileInfo.filename,
3355 index: this.index };
3356 }
3357 },
3358 find: function (obj, fun) {
3359 for (var i = 0, r = void 0; i < obj.length; i++) {
3360 r = fun.call(obj, obj[i]);
3361 if (r) {
3362 return r;
3363 }
3364 }
3365 return null;
3366 }
3367 });
3368
3369 var Attribute = function (key, op, value) {
3370 this.key = key;
3371 this.op = op;
3372 this.value = value;
3373 };
3374 Attribute.prototype = Object.assign(new Node(), {
3375 type: 'Attribute',
3376 eval: function (context) {
3377 return new Attribute(this.key.eval ? this.key.eval(context) : this.key, this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value);
3378 },
3379 genCSS: function (context, output) {
3380 output.add(this.toCSS(context));
3381 },
3382 toCSS: function (context) {
3383 var value = this.key.toCSS ? this.key.toCSS(context) : this.key;
3384 if (this.op) {
3385 value += this.op;
3386 value += (this.value.toCSS ? this.value.toCSS(context) : this.value);
3387 }
3388 return "[" + value + "]";
3389 }
3390 });
3391
3392 var Quoted = function (str, content, escaped, index, currentFileInfo) {
3393 this.escaped = (escaped == null) ? true : escaped;
3394 this.value = content || '';
3395 this.quote = str.charAt(0);
3396 this._index = index;
3397 this._fileInfo = currentFileInfo;
3398 this.variableRegex = /@\{([\w-]+)\}/g;
3399 this.propRegex = /\$\{([\w-]+)\}/g;
3400 this.allowRoot = escaped;
3401 };
3402 Quoted.prototype = Object.assign(new Node(), {
3403 type: 'Quoted',
3404 genCSS: function (context, output) {
3405 if (!this.escaped) {
3406 output.add(this.quote, this.fileInfo(), this.getIndex());
3407 }
3408 output.add(this.value);
3409 if (!this.escaped) {
3410 output.add(this.quote);
3411 }
3412 },
3413 containsVariables: function () {
3414 return this.value.match(this.variableRegex);
3415 },
3416 eval: function (context) {
3417 var that = this;
3418 var value = this.value;
3419 var variableReplacement = function (_, name) {
3420 var v = new Variable("@" + name, that.getIndex(), that.fileInfo()).eval(context, true);
3421 return (v instanceof Quoted) ? v.value : v.toCSS();
3422 };
3423 var propertyReplacement = function (_, name) {
3424 var v = new Property("$" + name, that.getIndex(), that.fileInfo()).eval(context, true);
3425 return (v instanceof Quoted) ? v.value : v.toCSS();
3426 };
3427 function iterativeReplace(value, regexp, replacementFnc) {
3428 var evaluatedValue = value;
3429 do {
3430 value = evaluatedValue.toString();
3431 evaluatedValue = value.replace(regexp, replacementFnc);
3432 } while (value !== evaluatedValue);
3433 return evaluatedValue;
3434 }
3435 value = iterativeReplace(value, this.variableRegex, variableReplacement);
3436 value = iterativeReplace(value, this.propRegex, propertyReplacement);
3437 return new Quoted(this.quote + value + this.quote, value, this.escaped, this.getIndex(), this.fileInfo());
3438 },
3439 compare: function (other) {
3440 // when comparing quoted strings allow the quote to differ
3441 if (other.type === 'Quoted' && !this.escaped && !other.escaped) {
3442 return Node.numericCompare(this.value, other.value);
3443 }
3444 else {
3445 return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
3446 }
3447 }
3448 });
3449
3450 function escapePath(path) {
3451 return path.replace(/[\(\)'"\s]/g, function (match) { return "\\" + match; });
3452 }
3453 var URL = function (val, index, currentFileInfo, isEvald) {
3454 this.value = val;
3455 this._index = index;
3456 this._fileInfo = currentFileInfo;
3457 this.isEvald = isEvald;
3458 };
3459 URL.prototype = Object.assign(new Node(), {
3460 type: 'Url',
3461 accept: function (visitor) {
3462 this.value = visitor.visit(this.value);
3463 },
3464 genCSS: function (context, output) {
3465 output.add('url(');
3466 this.value.genCSS(context, output);
3467 output.add(')');
3468 },
3469 eval: function (context) {
3470 var val = this.value.eval(context);
3471 var rootpath;
3472 if (!this.isEvald) {
3473 // Add the rootpath if the URL requires a rewrite
3474 rootpath = this.fileInfo() && this.fileInfo().rootpath;
3475 if (typeof rootpath === 'string' &&
3476 typeof val.value === 'string' &&
3477 context.pathRequiresRewrite(val.value)) {
3478 if (!val.quote) {
3479 rootpath = escapePath(rootpath);
3480 }
3481 val.value = context.rewritePath(val.value, rootpath);
3482 }
3483 else {
3484 val.value = context.normalizePath(val.value);
3485 }
3486 // Add url args if enabled
3487 if (context.urlArgs) {
3488 if (!val.value.match(/^\s*data:/)) {
3489 var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
3490 var urlArgs = delimiter + context.urlArgs;
3491 if (val.value.indexOf('#') !== -1) {
3492 val.value = val.value.replace('#', urlArgs + "#");
3493 }
3494 else {
3495 val.value += urlArgs;
3496 }
3497 }
3498 }
3499 }
3500 return new URL(val, this.getIndex(), this.fileInfo(), true);
3501 }
3502 });
3503
3504 var Media = function (value, features, index, currentFileInfo, visibilityInfo) {
3505 this._index = index;
3506 this._fileInfo = currentFileInfo;
3507 var selectors = (new Selector([], null, null, this._index, this._fileInfo)).createEmptySelectors();
3508 this.features = new Value(features);
3509 this.rules = [new Ruleset(selectors, value)];
3510 this.rules[0].allowImports = true;
3511 this.copyVisibilityInfo(visibilityInfo);
3512 this.allowRoot = true;
3513 this.setParent(selectors, this);
3514 this.setParent(this.features, this);
3515 this.setParent(this.rules, this);
3516 };
3517 Media.prototype = Object.assign(new AtRule(), {
3518 type: 'Media',
3519 isRulesetLike: function () {
3520 return true;
3521 },
3522 accept: function (visitor) {
3523 if (this.features) {
3524 this.features = visitor.visit(this.features);
3525 }
3526 if (this.rules) {
3527 this.rules = visitor.visitArray(this.rules);
3528 }
3529 },
3530 genCSS: function (context, output) {
3531 output.add('@media ', this._fileInfo, this._index);
3532 this.features.genCSS(context, output);
3533 this.outputRuleset(context, output, this.rules);
3534 },
3535 eval: function (context) {
3536 if (!context.mediaBlocks) {
3537 context.mediaBlocks = [];
3538 context.mediaPath = [];
3539 }
3540 var media = new Media(null, [], this._index, this._fileInfo, this.visibilityInfo());
3541 if (this.debugInfo) {
3542 this.rules[0].debugInfo = this.debugInfo;
3543 media.debugInfo = this.debugInfo;
3544 }
3545 media.features = this.features.eval(context);
3546 context.mediaPath.push(media);
3547 context.mediaBlocks.push(media);
3548 this.rules[0].functionRegistry = context.frames[0].functionRegistry.inherit();
3549 context.frames.unshift(this.rules[0]);
3550 media.rules = [this.rules[0].eval(context)];
3551 context.frames.shift();
3552 context.mediaPath.pop();
3553 return context.mediaPath.length === 0 ? media.evalTop(context) :
3554 media.evalNested(context);
3555 },
3556 evalTop: function (context) {
3557 var result = this;
3558 // Render all dependent Media blocks.
3559 if (context.mediaBlocks.length > 1) {
3560 var selectors = (new Selector([], null, null, this.getIndex(), this.fileInfo())).createEmptySelectors();
3561 result = new Ruleset(selectors, context.mediaBlocks);
3562 result.multiMedia = true;
3563 result.copyVisibilityInfo(this.visibilityInfo());
3564 this.setParent(result, this);
3565 }
3566 delete context.mediaBlocks;
3567 delete context.mediaPath;
3568 return result;
3569 },
3570 evalNested: function (context) {
3571 var i;
3572 var value;
3573 var path = context.mediaPath.concat([this]);
3574 // Extract the media-query conditions separated with `,` (OR).
3575 for (i = 0; i < path.length; i++) {
3576 value = path[i].features instanceof Value ?
3577 path[i].features.value : path[i].features;
3578 path[i] = Array.isArray(value) ? value : [value];
3579 }
3580 // Trace all permutations to generate the resulting media-query.
3581 //
3582 // (a, b and c) with nested (d, e) ->
3583 // a and d
3584 // a and e
3585 // b and c and d
3586 // b and c and e
3587 this.features = new Value(this.permute(path).map(function (path) {
3588 path = path.map(function (fragment) { return fragment.toCSS ? fragment : new Anonymous(fragment); });
3589 for (i = path.length - 1; i > 0; i--) {
3590 path.splice(i, 0, new Anonymous('and'));
3591 }
3592 return new Expression(path);
3593 }));
3594 this.setParent(this.features, this);
3595 // Fake a tree-node that doesn't output anything.
3596 return new Ruleset([], []);
3597 },
3598 permute: function (arr) {
3599 if (arr.length === 0) {
3600 return [];
3601 }
3602 else if (arr.length === 1) {
3603 return arr[0];
3604 }
3605 else {
3606 var result = [];
3607 var rest = this.permute(arr.slice(1));
3608 for (var i = 0; i < rest.length; i++) {
3609 for (var j = 0; j < arr[0].length; j++) {
3610 result.push([arr[0][j]].concat(rest[i]));
3611 }
3612 }
3613 return result;
3614 }
3615 },
3616 bubbleSelectors: function (selectors) {
3617 if (!selectors) {
3618 return;
3619 }
3620 this.rules = [new Ruleset(copyArray(selectors), [this.rules[0]])];
3621 this.setParent(this.rules, this);
3622 }
3623 });
3624
3625 //
3626 // CSS @import node
3627 //
3628 // The general strategy here is that we don't want to wait
3629 // for the parsing to be completed, before we start importing
3630 // the file. That's because in the context of a browser,
3631 // most of the time will be spent waiting for the server to respond.
3632 //
3633 // On creation, we push the import path to our import queue, though
3634 // `import,push`, we also pass it a callback, which it'll call once
3635 // the file has been fetched, and parsed.
3636 //
3637 var Import = function (path, features, options, index, currentFileInfo, visibilityInfo) {
3638 this.options = options;
3639 this._index = index;
3640 this._fileInfo = currentFileInfo;
3641 this.path = path;
3642 this.features = features;
3643 this.allowRoot = true;
3644 if (this.options.less !== undefined || this.options.inline) {
3645 this.css = !this.options.less || this.options.inline;
3646 }
3647 else {
3648 var pathValue = this.getPath();
3649 if (pathValue && /[#\.\&\?]css([\?;].*)?$/.test(pathValue)) {
3650 this.css = true;
3651 }
3652 }
3653 this.copyVisibilityInfo(visibilityInfo);
3654 this.setParent(this.features, this);
3655 this.setParent(this.path, this);
3656 };
3657 Import.prototype = Object.assign(new Node(), {
3658 type: 'Import',
3659 accept: function (visitor) {
3660 if (this.features) {
3661 this.features = visitor.visit(this.features);
3662 }
3663 this.path = visitor.visit(this.path);
3664 if (!this.options.isPlugin && !this.options.inline && this.root) {
3665 this.root = visitor.visit(this.root);
3666 }
3667 },
3668 genCSS: function (context, output) {
3669 if (this.css && this.path._fileInfo.reference === undefined) {
3670 output.add('@import ', this._fileInfo, this._index);
3671 this.path.genCSS(context, output);
3672 if (this.features) {
3673 output.add(' ');
3674 this.features.genCSS(context, output);
3675 }
3676 output.add(';');
3677 }
3678 },
3679 getPath: function () {
3680 return (this.path instanceof URL) ?
3681 this.path.value.value : this.path.value;
3682 },
3683 isVariableImport: function () {
3684 var path = this.path;
3685 if (path instanceof URL) {
3686 path = path.value;
3687 }
3688 if (path instanceof Quoted) {
3689 return path.containsVariables();
3690 }
3691 return true;
3692 },
3693 evalForImport: function (context) {
3694 var path = this.path;
3695 if (path instanceof URL) {
3696 path = path.value;
3697 }
3698 return new Import(path.eval(context), this.features, this.options, this._index, this._fileInfo, this.visibilityInfo());
3699 },
3700 evalPath: function (context) {
3701 var path = this.path.eval(context);
3702 var fileInfo = this._fileInfo;
3703 if (!(path instanceof URL)) {
3704 // Add the rootpath if the URL requires a rewrite
3705 var pathValue = path.value;
3706 if (fileInfo &&
3707 pathValue &&
3708 context.pathRequiresRewrite(pathValue)) {
3709 path.value = context.rewritePath(pathValue, fileInfo.rootpath);
3710 }
3711 else {
3712 path.value = context.normalizePath(path.value);
3713 }
3714 }
3715 return path;
3716 },
3717 eval: function (context) {
3718 var result = this.doEval(context);
3719 if (this.options.reference || this.blocksVisibility()) {
3720 if (result.length || result.length === 0) {
3721 result.forEach(function (node) {
3722 node.addVisibilityBlock();
3723 });
3724 }
3725 else {
3726 result.addVisibilityBlock();
3727 }
3728 }
3729 return result;
3730 },
3731 doEval: function (context) {
3732 var ruleset;
3733 var registry;
3734 var features = this.features && this.features.eval(context);
3735 if (this.options.isPlugin) {
3736 if (this.root && this.root.eval) {
3737 try {
3738 this.root.eval(context);
3739 }
3740 catch (e) {
3741 e.message = 'Plugin error during evaluation';
3742 throw new LessError(e, this.root.imports, this.root.filename);
3743 }
3744 }
3745 registry = context.frames[0] && context.frames[0].functionRegistry;
3746 if (registry && this.root && this.root.functions) {
3747 registry.addMultiple(this.root.functions);
3748 }
3749 return [];
3750 }
3751 if (this.skip) {
3752 if (typeof this.skip === 'function') {
3753 this.skip = this.skip();
3754 }
3755 if (this.skip) {
3756 return [];
3757 }
3758 }
3759 if (this.options.inline) {
3760 var contents = new Anonymous(this.root, 0, {
3761 filename: this.importedFilename,
3762 reference: this.path._fileInfo && this.path._fileInfo.reference
3763 }, true, true);
3764 return this.features ? new Media([contents], this.features.value) : [contents];
3765 }
3766 else if (this.css) {
3767 var newImport = new Import(this.evalPath(context), features, this.options, this._index);
3768 if (!newImport.css && this.error) {
3769 throw this.error;
3770 }
3771 return newImport;
3772 }
3773 else if (this.root) {
3774 ruleset = new Ruleset(null, copyArray(this.root.rules));
3775 ruleset.evalImports(context);
3776 return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules;
3777 }
3778 else {
3779 return [];
3780 }
3781 }
3782 });
3783
3784 var JsEvalNode = function () { };
3785 JsEvalNode.prototype = Object.assign(new Node(), {
3786 evaluateJavaScript: function (expression, context) {
3787 var result;
3788 var that = this;
3789 var evalContext = {};
3790 if (!context.javascriptEnabled) {
3791 throw { message: 'Inline JavaScript is not enabled. Is it set in your options?',
3792 filename: this.fileInfo().filename,
3793 index: this.getIndex() };
3794 }
3795 expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
3796 return that.jsify(new Variable("@" + name, that.getIndex(), that.fileInfo()).eval(context));
3797 });
3798 try {
3799 expression = new Function("return (" + expression + ")");
3800 }
3801 catch (e) {
3802 throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`", filename: this.fileInfo().filename,
3803 index: this.getIndex() };
3804 }
3805 var variables = context.frames[0].variables();
3806 for (var k in variables) {
3807 if (variables.hasOwnProperty(k)) {
3808 /* jshint loopfunc:true */
3809 evalContext[k.slice(1)] = {
3810 value: variables[k].value,
3811 toJS: function () {
3812 return this.value.eval(context).toCSS();
3813 }
3814 };
3815 }
3816 }
3817 try {
3818 result = expression.call(evalContext);
3819 }
3820 catch (e) {
3821 throw { message: "JavaScript evaluation error: '" + e.name + ": " + e.message.replace(/["]/g, '\'') + "'", filename: this.fileInfo().filename,
3822 index: this.getIndex() };
3823 }
3824 return result;
3825 },
3826 jsify: function (obj) {
3827 if (Array.isArray(obj.value) && (obj.value.length > 1)) {
3828 return "[" + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + "]";
3829 }
3830 else {
3831 return obj.toCSS();
3832 }
3833 }
3834 });
3835
3836 var JavaScript = function (string, escaped, index, currentFileInfo) {
3837 this.escaped = escaped;
3838 this.expression = string;
3839 this._index = index;
3840 this._fileInfo = currentFileInfo;
3841 };
3842 JavaScript.prototype = Object.assign(new JsEvalNode(), {
3843 type: 'JavaScript',
3844 eval: function (context) {
3845 var result = this.evaluateJavaScript(this.expression, context);
3846 var type = typeof result;
3847 if (type === 'number' && !isNaN(result)) {
3848 return new Dimension(result);
3849 }
3850 else if (type === 'string') {
3851 return new Quoted("\"" + result + "\"", result, this.escaped, this._index);
3852 }
3853 else if (Array.isArray(result)) {
3854 return new Anonymous(result.join(', '));
3855 }
3856 else {
3857 return new Anonymous(result);
3858 }
3859 }
3860 });
3861
3862 var Assignment = function (key, val) {
3863 this.key = key;
3864 this.value = val;
3865 };
3866 Assignment.prototype = Object.assign(new Node(), {
3867 type: 'Assignment',
3868 accept: function (visitor) {
3869 this.value = visitor.visit(this.value);
3870 },
3871 eval: function (context) {
3872 if (this.value.eval) {
3873 return new Assignment(this.key, this.value.eval(context));
3874 }
3875 return this;
3876 },
3877 genCSS: function (context, output) {
3878 output.add(this.key + "=");
3879 if (this.value.genCSS) {
3880 this.value.genCSS(context, output);
3881 }
3882 else {
3883 output.add(this.value);
3884 }
3885 }
3886 });
3887
3888 var Condition = function (op, l, r, i, negate) {
3889 this.op = op.trim();
3890 this.lvalue = l;
3891 this.rvalue = r;
3892 this._index = i;
3893 this.negate = negate;
3894 };
3895 Condition.prototype = Object.assign(new Node(), {
3896 type: 'Condition',
3897 accept: function (visitor) {
3898 this.lvalue = visitor.visit(this.lvalue);
3899 this.rvalue = visitor.visit(this.rvalue);
3900 },
3901 eval: function (context) {
3902 var result = (function (op, a, b) {
3903 switch (op) {
3904 case 'and': return a && b;
3905 case 'or': return a || b;
3906 default:
3907 switch (Node.compare(a, b)) {
3908 case -1:
3909 return op === '<' || op === '=<' || op === '<=';
3910 case 0:
3911 return op === '=' || op === '>=' || op === '=<' || op === '<=';
3912 case 1:
3913 return op === '>' || op === '>=';
3914 default:
3915 return false;
3916 }
3917 }
3918 })(this.op, this.lvalue.eval(context), this.rvalue.eval(context));
3919 return this.negate ? !result : result;
3920 }
3921 });
3922
3923 var UnicodeDescriptor = function (value) {
3924 this.value = value;
3925 };
3926 UnicodeDescriptor.prototype = Object.assign(new Node(), {
3927 type: 'UnicodeDescriptor'
3928 });
3929
3930 var Negative = function (node) {
3931 this.value = node;
3932 };
3933 Negative.prototype = Object.assign(new Node(), {
3934 type: 'Negative',
3935 genCSS: function (context, output) {
3936 output.add('-');
3937 this.value.genCSS(context, output);
3938 },
3939 eval: function (context) {
3940 if (context.isMathOn()) {
3941 return (new Operation('*', [new Dimension(-1), this.value])).eval(context);
3942 }
3943 return new Negative(this.value.eval(context));
3944 }
3945 });
3946
3947 var Extend = function (selector, option, index, currentFileInfo, visibilityInfo) {
3948 this.selector = selector;
3949 this.option = option;
3950 this.object_id = Extend.next_id++;
3951 this.parent_ids = [this.object_id];
3952 this._index = index;
3953 this._fileInfo = currentFileInfo;
3954 this.copyVisibilityInfo(visibilityInfo);
3955 this.allowRoot = true;
3956 switch (option) {
3957 case 'all':
3958 this.allowBefore = true;
3959 this.allowAfter = true;
3960 break;
3961 default:
3962 this.allowBefore = false;
3963 this.allowAfter = false;
3964 break;
3965 }
3966 this.setParent(this.selector, this);
3967 };
3968 Extend.prototype = Object.assign(new Node(), {
3969 type: 'Extend',
3970 accept: function (visitor) {
3971 this.selector = visitor.visit(this.selector);
3972 },
3973 eval: function (context) {
3974 return new Extend(this.selector.eval(context), this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
3975 },
3976 clone: function (context) {
3977 return new Extend(this.selector, this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
3978 },
3979 // it concatenates (joins) all selectors in selector array
3980 findSelfSelectors: function (selectors) {
3981 var selfElements = [], i, selectorElements;
3982 for (i = 0; i < selectors.length; i++) {
3983 selectorElements = selectors[i].elements;
3984 // duplicate the logic in genCSS function inside the selector node.
3985 // future TODO - move both logics into the selector joiner visitor
3986 if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === '') {
3987 selectorElements[0].combinator.value = ' ';
3988 }
3989 selfElements = selfElements.concat(selectors[i].elements);
3990 }
3991 this.selfSelectors = [new Selector(selfElements)];
3992 this.selfSelectors[0].copyVisibilityInfo(this.visibilityInfo());
3993 }
3994 });
3995 Extend.next_id = 0;
3996
3997 var VariableCall = function (variable, index, currentFileInfo) {
3998 this.variable = variable;
3999 this._index = index;
4000 this._fileInfo = currentFileInfo;
4001 this.allowRoot = true;
4002 };
4003 VariableCall.prototype = Object.assign(new Node(), {
4004 type: 'VariableCall',
4005 eval: function (context) {
4006 var rules;
4007 var detachedRuleset = new Variable(this.variable, this.getIndex(), this.fileInfo()).eval(context);
4008 var error = new LessError({ message: "Could not evaluate variable call " + this.variable });
4009 if (!detachedRuleset.ruleset) {
4010 if (detachedRuleset.rules) {
4011 rules = detachedRuleset;
4012 }
4013 else if (Array.isArray(detachedRuleset)) {
4014 rules = new Ruleset('', detachedRuleset);
4015 }
4016 else if (Array.isArray(detachedRuleset.value)) {
4017 rules = new Ruleset('', detachedRuleset.value);
4018 }
4019 else {
4020 throw error;
4021 }
4022 detachedRuleset = new DetachedRuleset(rules);
4023 }
4024 if (detachedRuleset.ruleset) {
4025 return detachedRuleset.callEval(context);
4026 }
4027 throw error;
4028 }
4029 });
4030
4031 var NamespaceValue = function (ruleCall, lookups, index, fileInfo) {
4032 this.value = ruleCall;
4033 this.lookups = lookups;
4034 this._index = index;
4035 this._fileInfo = fileInfo;
4036 };
4037 NamespaceValue.prototype = Object.assign(new Node(), {
4038 type: 'NamespaceValue',
4039 eval: function (context) {
4040 var i, name, rules = this.value.eval(context);
4041 for (i = 0; i < this.lookups.length; i++) {
4042 name = this.lookups[i];
4043 /**
4044 * Eval'd DRs return rulesets.
4045 * Eval'd mixins return rules, so let's make a ruleset if we need it.
4046 * We need to do this because of late parsing of values
4047 */
4048 if (Array.isArray(rules)) {
4049 rules = new Ruleset([new Selector()], rules);
4050 }
4051 if (name === '') {
4052 rules = rules.lastDeclaration();
4053 }
4054 else if (name.charAt(0) === '@') {
4055 if (name.charAt(1) === '@') {
4056 name = "@" + new Variable(name.substr(1)).eval(context).value;
4057 }
4058 if (rules.variables) {
4059 rules = rules.variable(name);
4060 }
4061 if (!rules) {
4062 throw { type: 'Name', message: "variable " + name + " not found", filename: this.fileInfo().filename,
4063 index: this.getIndex() };
4064 }
4065 }
4066 else {
4067 if (name.substring(0, 2) === '$@') {
4068 name = "$" + new Variable(name.substr(1)).eval(context).value;
4069 }
4070 else {
4071 name = name.charAt(0) === '$' ? name : "$" + name;
4072 }
4073 if (rules.properties) {
4074 rules = rules.property(name);
4075 }
4076 if (!rules) {
4077 throw { type: 'Name', message: "property \"" + name.substr(1) + "\" not found", filename: this.fileInfo().filename,
4078 index: this.getIndex() };
4079 }
4080 // Properties are an array of values, since a ruleset can have multiple props.
4081 // We pick the last one (the "cascaded" value)
4082 rules = rules[rules.length - 1];
4083 }
4084 if (rules.value) {
4085 rules = rules.eval(context).value;
4086 }
4087 if (rules.ruleset) {
4088 rules = rules.ruleset.eval(context);
4089 }
4090 }
4091 return rules;
4092 }
4093 });
4094
4095 var Definition = function (name, params, rules, condition, variadic, frames, visibilityInfo) {
4096 this.name = name || 'anonymous mixin';
4097 this.selectors = [new Selector([new Element(null, name, false, this._index, this._fileInfo)])];
4098 this.params = params;
4099 this.condition = condition;
4100 this.variadic = variadic;
4101 this.arity = params.length;
4102 this.rules = rules;
4103 this._lookups = {};
4104 var optionalParameters = [];
4105 this.required = params.reduce(function (count, p) {
4106 if (!p.name || (p.name && !p.value)) {
4107 return count + 1;
4108 }
4109 else {
4110 optionalParameters.push(p.name);
4111 return count;
4112 }
4113 }, 0);
4114 this.optionalParameters = optionalParameters;
4115 this.frames = frames;
4116 this.copyVisibilityInfo(visibilityInfo);
4117 this.allowRoot = true;
4118 };
4119 Definition.prototype = Object.assign(new Ruleset(), {
4120 type: 'MixinDefinition',
4121 evalFirst: true,
4122 accept: function (visitor) {
4123 if (this.params && this.params.length) {
4124 this.params = visitor.visitArray(this.params);
4125 }
4126 this.rules = visitor.visitArray(this.rules);
4127 if (this.condition) {
4128 this.condition = visitor.visit(this.condition);
4129 }
4130 },
4131 evalParams: function (context, mixinEnv, args, evaldArguments) {
4132 /* jshint boss:true */
4133 var frame = new Ruleset(null, null);
4134 var varargs;
4135 var arg;
4136 var params = copyArray(this.params);
4137 var i;
4138 var j;
4139 var val;
4140 var name;
4141 var isNamedFound;
4142 var argIndex;
4143 var argsLength = 0;
4144 if (mixinEnv.frames && mixinEnv.frames[0] && mixinEnv.frames[0].functionRegistry) {
4145 frame.functionRegistry = mixinEnv.frames[0].functionRegistry.inherit();
4146 }
4147 mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
4148 if (args) {
4149 args = copyArray(args);
4150 argsLength = args.length;
4151 for (i = 0; i < argsLength; i++) {
4152 arg = args[i];
4153 if (name = (arg && arg.name)) {
4154 isNamedFound = false;
4155 for (j = 0; j < params.length; j++) {
4156 if (!evaldArguments[j] && name === params[j].name) {
4157 evaldArguments[j] = arg.value.eval(context);
4158 frame.prependRule(new Declaration(name, arg.value.eval(context)));
4159 isNamedFound = true;
4160 break;
4161 }
4162 }
4163 if (isNamedFound) {
4164 args.splice(i, 1);
4165 i--;
4166 continue;
4167 }
4168 else {
4169 throw { type: 'Runtime', message: "Named argument for " + this.name + " " + args[i].name + " not found" };
4170 }
4171 }
4172 }
4173 }
4174 argIndex = 0;
4175 for (i = 0; i < params.length; i++) {
4176 if (evaldArguments[i]) {
4177 continue;
4178 }
4179 arg = args && args[argIndex];
4180 if (name = params[i].name) {
4181 if (params[i].variadic) {
4182 varargs = [];
4183 for (j = argIndex; j < argsLength; j++) {
4184 varargs.push(args[j].value.eval(context));
4185 }
4186 frame.prependRule(new Declaration(name, new Expression(varargs).eval(context)));
4187 }
4188 else {
4189 val = arg && arg.value;
4190 if (val) {
4191 // This was a mixin call, pass in a detached ruleset of it's eval'd rules
4192 if (Array.isArray(val)) {
4193 val = new DetachedRuleset(new Ruleset('', val));
4194 }
4195 else {
4196 val = val.eval(context);
4197 }
4198 }
4199 else if (params[i].value) {
4200 val = params[i].value.eval(mixinEnv);
4201 frame.resetCache();
4202 }
4203 else {
4204 throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + " (" + argsLength + " for " + this.arity + ")" };
4205 }
4206 frame.prependRule(new Declaration(name, val));
4207 evaldArguments[i] = val;
4208 }
4209 }
4210 if (params[i].variadic && args) {
4211 for (j = argIndex; j < argsLength; j++) {
4212 evaldArguments[j] = args[j].value.eval(context);
4213 }
4214 }
4215 argIndex++;
4216 }
4217 return frame;
4218 },
4219 makeImportant: function () {
4220 var rules = !this.rules ? this.rules : this.rules.map(function (r) {
4221 if (r.makeImportant) {
4222 return r.makeImportant(true);
4223 }
4224 else {
4225 return r;
4226 }
4227 });
4228 var result = new Definition(this.name, this.params, rules, this.condition, this.variadic, this.frames);
4229 return result;
4230 },
4231 eval: function (context) {
4232 return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || copyArray(context.frames));
4233 },
4234 evalCall: function (context, args, important) {
4235 var _arguments = [];
4236 var mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames;
4237 var frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments);
4238 var rules;
4239 var ruleset;
4240 frame.prependRule(new Declaration('@arguments', new Expression(_arguments).eval(context)));
4241 rules = copyArray(this.rules);
4242 ruleset = new Ruleset(null, rules);
4243 ruleset.originalRuleset = this;
4244 ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames)));
4245 if (important) {
4246 ruleset = ruleset.makeImportant();
4247 }
4248 return ruleset;
4249 },
4250 matchCondition: function (args, context) {
4251 if (this.condition && !this.condition.eval(new contexts.Eval(context, [this.evalParams(context, /* the parameter variables */ new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
4252 .concat(this.frames || []) // the parent namespace/mixin frames
4253 .concat(context.frames)))) { // the current environment frames
4254 return false;
4255 }
4256 return true;
4257 },
4258 matchArgs: function (args, context) {
4259 var allArgsCnt = (args && args.length) || 0;
4260 var len;
4261 var optionalParameters = this.optionalParameters;
4262 var requiredArgsCnt = !args ? 0 : args.reduce(function (count, p) {
4263 if (optionalParameters.indexOf(p.name) < 0) {
4264 return count + 1;
4265 }
4266 else {
4267 return count;
4268 }
4269 }, 0);
4270 if (!this.variadic) {
4271 if (requiredArgsCnt < this.required) {
4272 return false;
4273 }
4274 if (allArgsCnt > this.params.length) {
4275 return false;
4276 }
4277 }
4278 else {
4279 if (requiredArgsCnt < (this.required - 1)) {
4280 return false;
4281 }
4282 }
4283 // check patterns
4284 len = Math.min(requiredArgsCnt, this.arity);
4285 for (var i = 0; i < len; i++) {
4286 if (!this.params[i].name && !this.params[i].variadic) {
4287 if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) {
4288 return false;
4289 }
4290 }
4291 }
4292 return true;
4293 }
4294 });
4295
4296 var MixinCall = function (elements, args, index, currentFileInfo, important) {
4297 this.selector = new Selector(elements);
4298 this.arguments = args || [];
4299 this._index = index;
4300 this._fileInfo = currentFileInfo;
4301 this.important = important;
4302 this.allowRoot = true;
4303 this.setParent(this.selector, this);
4304 };
4305 MixinCall.prototype = Object.assign(new Node(), {
4306 type: 'MixinCall',
4307 accept: function (visitor) {
4308 if (this.selector) {
4309 this.selector = visitor.visit(this.selector);
4310 }
4311 if (this.arguments.length) {
4312 this.arguments = visitor.visitArray(this.arguments);
4313 }
4314 },
4315 eval: function (context) {
4316 var mixins;
4317 var mixin;
4318 var mixinPath;
4319 var args = [];
4320 var arg;
4321 var argValue;
4322 var rules = [];
4323 var match = false;
4324 var i;
4325 var m;
4326 var f;
4327 var isRecursive;
4328 var isOneFound;
4329 var candidates = [];
4330 var candidate;
4331 var conditionResult = [];
4332 var defaultResult;
4333 var defFalseEitherCase = -1;
4334 var defNone = 0;
4335 var defTrue = 1;
4336 var defFalse = 2;
4337 var count;
4338 var originalRuleset;
4339 var noArgumentsFilter;
4340 this.selector = this.selector.eval(context);
4341 function calcDefGroup(mixin, mixinPath) {
4342 var f, p, namespace;
4343 for (f = 0; f < 2; f++) {
4344 conditionResult[f] = true;
4345 defaultFunc.value(f);
4346 for (p = 0; p < mixinPath.length && conditionResult[f]; p++) {
4347 namespace = mixinPath[p];
4348 if (namespace.matchCondition) {
4349 conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context);
4350 }
4351 }
4352 if (mixin.matchCondition) {
4353 conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context);
4354 }
4355 }
4356 if (conditionResult[0] || conditionResult[1]) {
4357 if (conditionResult[0] != conditionResult[1]) {
4358 return conditionResult[1] ?
4359 defTrue : defFalse;
4360 }
4361 return defNone;
4362 }
4363 return defFalseEitherCase;
4364 }
4365 for (i = 0; i < this.arguments.length; i++) {
4366 arg = this.arguments[i];
4367 argValue = arg.value.eval(context);
4368 if (arg.expand && Array.isArray(argValue.value)) {
4369 argValue = argValue.value;
4370 for (m = 0; m < argValue.length; m++) {
4371 args.push({ value: argValue[m] });
4372 }
4373 }
4374 else {
4375 args.push({ name: arg.name, value: argValue });
4376 }
4377 }
4378 noArgumentsFilter = function (rule) { return rule.matchArgs(null, context); };
4379 for (i = 0; i < context.frames.length; i++) {
4380 if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) {
4381 isOneFound = true;
4382 // To make `default()` function independent of definition order we have two "subpasses" here.
4383 // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
4384 // and build candidate list with corresponding flags. Then, when we know all possible matches,
4385 // we make a final decision.
4386 for (m = 0; m < mixins.length; m++) {
4387 mixin = mixins[m].rule;
4388 mixinPath = mixins[m].path;
4389 isRecursive = false;
4390 for (f = 0; f < context.frames.length; f++) {
4391 if ((!(mixin instanceof Definition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) {
4392 isRecursive = true;
4393 break;
4394 }
4395 }
4396 if (isRecursive) {
4397 continue;
4398 }
4399 if (mixin.matchArgs(args, context)) {
4400 candidate = { mixin: mixin, group: calcDefGroup(mixin, mixinPath) };
4401 if (candidate.group !== defFalseEitherCase) {
4402 candidates.push(candidate);
4403 }
4404 match = true;
4405 }
4406 }
4407 defaultFunc.reset();
4408 count = [0, 0, 0];
4409 for (m = 0; m < candidates.length; m++) {
4410 count[candidates[m].group]++;
4411 }
4412 if (count[defNone] > 0) {
4413 defaultResult = defFalse;
4414 }
4415 else {
4416 defaultResult = defTrue;
4417 if ((count[defTrue] + count[defFalse]) > 1) {
4418 throw { type: 'Runtime', message: "Ambiguous use of `default()` found when matching for `" + this.format(args) + "`", index: this.getIndex(), filename: this.fileInfo().filename };
4419 }
4420 }
4421 for (m = 0; m < candidates.length; m++) {
4422 candidate = candidates[m].group;
4423 if ((candidate === defNone) || (candidate === defaultResult)) {
4424 try {
4425 mixin = candidates[m].mixin;
4426 if (!(mixin instanceof Definition)) {
4427 originalRuleset = mixin.originalRuleset || mixin;
4428 mixin = new Definition('', [], mixin.rules, null, false, null, originalRuleset.visibilityInfo());
4429 mixin.originalRuleset = originalRuleset;
4430 }
4431 var newRules = mixin.evalCall(context, args, this.important).rules;
4432 this._setVisibilityToReplacement(newRules);
4433 Array.prototype.push.apply(rules, newRules);
4434 }
4435 catch (e) {
4436 throw { message: e.message, index: this.getIndex(), filename: this.fileInfo().filename, stack: e.stack };
4437 }
4438 }
4439 }
4440 if (match) {
4441 return rules;
4442 }
4443 }
4444 }
4445 if (isOneFound) {
4446 throw { type: 'Runtime', message: "No matching definition was found for `" + this.format(args) + "`", index: this.getIndex(), filename: this.fileInfo().filename };
4447 }
4448 else {
4449 throw { type: 'Name', message: this.selector.toCSS().trim() + " is undefined", index: this.getIndex(), filename: this.fileInfo().filename };
4450 }
4451 },
4452 _setVisibilityToReplacement: function (replacement) {
4453 var i, rule;
4454 if (this.blocksVisibility()) {
4455 for (i = 0; i < replacement.length; i++) {
4456 rule = replacement[i];
4457 rule.addVisibilityBlock();
4458 }
4459 }
4460 },
4461 format: function (args) {
4462 return this.selector.toCSS().trim() + "(" + (args ? args.map(function (a) {
4463 var argValue = '';
4464 if (a.name) {
4465 argValue += a.name + ":";
4466 }
4467 if (a.value.toCSS) {
4468 argValue += a.value.toCSS();
4469 }
4470 else {
4471 argValue += '???';
4472 }
4473 return argValue;
4474 }).join(', ') : '') + ")";
4475 }
4476 });
4477
4478 var tree = {
4479 Node: Node, Color: Color, AtRule: AtRule, DetachedRuleset: DetachedRuleset, Operation: Operation,
4480 Dimension: Dimension, Unit: Unit, Keyword: Keyword, Variable: Variable, Property: Property,
4481 Ruleset: Ruleset, Element: Element, Attribute: Attribute, Combinator: Combinator, Selector: Selector,
4482 Quoted: Quoted, Expression: Expression, Declaration: Declaration, Call: Call, URL: URL, Import: Import,
4483 Comment: Comment, Anonymous: Anonymous, Value: Value, JavaScript: JavaScript, Assignment: Assignment,
4484 Condition: Condition, Paren: Paren, Media: Media, UnicodeDescriptor: UnicodeDescriptor, Negative: Negative,
4485 Extend: Extend, VariableCall: VariableCall, NamespaceValue: NamespaceValue,
4486 mixin: {
4487 Call: MixinCall,
4488 Definition: Definition
4489 }
4490 };
4491
4492 var AbstractFileManager = /** @class */ (function () {
4493 function AbstractFileManager() {
4494 }
4495 AbstractFileManager.prototype.getPath = function (filename) {
4496 var j = filename.lastIndexOf('?');
4497 if (j > 0) {
4498 filename = filename.slice(0, j);
4499 }
4500 j = filename.lastIndexOf('/');
4501 if (j < 0) {
4502 j = filename.lastIndexOf('\\');
4503 }
4504 if (j < 0) {
4505 return '';
4506 }
4507 return filename.slice(0, j + 1);
4508 };
4509 AbstractFileManager.prototype.tryAppendExtension = function (path, ext) {
4510 return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + ext;
4511 };
4512 AbstractFileManager.prototype.tryAppendLessExtension = function (path) {
4513 return this.tryAppendExtension(path, '.less');
4514 };
4515 AbstractFileManager.prototype.supportsSync = function () {
4516 return false;
4517 };
4518 AbstractFileManager.prototype.alwaysMakePathsAbsolute = function () {
4519 return false;
4520 };
4521 AbstractFileManager.prototype.isPathAbsolute = function (filename) {
4522 return (/^(?:[a-z-]+:|\/|\\|#)/i).test(filename);
4523 };
4524 // TODO: pull out / replace?
4525 AbstractFileManager.prototype.join = function (basePath, laterPath) {
4526 if (!basePath) {
4527 return laterPath;
4528 }
4529 return basePath + laterPath;
4530 };
4531 AbstractFileManager.prototype.pathDiff = function (url, baseUrl) {
4532 // diff between two paths to create a relative path
4533 var urlParts = this.extractUrlParts(url);
4534 var baseUrlParts = this.extractUrlParts(baseUrl);
4535 var i;
4536 var max;
4537 var urlDirectories;
4538 var baseUrlDirectories;
4539 var diff = '';
4540 if (urlParts.hostPart !== baseUrlParts.hostPart) {
4541 return '';
4542 }
4543 max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
4544 for (i = 0; i < max; i++) {
4545 if (baseUrlParts.directories[i] !== urlParts.directories[i]) {
4546 break;
4547 }
4548 }
4549 baseUrlDirectories = baseUrlParts.directories.slice(i);
4550 urlDirectories = urlParts.directories.slice(i);
4551 for (i = 0; i < baseUrlDirectories.length - 1; i++) {
4552 diff += '../';
4553 }
4554 for (i = 0; i < urlDirectories.length - 1; i++) {
4555 diff += urlDirectories[i] + "/";
4556 }
4557 return diff;
4558 };
4559 // helper function, not part of API
4560 AbstractFileManager.prototype.extractUrlParts = function (url, baseUrl) {
4561 // urlParts[1] = protocol://hostname/ OR /
4562 // urlParts[2] = / if path relative to host base
4563 // urlParts[3] = directories
4564 // urlParts[4] = filename
4565 // urlParts[5] = parameters
4566 var urlPartsRegex = /^((?:[a-z-]+:)?\/{2}(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i;
4567 var urlParts = url.match(urlPartsRegex);
4568 var returner = {};
4569 var rawDirectories = [];
4570 var directories = [];
4571 var i;
4572 var baseUrlParts;
4573 if (!urlParts) {
4574 throw new Error("Could not parse sheet href - '" + url + "'");
4575 }
4576 // Stylesheets in IE don't always return the full path
4577 if (baseUrl && (!urlParts[1] || urlParts[2])) {
4578 baseUrlParts = baseUrl.match(urlPartsRegex);
4579 if (!baseUrlParts) {
4580 throw new Error("Could not parse page url - '" + baseUrl + "'");
4581 }
4582 urlParts[1] = urlParts[1] || baseUrlParts[1] || '';
4583 if (!urlParts[2]) {
4584 urlParts[3] = baseUrlParts[3] + urlParts[3];
4585 }
4586 }
4587 if (urlParts[3]) {
4588 rawDirectories = urlParts[3].replace(/\\/g, '/').split('/');
4589 // collapse '..' and skip '.'
4590 for (i = 0; i < rawDirectories.length; i++) {
4591 if (rawDirectories[i] === '..') {
4592 directories.pop();
4593 }
4594 else if (rawDirectories[i] !== '.') {
4595 directories.push(rawDirectories[i]);
4596 }
4597 }
4598 }
4599 returner.hostPart = urlParts[1];
4600 returner.directories = directories;
4601 returner.rawPath = (urlParts[1] || '') + rawDirectories.join('/');
4602 returner.path = (urlParts[1] || '') + directories.join('/');
4603 returner.filename = urlParts[4];
4604 returner.fileUrl = returner.path + (urlParts[4] || '');
4605 returner.url = returner.fileUrl + (urlParts[5] || '');
4606 return returner;
4607 };
4608 return AbstractFileManager;
4609 }());
4610
4611 var AbstractPluginLoader = /** @class */ (function () {
4612 function AbstractPluginLoader() {
4613 // Implemented by Node.js plugin loader
4614 this.require = function () {
4615 return null;
4616 };
4617 }
4618 AbstractPluginLoader.prototype.evalPlugin = function (contents, context, imports, pluginOptions, fileInfo) {
4619 var loader, registry, pluginObj, localModule, pluginManager, filename, result;
4620 pluginManager = context.pluginManager;
4621 if (fileInfo) {
4622 if (typeof fileInfo === 'string') {
4623 filename = fileInfo;
4624 }
4625 else {
4626 filename = fileInfo.filename;
4627 }
4628 }
4629 var shortname = (new this.less.FileManager()).extractUrlParts(filename).filename;
4630 if (filename) {
4631 pluginObj = pluginManager.get(filename);
4632 if (pluginObj) {
4633 result = this.trySetOptions(pluginObj, filename, shortname, pluginOptions);
4634 if (result) {
4635 return result;
4636 }
4637 try {
4638 if (pluginObj.use) {
4639 pluginObj.use.call(this.context, pluginObj);
4640 }
4641 }
4642 catch (e) {
4643 e.message = e.message || 'Error during @plugin call';
4644 return new LessError(e, imports, filename);
4645 }
4646 return pluginObj;
4647 }
4648 }
4649 localModule = {
4650 exports: {},
4651 pluginManager: pluginManager,
4652 fileInfo: fileInfo
4653 };
4654 registry = functionRegistry.create();
4655 var registerPlugin = function (obj) {
4656 pluginObj = obj;
4657 };
4658 try {
4659 loader = new Function('module', 'require', 'registerPlugin', 'functions', 'tree', 'less', 'fileInfo', contents);
4660 loader(localModule, this.require(filename), registerPlugin, registry, this.less.tree, this.less, fileInfo);
4661 }
4662 catch (e) {
4663 return new LessError(e, imports, filename);
4664 }
4665 if (!pluginObj) {
4666 pluginObj = localModule.exports;
4667 }
4668 pluginObj = this.validatePlugin(pluginObj, filename, shortname);
4669 if (pluginObj instanceof LessError) {
4670 return pluginObj;
4671 }
4672 if (pluginObj) {
4673 pluginObj.imports = imports;
4674 pluginObj.filename = filename;
4675 // For < 3.x (or unspecified minVersion) - setOptions() before install()
4676 if (!pluginObj.minVersion || this.compareVersion('3.0.0', pluginObj.minVersion) < 0) {
4677 result = this.trySetOptions(pluginObj, filename, shortname, pluginOptions);
4678 if (result) {
4679 return result;
4680 }
4681 }
4682 // Run on first load
4683 pluginManager.addPlugin(pluginObj, fileInfo.filename, registry);
4684 pluginObj.functions = registry.getLocalFunctions();
4685 // Need to call setOptions again because the pluginObj might have functions
4686 result = this.trySetOptions(pluginObj, filename, shortname, pluginOptions);
4687 if (result) {
4688 return result;
4689 }
4690 // Run every @plugin call
4691 try {
4692 if (pluginObj.use) {
4693 pluginObj.use.call(this.context, pluginObj);
4694 }
4695 }
4696 catch (e) {
4697 e.message = e.message || 'Error during @plugin call';
4698 return new LessError(e, imports, filename);
4699 }
4700 }
4701 else {
4702 return new LessError({ message: 'Not a valid plugin' }, imports, filename);
4703 }
4704 return pluginObj;
4705 };
4706 AbstractPluginLoader.prototype.trySetOptions = function (plugin, filename, name, options) {
4707 if (options && !plugin.setOptions) {
4708 return new LessError({
4709 message: "Options have been provided but the plugin " + name + " does not support any options."
4710 });
4711 }
4712 try {
4713 plugin.setOptions && plugin.setOptions(options);
4714 }
4715 catch (e) {
4716 return new LessError(e);
4717 }
4718 };
4719 AbstractPluginLoader.prototype.validatePlugin = function (plugin, filename, name) {
4720 if (plugin) {
4721 // support plugins being a function
4722 // so that the plugin can be more usable programmatically
4723 if (typeof plugin === 'function') {
4724 plugin = new plugin();
4725 }
4726 if (plugin.minVersion) {
4727 if (this.compareVersion(plugin.minVersion, this.less.version) < 0) {
4728 return new LessError({
4729 message: "Plugin " + name + " requires version " + this.versionToString(plugin.minVersion)
4730 });
4731 }
4732 }
4733 return plugin;
4734 }
4735 return null;
4736 };
4737 AbstractPluginLoader.prototype.compareVersion = function (aVersion, bVersion) {
4738 if (typeof aVersion === 'string') {
4739 aVersion = aVersion.match(/^(\d+)\.?(\d+)?\.?(\d+)?/);
4740 aVersion.shift();
4741 }
4742 for (var i = 0; i < aVersion.length; i++) {
4743 if (aVersion[i] !== bVersion[i]) {
4744 return parseInt(aVersion[i]) > parseInt(bVersion[i]) ? -1 : 1;
4745 }
4746 }
4747 return 0;
4748 };
4749 AbstractPluginLoader.prototype.versionToString = function (version) {
4750 var versionString = '';
4751 for (var i = 0; i < version.length; i++) {
4752 versionString += (versionString ? '.' : '') + version[i];
4753 }
4754 return versionString;
4755 };
4756 AbstractPluginLoader.prototype.printUsage = function (plugins) {
4757 for (var i = 0; i < plugins.length; i++) {
4758 var plugin = plugins[i];
4759 if (plugin.printUsage) {
4760 plugin.printUsage();
4761 }
4762 }
4763 };
4764 return AbstractPluginLoader;
4765 }());
4766
4767 var _visitArgs = { visitDeeper: true };
4768 var _hasIndexed = false;
4769 function _noop(node) {
4770 return node;
4771 }
4772 function indexNodeTypes(parent, ticker) {
4773 // add .typeIndex to tree node types for lookup table
4774 var key, child;
4775 for (key in parent) {
4776 /* eslint guard-for-in: 0 */
4777 child = parent[key];
4778 switch (typeof child) {
4779 case 'function':
4780 // ignore bound functions directly on tree which do not have a prototype
4781 // or aren't nodes
4782 if (child.prototype && child.prototype.type) {
4783 child.prototype.typeIndex = ticker++;
4784 }
4785 break;
4786 case 'object':
4787 ticker = indexNodeTypes(child, ticker);
4788 break;
4789 }
4790 }
4791 return ticker;
4792 }
4793 var Visitor = /** @class */ (function () {
4794 function Visitor(implementation) {
4795 this._implementation = implementation;
4796 this._visitInCache = {};
4797 this._visitOutCache = {};
4798 if (!_hasIndexed) {
4799 indexNodeTypes(tree, 1);
4800 _hasIndexed = true;
4801 }
4802 }
4803 Visitor.prototype.visit = function (node) {
4804 if (!node) {
4805 return node;
4806 }
4807 var nodeTypeIndex = node.typeIndex;
4808 if (!nodeTypeIndex) {
4809 // MixinCall args aren't a node type?
4810 if (node.value && node.value.typeIndex) {
4811 this.visit(node.value);
4812 }
4813 return node;
4814 }
4815 var impl = this._implementation;
4816 var func = this._visitInCache[nodeTypeIndex];
4817 var funcOut = this._visitOutCache[nodeTypeIndex];
4818 var visitArgs = _visitArgs;
4819 var fnName;
4820 visitArgs.visitDeeper = true;
4821 if (!func) {
4822 fnName = "visit" + node.type;
4823 func = impl[fnName] || _noop;
4824 funcOut = impl[fnName + "Out"] || _noop;
4825 this._visitInCache[nodeTypeIndex] = func;
4826 this._visitOutCache[nodeTypeIndex] = funcOut;
4827 }
4828 if (func !== _noop) {
4829 var newNode = func.call(impl, node, visitArgs);
4830 if (node && impl.isReplacing) {
4831 node = newNode;
4832 }
4833 }
4834 if (visitArgs.visitDeeper && node) {
4835 if (node.length) {
4836 for (var i = 0, cnt = node.length; i < cnt; i++) {
4837 if (node[i].accept) {
4838 node[i].accept(this);
4839 }
4840 }
4841 }
4842 else if (node.accept) {
4843 node.accept(this);
4844 }
4845 }
4846 if (funcOut != _noop) {
4847 funcOut.call(impl, node);
4848 }
4849 return node;
4850 };
4851 Visitor.prototype.visitArray = function (nodes, nonReplacing) {
4852 if (!nodes) {
4853 return nodes;
4854 }
4855 var cnt = nodes.length;
4856 var i;
4857 // Non-replacing
4858 if (nonReplacing || !this._implementation.isReplacing) {
4859 for (i = 0; i < cnt; i++) {
4860 this.visit(nodes[i]);
4861 }
4862 return nodes;
4863 }
4864 // Replacing
4865 var out = [];
4866 for (i = 0; i < cnt; i++) {
4867 var evald = this.visit(nodes[i]);
4868 if (evald === undefined) {
4869 continue;
4870 }
4871 if (!evald.splice) {
4872 out.push(evald);
4873 }
4874 else if (evald.length) {
4875 this.flatten(evald, out);
4876 }
4877 }
4878 return out;
4879 };
4880 Visitor.prototype.flatten = function (arr, out) {
4881 if (!out) {
4882 out = [];
4883 }
4884 var cnt, i, item, nestedCnt, j, nestedItem;
4885 for (i = 0, cnt = arr.length; i < cnt; i++) {
4886 item = arr[i];
4887 if (item === undefined) {
4888 continue;
4889 }
4890 if (!item.splice) {
4891 out.push(item);
4892 continue;
4893 }
4894 for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) {
4895 nestedItem = item[j];
4896 if (nestedItem === undefined) {
4897 continue;
4898 }
4899 if (!nestedItem.splice) {
4900 out.push(nestedItem);
4901 }
4902 else if (nestedItem.length) {
4903 this.flatten(nestedItem, out);
4904 }
4905 }
4906 }
4907 return out;
4908 };
4909 return Visitor;
4910 }());
4911
4912 var ImportSequencer = /** @class */ (function () {
4913 function ImportSequencer(onSequencerEmpty) {
4914 this.imports = [];
4915 this.variableImports = [];
4916 this._onSequencerEmpty = onSequencerEmpty;
4917 this._currentDepth = 0;
4918 }
4919 ImportSequencer.prototype.addImport = function (callback) {
4920 var importSequencer = this, importItem = {
4921 callback: callback,
4922 args: null,
4923 isReady: false
4924 };
4925 this.imports.push(importItem);
4926 return function () {
4927 importItem.args = Array.prototype.slice.call(arguments, 0);
4928 importItem.isReady = true;
4929 importSequencer.tryRun();
4930 };
4931 };
4932 ImportSequencer.prototype.addVariableImport = function (callback) {
4933 this.variableImports.push(callback);
4934 };
4935 ImportSequencer.prototype.tryRun = function () {
4936 this._currentDepth++;
4937 try {
4938 while (true) {
4939 while (this.imports.length > 0) {
4940 var importItem = this.imports[0];
4941 if (!importItem.isReady) {
4942 return;
4943 }
4944 this.imports = this.imports.slice(1);
4945 importItem.callback.apply(null, importItem.args);
4946 }
4947 if (this.variableImports.length === 0) {
4948 break;
4949 }
4950 var variableImport = this.variableImports[0];
4951 this.variableImports = this.variableImports.slice(1);
4952 variableImport();
4953 }
4954 }
4955 finally {
4956 this._currentDepth--;
4957 }
4958 if (this._currentDepth === 0 && this._onSequencerEmpty) {
4959 this._onSequencerEmpty();
4960 }
4961 };
4962 return ImportSequencer;
4963 }());
4964
4965 var ImportVisitor = function (importer, finish) {
4966 this._visitor = new Visitor(this);
4967 this._importer = importer;
4968 this._finish = finish;
4969 this.context = new contexts.Eval();
4970 this.importCount = 0;
4971 this.onceFileDetectionMap = {};
4972 this.recursionDetector = {};
4973 this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this));
4974 };
4975 ImportVisitor.prototype = {
4976 isReplacing: false,
4977 run: function (root) {
4978 try {
4979 // process the contents
4980 this._visitor.visit(root);
4981 }
4982 catch (e) {
4983 this.error = e;
4984 }
4985 this.isFinished = true;
4986 this._sequencer.tryRun();
4987 },
4988 _onSequencerEmpty: function () {
4989 if (!this.isFinished) {
4990 return;
4991 }
4992 this._finish(this.error);
4993 },
4994 visitImport: function (importNode, visitArgs) {
4995 var inlineCSS = importNode.options.inline;
4996 if (!importNode.css || inlineCSS) {
4997 var context = new contexts.Eval(this.context, copyArray(this.context.frames));
4998 var importParent = context.frames[0];
4999 this.importCount++;
5000 if (importNode.isVariableImport()) {
5001 this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent));
5002 }
5003 else {
5004 this.processImportNode(importNode, context, importParent);
5005 }
5006 }
5007 visitArgs.visitDeeper = false;
5008 },
5009 processImportNode: function (importNode, context, importParent) {
5010 var evaldImportNode;
5011 var inlineCSS = importNode.options.inline;
5012 try {
5013 evaldImportNode = importNode.evalForImport(context);
5014 }
5015 catch (e) {
5016 if (!e.filename) {
5017 e.index = importNode.getIndex();
5018 e.filename = importNode.fileInfo().filename;
5019 }
5020 // attempt to eval properly and treat as css
5021 importNode.css = true;
5022 // if that fails, this error will be thrown
5023 importNode.error = e;
5024 }
5025 if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
5026 if (evaldImportNode.options.multiple) {
5027 context.importMultiple = true;
5028 }
5029 // try appending if we haven't determined if it is css or not
5030 var tryAppendLessExtension = evaldImportNode.css === undefined;
5031 for (var i = 0; i < importParent.rules.length; i++) {
5032 if (importParent.rules[i] === importNode) {
5033 importParent.rules[i] = evaldImportNode;
5034 break;
5035 }
5036 }
5037 var onImported = this.onImported.bind(this, evaldImportNode, context), sequencedOnImported = this._sequencer.addImport(onImported);
5038 this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.fileInfo(), evaldImportNode.options, sequencedOnImported);
5039 }
5040 else {
5041 this.importCount--;
5042 if (this.isFinished) {
5043 this._sequencer.tryRun();
5044 }
5045 }
5046 },
5047 onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
5048 if (e) {
5049 if (!e.filename) {
5050 e.index = importNode.getIndex();
5051 e.filename = importNode.fileInfo().filename;
5052 }
5053 this.error = e;
5054 }
5055 var importVisitor = this, inlineCSS = importNode.options.inline, isPlugin = importNode.options.isPlugin, isOptional = importNode.options.optional, duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
5056 if (!context.importMultiple) {
5057 if (duplicateImport) {
5058 importNode.skip = true;
5059 }
5060 else {
5061 importNode.skip = function () {
5062 if (fullPath in importVisitor.onceFileDetectionMap) {
5063 return true;
5064 }
5065 importVisitor.onceFileDetectionMap[fullPath] = true;
5066 return false;
5067 };
5068 }
5069 }
5070 if (!fullPath && isOptional) {
5071 importNode.skip = true;
5072 }
5073 if (root) {
5074 importNode.root = root;
5075 importNode.importedFilename = fullPath;
5076 if (!inlineCSS && !isPlugin && (context.importMultiple || !duplicateImport)) {
5077 importVisitor.recursionDetector[fullPath] = true;
5078 var oldContext = this.context;
5079 this.context = context;
5080 try {
5081 this._visitor.visit(root);
5082 }
5083 catch (e) {
5084 this.error = e;
5085 }
5086 this.context = oldContext;
5087 }
5088 }
5089 importVisitor.importCount--;
5090 if (importVisitor.isFinished) {
5091 importVisitor._sequencer.tryRun();
5092 }
5093 },
5094 visitDeclaration: function (declNode, visitArgs) {
5095 if (declNode.value.type === 'DetachedRuleset') {
5096 this.context.frames.unshift(declNode);
5097 }
5098 else {
5099 visitArgs.visitDeeper = false;
5100 }
5101 },
5102 visitDeclarationOut: function (declNode) {
5103 if (declNode.value.type === 'DetachedRuleset') {
5104 this.context.frames.shift();
5105 }
5106 },
5107 visitAtRule: function (atRuleNode, visitArgs) {
5108 this.context.frames.unshift(atRuleNode);
5109 },
5110 visitAtRuleOut: function (atRuleNode) {
5111 this.context.frames.shift();
5112 },
5113 visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
5114 this.context.frames.unshift(mixinDefinitionNode);
5115 },
5116 visitMixinDefinitionOut: function (mixinDefinitionNode) {
5117 this.context.frames.shift();
5118 },
5119 visitRuleset: function (rulesetNode, visitArgs) {
5120 this.context.frames.unshift(rulesetNode);
5121 },
5122 visitRulesetOut: function (rulesetNode) {
5123 this.context.frames.shift();
5124 },
5125 visitMedia: function (mediaNode, visitArgs) {
5126 this.context.frames.unshift(mediaNode.rules[0]);
5127 },
5128 visitMediaOut: function (mediaNode) {
5129 this.context.frames.shift();
5130 }
5131 };
5132
5133 var SetTreeVisibilityVisitor = /** @class */ (function () {
5134 function SetTreeVisibilityVisitor(visible) {
5135 this.visible = visible;
5136 }
5137 SetTreeVisibilityVisitor.prototype.run = function (root) {
5138 this.visit(root);
5139 };
5140 SetTreeVisibilityVisitor.prototype.visitArray = function (nodes) {
5141 if (!nodes) {
5142 return nodes;
5143 }
5144 var cnt = nodes.length;
5145 var i;
5146 for (i = 0; i < cnt; i++) {
5147 this.visit(nodes[i]);
5148 }
5149 return nodes;
5150 };
5151 SetTreeVisibilityVisitor.prototype.visit = function (node) {
5152 if (!node) {
5153 return node;
5154 }
5155 if (node.constructor === Array) {
5156 return this.visitArray(node);
5157 }
5158 if (!node.blocksVisibility || node.blocksVisibility()) {
5159 return node;
5160 }
5161 if (this.visible) {
5162 node.ensureVisibility();
5163 }
5164 else {
5165 node.ensureInvisibility();
5166 }
5167 node.accept(this);
5168 return node;
5169 };
5170 return SetTreeVisibilityVisitor;
5171 }());
5172
5173 /* jshint loopfunc:true */
5174 var ExtendFinderVisitor = /** @class */ (function () {
5175 function ExtendFinderVisitor() {
5176 this._visitor = new Visitor(this);
5177 this.contexts = [];
5178 this.allExtendsStack = [[]];
5179 }
5180 ExtendFinderVisitor.prototype.run = function (root) {
5181 root = this._visitor.visit(root);
5182 root.allExtends = this.allExtendsStack[0];
5183 return root;
5184 };
5185 ExtendFinderVisitor.prototype.visitDeclaration = function (declNode, visitArgs) {
5186 visitArgs.visitDeeper = false;
5187 };
5188 ExtendFinderVisitor.prototype.visitMixinDefinition = function (mixinDefinitionNode, visitArgs) {
5189 visitArgs.visitDeeper = false;
5190 };
5191 ExtendFinderVisitor.prototype.visitRuleset = function (rulesetNode, visitArgs) {
5192 if (rulesetNode.root) {
5193 return;
5194 }
5195 var i;
5196 var j;
5197 var extend;
5198 var allSelectorsExtendList = [];
5199 var extendList;
5200 // get &:extend(.a); rules which apply to all selectors in this ruleset
5201 var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0;
5202 for (i = 0; i < ruleCnt; i++) {
5203 if (rulesetNode.rules[i] instanceof tree.Extend) {
5204 allSelectorsExtendList.push(rules[i]);
5205 rulesetNode.extendOnEveryPath = true;
5206 }
5207 }
5208 // now find every selector and apply the extends that apply to all extends
5209 // and the ones which apply to an individual extend
5210 var paths = rulesetNode.paths;
5211 for (i = 0; i < paths.length; i++) {
5212 var selectorPath = paths[i], selector = selectorPath[selectorPath.length - 1], selExtendList = selector.extendList;
5213 extendList = selExtendList ? copyArray(selExtendList).concat(allSelectorsExtendList)
5214 : allSelectorsExtendList;
5215 if (extendList) {
5216 extendList = extendList.map(function (allSelectorsExtend) {
5217 return allSelectorsExtend.clone();
5218 });
5219 }
5220 for (j = 0; j < extendList.length; j++) {
5221 this.foundExtends = true;
5222 extend = extendList[j];
5223 extend.findSelfSelectors(selectorPath);
5224 extend.ruleset = rulesetNode;
5225 if (j === 0) {
5226 extend.firstExtendOnThisSelectorPath = true;
5227 }
5228 this.allExtendsStack[this.allExtendsStack.length - 1].push(extend);
5229 }
5230 }
5231 this.contexts.push(rulesetNode.selectors);
5232 };
5233 ExtendFinderVisitor.prototype.visitRulesetOut = function (rulesetNode) {
5234 if (!rulesetNode.root) {
5235 this.contexts.length = this.contexts.length - 1;
5236 }
5237 };
5238 ExtendFinderVisitor.prototype.visitMedia = function (mediaNode, visitArgs) {
5239 mediaNode.allExtends = [];
5240 this.allExtendsStack.push(mediaNode.allExtends);
5241 };
5242 ExtendFinderVisitor.prototype.visitMediaOut = function (mediaNode) {
5243 this.allExtendsStack.length = this.allExtendsStack.length - 1;
5244 };
5245 ExtendFinderVisitor.prototype.visitAtRule = function (atRuleNode, visitArgs) {
5246 atRuleNode.allExtends = [];
5247 this.allExtendsStack.push(atRuleNode.allExtends);
5248 };
5249 ExtendFinderVisitor.prototype.visitAtRuleOut = function (atRuleNode) {
5250 this.allExtendsStack.length = this.allExtendsStack.length - 1;
5251 };
5252 return ExtendFinderVisitor;
5253 }());
5254 var ProcessExtendsVisitor = /** @class */ (function () {
5255 function ProcessExtendsVisitor() {
5256 this._visitor = new Visitor(this);
5257 }
5258 ProcessExtendsVisitor.prototype.run = function (root) {
5259 var extendFinder = new ExtendFinderVisitor();
5260 this.extendIndices = {};
5261 extendFinder.run(root);
5262 if (!extendFinder.foundExtends) {
5263 return root;
5264 }
5265 root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
5266 this.allExtendsStack = [root.allExtends];
5267 var newRoot = this._visitor.visit(root);
5268 this.checkExtendsForNonMatched(root.allExtends);
5269 return newRoot;
5270 };
5271 ProcessExtendsVisitor.prototype.checkExtendsForNonMatched = function (extendList) {
5272 var indices = this.extendIndices;
5273 extendList.filter(function (extend) {
5274 return !extend.hasFoundMatches && extend.parent_ids.length == 1;
5275 }).forEach(function (extend) {
5276 var selector = '_unknown_';
5277 try {
5278 selector = extend.selector.toCSS({});
5279 }
5280 catch (_) { }
5281 if (!indices[extend.index + " " + selector]) {
5282 indices[extend.index + " " + selector] = true;
5283 logger.warn("extend '" + selector + "' has no matches");
5284 }
5285 });
5286 };
5287 ProcessExtendsVisitor.prototype.doExtendChaining = function (extendsList, extendsListTarget, iterationCount) {
5288 //
5289 // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering
5290 // and pasting the selector we would do normally, but we are also adding an extend with the same target selector
5291 // this means this new extend can then go and alter other extends
5292 //
5293 // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
5294 // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already
5295 // processed if we look at each selector at a time, as is done in visitRuleset
5296 var extendIndex;
5297 var targetExtendIndex;
5298 var matches;
5299 var extendsToAdd = [];
5300 var newSelector;
5301 var extendVisitor = this;
5302 var selectorPath;
5303 var extend;
5304 var targetExtend;
5305 var newExtend;
5306 iterationCount = iterationCount || 0;
5307 // loop through comparing every extend with every target extend.
5308 // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
5309 // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
5310 // and the second is the target.
5311 // the separation into two lists allows us to process a subset of chains with a bigger set, as is the
5312 // case when processing media queries
5313 for (extendIndex = 0; extendIndex < extendsList.length; extendIndex++) {
5314 for (targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++) {
5315 extend = extendsList[extendIndex];
5316 targetExtend = extendsListTarget[targetExtendIndex];
5317 // look for circular references
5318 if (extend.parent_ids.indexOf(targetExtend.object_id) >= 0) {
5319 continue;
5320 }
5321 // find a match in the target extends self selector (the bit before :extend)
5322 selectorPath = [targetExtend.selfSelectors[0]];
5323 matches = extendVisitor.findMatch(extend, selectorPath);
5324 if (matches.length) {
5325 extend.hasFoundMatches = true;
5326 // we found a match, so for each self selector..
5327 extend.selfSelectors.forEach(function (selfSelector) {
5328 var info = targetExtend.visibilityInfo();
5329 // process the extend as usual
5330 newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector, extend.isVisible());
5331 // but now we create a new extend from it
5332 newExtend = new (tree.Extend)(targetExtend.selector, targetExtend.option, 0, targetExtend.fileInfo(), info);
5333 newExtend.selfSelectors = newSelector;
5334 // add the extend onto the list of extends for that selector
5335 newSelector[newSelector.length - 1].extendList = [newExtend];
5336 // record that we need to add it.
5337 extendsToAdd.push(newExtend);
5338 newExtend.ruleset = targetExtend.ruleset;
5339 // remember its parents for circular references
5340 newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids);
5341 // only process the selector once.. if we have :extend(.a,.b) then multiple
5342 // extends will look at the same selector path, so when extending
5343 // we know that any others will be duplicates in terms of what is added to the css
5344 if (targetExtend.firstExtendOnThisSelectorPath) {
5345 newExtend.firstExtendOnThisSelectorPath = true;
5346 targetExtend.ruleset.paths.push(newSelector);
5347 }
5348 });
5349 }
5350 }
5351 }
5352 if (extendsToAdd.length) {
5353 // try to detect circular references to stop a stack overflow.
5354 // may no longer be needed.
5355 this.extendChainCount++;
5356 if (iterationCount > 100) {
5357 var selectorOne = '{unable to calculate}';
5358 var selectorTwo = '{unable to calculate}';
5359 try {
5360 selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
5361 selectorTwo = extendsToAdd[0].selector.toCSS();
5362 }
5363 catch (e) { }
5364 throw { message: "extend circular reference detected. One of the circular extends is currently:" + selectorOne + ":extend(" + selectorTwo + ")" };
5365 }
5366 // now process the new extends on the existing rules so that we can handle a extending b extending c extending
5367 // d extending e...
5368 return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount + 1));
5369 }
5370 else {
5371 return extendsToAdd;
5372 }
5373 };
5374 ProcessExtendsVisitor.prototype.visitDeclaration = function (ruleNode, visitArgs) {
5375 visitArgs.visitDeeper = false;
5376 };
5377 ProcessExtendsVisitor.prototype.visitMixinDefinition = function (mixinDefinitionNode, visitArgs) {
5378 visitArgs.visitDeeper = false;
5379 };
5380 ProcessExtendsVisitor.prototype.visitSelector = function (selectorNode, visitArgs) {
5381 visitArgs.visitDeeper = false;
5382 };
5383 ProcessExtendsVisitor.prototype.visitRuleset = function (rulesetNode, visitArgs) {
5384 if (rulesetNode.root) {
5385 return;
5386 }
5387 var matches;
5388 var pathIndex;
5389 var extendIndex;
5390 var allExtends = this.allExtendsStack[this.allExtendsStack.length - 1];
5391 var selectorsToAdd = [];
5392 var extendVisitor = this;
5393 var selectorPath;
5394 // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
5395 for (extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
5396 for (pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
5397 selectorPath = rulesetNode.paths[pathIndex];
5398 // extending extends happens initially, before the main pass
5399 if (rulesetNode.extendOnEveryPath) {
5400 continue;
5401 }
5402 var extendList = selectorPath[selectorPath.length - 1].extendList;
5403 if (extendList && extendList.length) {
5404 continue;
5405 }
5406 matches = this.findMatch(allExtends[extendIndex], selectorPath);
5407 if (matches.length) {
5408 allExtends[extendIndex].hasFoundMatches = true;
5409 allExtends[extendIndex].selfSelectors.forEach(function (selfSelector) {
5410 var extendedSelectors;
5411 extendedSelectors = extendVisitor.extendSelector(matches, selectorPath, selfSelector, allExtends[extendIndex].isVisible());
5412 selectorsToAdd.push(extendedSelectors);
5413 });
5414 }
5415 }
5416 }
5417 rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
5418 };
5419 ProcessExtendsVisitor.prototype.findMatch = function (extend, haystackSelectorPath) {
5420 //
5421 // look through the haystack selector path to try and find the needle - extend.selector
5422 // returns an array of selector matches that can then be replaced
5423 //
5424 var haystackSelectorIndex;
5425 var hackstackSelector;
5426 var hackstackElementIndex;
5427 var haystackElement;
5428 var targetCombinator;
5429 var i;
5430 var extendVisitor = this;
5431 var needleElements = extend.selector.elements;
5432 var potentialMatches = [];
5433 var potentialMatch;
5434 var matches = [];
5435 // loop through the haystack elements
5436 for (haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
5437 hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
5438 for (hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
5439 haystackElement = hackstackSelector.elements[hackstackElementIndex];
5440 // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
5441 if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
5442 potentialMatches.push({ pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0,
5443 initialCombinator: haystackElement.combinator });
5444 }
5445 for (i = 0; i < potentialMatches.length; i++) {
5446 potentialMatch = potentialMatches[i];
5447 // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
5448 // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to
5449 // work out what the resulting combinator will be
5450 targetCombinator = haystackElement.combinator.value;
5451 if (targetCombinator === '' && hackstackElementIndex === 0) {
5452 targetCombinator = ' ';
5453 }
5454 // if we don't match, null our match to indicate failure
5455 if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) ||
5456 (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
5457 potentialMatch = null;
5458 }
5459 else {
5460 potentialMatch.matched++;
5461 }
5462 // if we are still valid and have finished, test whether we have elements after and whether these are allowed
5463 if (potentialMatch) {
5464 potentialMatch.finished = potentialMatch.matched === needleElements.length;
5465 if (potentialMatch.finished &&
5466 (!extend.allowAfter &&
5467 (hackstackElementIndex + 1 < hackstackSelector.elements.length || haystackSelectorIndex + 1 < haystackSelectorPath.length))) {
5468 potentialMatch = null;
5469 }
5470 }
5471 // if null we remove, if not, we are still valid, so either push as a valid match or continue
5472 if (potentialMatch) {
5473 if (potentialMatch.finished) {
5474 potentialMatch.length = needleElements.length;
5475 potentialMatch.endPathIndex = haystackSelectorIndex;
5476 potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
5477 potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
5478 matches.push(potentialMatch);
5479 }
5480 }
5481 else {
5482 potentialMatches.splice(i, 1);
5483 i--;
5484 }
5485 }
5486 }
5487 }
5488 return matches;
5489 };
5490 ProcessExtendsVisitor.prototype.isElementValuesEqual = function (elementValue1, elementValue2) {
5491 if (typeof elementValue1 === 'string' || typeof elementValue2 === 'string') {
5492 return elementValue1 === elementValue2;
5493 }
5494 if (elementValue1 instanceof tree.Attribute) {
5495 if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) {
5496 return false;
5497 }
5498 if (!elementValue1.value || !elementValue2.value) {
5499 if (elementValue1.value || elementValue2.value) {
5500 return false;
5501 }
5502 return true;
5503 }
5504 elementValue1 = elementValue1.value.value || elementValue1.value;
5505 elementValue2 = elementValue2.value.value || elementValue2.value;
5506 return elementValue1 === elementValue2;
5507 }
5508 elementValue1 = elementValue1.value;
5509 elementValue2 = elementValue2.value;
5510 if (elementValue1 instanceof tree.Selector) {
5511 if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
5512 return false;
5513 }
5514 for (var i = 0; i < elementValue1.elements.length; i++) {
5515 if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
5516 if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
5517 return false;
5518 }
5519 }
5520 if (!this.isElementValuesEqual(elementValue1.elements[i].value, elementValue2.elements[i].value)) {
5521 return false;
5522 }
5523 }
5524 return true;
5525 }
5526 return false;
5527 };
5528 ProcessExtendsVisitor.prototype.extendSelector = function (matches, selectorPath, replacementSelector, isVisible) {
5529 // for a set of matches, replace each match with the replacement selector
5530 var currentSelectorPathIndex = 0, currentSelectorPathElementIndex = 0, path = [], matchIndex, selector, firstElement, match, newElements;
5531 for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
5532 match = matches[matchIndex];
5533 selector = selectorPath[match.pathIndex];
5534 firstElement = new tree.Element(match.initialCombinator, replacementSelector.elements[0].value, replacementSelector.elements[0].isVariable, replacementSelector.elements[0].getIndex(), replacementSelector.elements[0].fileInfo());
5535 if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
5536 path[path.length - 1].elements = path[path.length - 1]
5537 .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
5538 currentSelectorPathElementIndex = 0;
5539 currentSelectorPathIndex++;
5540 }
5541 newElements = selector.elements
5542 .slice(currentSelectorPathElementIndex, match.index)
5543 .concat([firstElement])
5544 .concat(replacementSelector.elements.slice(1));
5545 if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) {
5546 path[path.length - 1].elements =
5547 path[path.length - 1].elements.concat(newElements);
5548 }
5549 else {
5550 path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
5551 path.push(new tree.Selector(newElements));
5552 }
5553 currentSelectorPathIndex = match.endPathIndex;
5554 currentSelectorPathElementIndex = match.endPathElementIndex;
5555 if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) {
5556 currentSelectorPathElementIndex = 0;
5557 currentSelectorPathIndex++;
5558 }
5559 }
5560 if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
5561 path[path.length - 1].elements = path[path.length - 1]
5562 .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
5563 currentSelectorPathIndex++;
5564 }
5565 path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
5566 path = path.map(function (currentValue) {
5567 // we can re-use elements here, because the visibility property matters only for selectors
5568 var derived = currentValue.createDerived(currentValue.elements);
5569 if (isVisible) {
5570 derived.ensureVisibility();
5571 }
5572 else {
5573 derived.ensureInvisibility();
5574 }
5575 return derived;
5576 });
5577 return path;
5578 };
5579 ProcessExtendsVisitor.prototype.visitMedia = function (mediaNode, visitArgs) {
5580 var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
5581 newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
5582 this.allExtendsStack.push(newAllExtends);
5583 };
5584 ProcessExtendsVisitor.prototype.visitMediaOut = function (mediaNode) {
5585 var lastIndex = this.allExtendsStack.length - 1;
5586 this.allExtendsStack.length = lastIndex;
5587 };
5588 ProcessExtendsVisitor.prototype.visitAtRule = function (atRuleNode, visitArgs) {
5589 var newAllExtends = atRuleNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
5590 newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, atRuleNode.allExtends));
5591 this.allExtendsStack.push(newAllExtends);
5592 };
5593 ProcessExtendsVisitor.prototype.visitAtRuleOut = function (atRuleNode) {
5594 var lastIndex = this.allExtendsStack.length - 1;
5595 this.allExtendsStack.length = lastIndex;
5596 };
5597 return ProcessExtendsVisitor;
5598 }());
5599
5600 var JoinSelectorVisitor = /** @class */ (function () {
5601 function JoinSelectorVisitor() {
5602 this.contexts = [[]];
5603 this._visitor = new Visitor(this);
5604 }
5605 JoinSelectorVisitor.prototype.run = function (root) {
5606 return this._visitor.visit(root);
5607 };
5608 JoinSelectorVisitor.prototype.visitDeclaration = function (declNode, visitArgs) {
5609 visitArgs.visitDeeper = false;
5610 };
5611 JoinSelectorVisitor.prototype.visitMixinDefinition = function (mixinDefinitionNode, visitArgs) {
5612 visitArgs.visitDeeper = false;
5613 };
5614 JoinSelectorVisitor.prototype.visitRuleset = function (rulesetNode, visitArgs) {
5615 var context = this.contexts[this.contexts.length - 1];
5616 var paths = [];
5617 var selectors;
5618 this.contexts.push(paths);
5619 if (!rulesetNode.root) {
5620 selectors = rulesetNode.selectors;
5621 if (selectors) {
5622 selectors = selectors.filter(function (selector) { return selector.getIsOutput(); });
5623 rulesetNode.selectors = selectors.length ? selectors : (selectors = null);
5624 if (selectors) {
5625 rulesetNode.joinSelectors(paths, context, selectors);
5626 }
5627 }
5628 if (!selectors) {
5629 rulesetNode.rules = null;
5630 }
5631 rulesetNode.paths = paths;
5632 }
5633 };
5634 JoinSelectorVisitor.prototype.visitRulesetOut = function (rulesetNode) {
5635 this.contexts.length = this.contexts.length - 1;
5636 };
5637 JoinSelectorVisitor.prototype.visitMedia = function (mediaNode, visitArgs) {
5638 var context = this.contexts[this.contexts.length - 1];
5639 mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
5640 };
5641 JoinSelectorVisitor.prototype.visitAtRule = function (atRuleNode, visitArgs) {
5642 var context = this.contexts[this.contexts.length - 1];
5643 if (atRuleNode.rules && atRuleNode.rules.length) {
5644 atRuleNode.rules[0].root = (atRuleNode.isRooted || context.length === 0 || null);
5645 }
5646 };
5647 return JoinSelectorVisitor;
5648 }());
5649
5650 var CSSVisitorUtils = /** @class */ (function () {
5651 function CSSVisitorUtils(context) {
5652 this._visitor = new Visitor(this);
5653 this._context = context;
5654 }
5655 CSSVisitorUtils.prototype.containsSilentNonBlockedChild = function (bodyRules) {
5656 var rule;
5657 if (!bodyRules) {
5658 return false;
5659 }
5660 for (var r = 0; r < bodyRules.length; r++) {
5661 rule = bodyRules[r];
5662 if (rule.isSilent && rule.isSilent(this._context) && !rule.blocksVisibility()) {
5663 // the atrule contains something that was referenced (likely by extend)
5664 // therefore it needs to be shown in output too
5665 return true;
5666 }
5667 }
5668 return false;
5669 };
5670 CSSVisitorUtils.prototype.keepOnlyVisibleChilds = function (owner) {
5671 if (owner && owner.rules) {
5672 owner.rules = owner.rules.filter(function (thing) { return thing.isVisible(); });
5673 }
5674 };
5675 CSSVisitorUtils.prototype.isEmpty = function (owner) {
5676 return (owner && owner.rules)
5677 ? (owner.rules.length === 0) : true;
5678 };
5679 CSSVisitorUtils.prototype.hasVisibleSelector = function (rulesetNode) {
5680 return (rulesetNode && rulesetNode.paths)
5681 ? (rulesetNode.paths.length > 0) : false;
5682 };
5683 CSSVisitorUtils.prototype.resolveVisibility = function (node, originalRules) {
5684 if (!node.blocksVisibility()) {
5685 if (this.isEmpty(node) && !this.containsSilentNonBlockedChild(originalRules)) {
5686 return;
5687 }
5688 return node;
5689 }
5690 var compiledRulesBody = node.rules[0];
5691 this.keepOnlyVisibleChilds(compiledRulesBody);
5692 if (this.isEmpty(compiledRulesBody)) {
5693 return;
5694 }
5695 node.ensureVisibility();
5696 node.removeVisibilityBlock();
5697 return node;
5698 };
5699 CSSVisitorUtils.prototype.isVisibleRuleset = function (rulesetNode) {
5700 if (rulesetNode.firstRoot) {
5701 return true;
5702 }
5703 if (this.isEmpty(rulesetNode)) {
5704 return false;
5705 }
5706 if (!rulesetNode.root && !this.hasVisibleSelector(rulesetNode)) {
5707 return false;
5708 }
5709 return true;
5710 };
5711 return CSSVisitorUtils;
5712 }());
5713 var ToCSSVisitor = function (context) {
5714 this._visitor = new Visitor(this);
5715 this._context = context;
5716 this.utils = new CSSVisitorUtils(context);
5717 };
5718 ToCSSVisitor.prototype = {
5719 isReplacing: true,
5720 run: function (root) {
5721 return this._visitor.visit(root);
5722 },
5723 visitDeclaration: function (declNode, visitArgs) {
5724 if (declNode.blocksVisibility() || declNode.variable) {
5725 return;
5726 }
5727 return declNode;
5728 },
5729 visitMixinDefinition: function (mixinNode, visitArgs) {
5730 // mixin definitions do not get eval'd - this means they keep state
5731 // so we have to clear that state here so it isn't used if toCSS is called twice
5732 mixinNode.frames = [];
5733 },
5734 visitExtend: function (extendNode, visitArgs) {
5735 },
5736 visitComment: function (commentNode, visitArgs) {
5737 if (commentNode.blocksVisibility() || commentNode.isSilent(this._context)) {
5738 return;
5739 }
5740 return commentNode;
5741 },
5742 visitMedia: function (mediaNode, visitArgs) {
5743 var originalRules = mediaNode.rules[0].rules;
5744 mediaNode.accept(this._visitor);
5745 visitArgs.visitDeeper = false;
5746 return this.utils.resolveVisibility(mediaNode, originalRules);
5747 },
5748 visitImport: function (importNode, visitArgs) {
5749 if (importNode.blocksVisibility()) {
5750 return;
5751 }
5752 return importNode;
5753 },
5754 visitAtRule: function (atRuleNode, visitArgs) {
5755 if (atRuleNode.rules && atRuleNode.rules.length) {
5756 return this.visitAtRuleWithBody(atRuleNode, visitArgs);
5757 }
5758 else {
5759 return this.visitAtRuleWithoutBody(atRuleNode, visitArgs);
5760 }
5761 },
5762 visitAnonymous: function (anonymousNode, visitArgs) {
5763 if (!anonymousNode.blocksVisibility()) {
5764 anonymousNode.accept(this._visitor);
5765 return anonymousNode;
5766 }
5767 },
5768 visitAtRuleWithBody: function (atRuleNode, visitArgs) {
5769 // if there is only one nested ruleset and that one has no path, then it is
5770 // just fake ruleset
5771 function hasFakeRuleset(atRuleNode) {
5772 var bodyRules = atRuleNode.rules;
5773 return bodyRules.length === 1 && (!bodyRules[0].paths || bodyRules[0].paths.length === 0);
5774 }
5775 function getBodyRules(atRuleNode) {
5776 var nodeRules = atRuleNode.rules;
5777 if (hasFakeRuleset(atRuleNode)) {
5778 return nodeRules[0].rules;
5779 }
5780 return nodeRules;
5781 }
5782 // it is still true that it is only one ruleset in array
5783 // this is last such moment
5784 // process childs
5785 var originalRules = getBodyRules(atRuleNode);
5786 atRuleNode.accept(this._visitor);
5787 visitArgs.visitDeeper = false;
5788 if (!this.utils.isEmpty(atRuleNode)) {
5789 this._mergeRules(atRuleNode.rules[0].rules);
5790 }
5791 return this.utils.resolveVisibility(atRuleNode, originalRules);
5792 },
5793 visitAtRuleWithoutBody: function (atRuleNode, visitArgs) {
5794 if (atRuleNode.blocksVisibility()) {
5795 return;
5796 }
5797 if (atRuleNode.name === '@charset') {
5798 // Only output the debug info together with subsequent @charset definitions
5799 // a comment (or @media statement) before the actual @charset atrule would
5800 // be considered illegal css as it has to be on the first line
5801 if (this.charset) {
5802 if (atRuleNode.debugInfo) {
5803 var comment = new tree.Comment("/* " + atRuleNode.toCSS(this._context).replace(/\n/g, '') + " */\n");
5804 comment.debugInfo = atRuleNode.debugInfo;
5805 return this._visitor.visit(comment);
5806 }
5807 return;
5808 }
5809 this.charset = true;
5810 }
5811 return atRuleNode;
5812 },
5813 checkValidNodes: function (rules, isRoot) {
5814 if (!rules) {
5815 return;
5816 }
5817 for (var i = 0; i < rules.length; i++) {
5818 var ruleNode = rules[i];
5819 if (isRoot && ruleNode instanceof tree.Declaration && !ruleNode.variable) {
5820 throw { message: 'Properties must be inside selector blocks. They cannot be in the root',
5821 index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename };
5822 }
5823 if (ruleNode instanceof tree.Call) {
5824 throw { message: "Function '" + ruleNode.name + "' did not return a root node", index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename };
5825 }
5826 if (ruleNode.type && !ruleNode.allowRoot) {
5827 throw { message: ruleNode.type + " node returned by a function is not valid here", index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename };
5828 }
5829 }
5830 },
5831 visitRuleset: function (rulesetNode, visitArgs) {
5832 // at this point rulesets are nested into each other
5833 var rule;
5834 var rulesets = [];
5835 this.checkValidNodes(rulesetNode.rules, rulesetNode.firstRoot);
5836 if (!rulesetNode.root) {
5837 // remove invisible paths
5838 this._compileRulesetPaths(rulesetNode);
5839 // remove rulesets from this ruleset body and compile them separately
5840 var nodeRules = rulesetNode.rules;
5841 var nodeRuleCnt = nodeRules ? nodeRules.length : 0;
5842 for (var i = 0; i < nodeRuleCnt;) {
5843 rule = nodeRules[i];
5844 if (rule && rule.rules) {
5845 // visit because we are moving them out from being a child
5846 rulesets.push(this._visitor.visit(rule));
5847 nodeRules.splice(i, 1);
5848 nodeRuleCnt--;
5849 continue;
5850 }
5851 i++;
5852 }
5853 // accept the visitor to remove rules and refactor itself
5854 // then we can decide nogw whether we want it or not
5855 // compile body
5856 if (nodeRuleCnt > 0) {
5857 rulesetNode.accept(this._visitor);
5858 }
5859 else {
5860 rulesetNode.rules = null;
5861 }
5862 visitArgs.visitDeeper = false;
5863 }
5864 else { // if (! rulesetNode.root) {
5865 rulesetNode.accept(this._visitor);
5866 visitArgs.visitDeeper = false;
5867 }
5868 if (rulesetNode.rules) {
5869 this._mergeRules(rulesetNode.rules);
5870 this._removeDuplicateRules(rulesetNode.rules);
5871 }
5872 // now decide whether we keep the ruleset
5873 if (this.utils.isVisibleRuleset(rulesetNode)) {
5874 rulesetNode.ensureVisibility();
5875 rulesets.splice(0, 0, rulesetNode);
5876 }
5877 if (rulesets.length === 1) {
5878 return rulesets[0];
5879 }
5880 return rulesets;
5881 },
5882 _compileRulesetPaths: function (rulesetNode) {
5883 if (rulesetNode.paths) {
5884 rulesetNode.paths = rulesetNode.paths
5885 .filter(function (p) {
5886 var i;
5887 if (p[0].elements[0].combinator.value === ' ') {
5888 p[0].elements[0].combinator = new (tree.Combinator)('');
5889 }
5890 for (i = 0; i < p.length; i++) {
5891 if (p[i].isVisible() && p[i].getIsOutput()) {
5892 return true;
5893 }
5894 }
5895 return false;
5896 });
5897 }
5898 },
5899 _removeDuplicateRules: function (rules) {
5900 if (!rules) {
5901 return;
5902 }
5903 // remove duplicates
5904 var ruleCache = {};
5905 var ruleList;
5906 var rule;
5907 var i;
5908 for (i = rules.length - 1; i >= 0; i--) {
5909 rule = rules[i];
5910 if (rule instanceof tree.Declaration) {
5911 if (!ruleCache[rule.name]) {
5912 ruleCache[rule.name] = rule;
5913 }
5914 else {
5915 ruleList = ruleCache[rule.name];
5916 if (ruleList instanceof tree.Declaration) {
5917 ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)];
5918 }
5919 var ruleCSS = rule.toCSS(this._context);
5920 if (ruleList.indexOf(ruleCSS) !== -1) {
5921 rules.splice(i, 1);
5922 }
5923 else {
5924 ruleList.push(ruleCSS);
5925 }
5926 }
5927 }
5928 }
5929 },
5930 _mergeRules: function (rules) {
5931 if (!rules) {
5932 return;
5933 }
5934 var groups = {};
5935 var groupsArr = [];
5936 for (var i = 0; i < rules.length; i++) {
5937 var rule = rules[i];
5938 if (rule.merge) {
5939 var key = rule.name;
5940 groups[key] ? rules.splice(i--, 1) :
5941 groupsArr.push(groups[key] = []);
5942 groups[key].push(rule);
5943 }
5944 }
5945 groupsArr.forEach(function (group) {
5946 if (group.length > 0) {
5947 var result_1 = group[0];
5948 var space_1 = [];
5949 var comma_1 = [new tree.Expression(space_1)];
5950 group.forEach(function (rule) {
5951 if ((rule.merge === '+') && (space_1.length > 0)) {
5952 comma_1.push(new tree.Expression(space_1 = []));
5953 }
5954 space_1.push(rule.value);
5955 result_1.important = result_1.important || rule.important;
5956 });
5957 result_1.value = new tree.Value(comma_1);
5958 }
5959 });
5960 }
5961 };
5962
5963 var visitors = {
5964 Visitor: Visitor,
5965 ImportVisitor: ImportVisitor,
5966 MarkVisibleSelectorsVisitor: SetTreeVisibilityVisitor,
5967 ExtendVisitor: ProcessExtendsVisitor,
5968 JoinSelectorVisitor: JoinSelectorVisitor,
5969 ToCSSVisitor: ToCSSVisitor
5970 };
5971
5972 // Split the input into chunks.
5973 function chunker (input, fail) {
5974 var len = input.length;
5975 var level = 0;
5976 var parenLevel = 0;
5977 var lastOpening;
5978 var lastOpeningParen;
5979 var lastMultiComment;
5980 var lastMultiCommentEndBrace;
5981 var chunks = [];
5982 var emitFrom = 0;
5983 var chunkerCurrentIndex;
5984 var currentChunkStartIndex;
5985 var cc;
5986 var cc2;
5987 var matched;
5988 function emitChunk(force) {
5989 var len = chunkerCurrentIndex - emitFrom;
5990 if (((len < 512) && !force) || !len) {
5991 return;
5992 }
5993 chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1));
5994 emitFrom = chunkerCurrentIndex + 1;
5995 }
5996 for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
5997 cc = input.charCodeAt(chunkerCurrentIndex);
5998 if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
5999 // a-z or whitespace
6000 continue;
6001 }
6002 switch (cc) {
6003 case 40: // (
6004 parenLevel++;
6005 lastOpeningParen = chunkerCurrentIndex;
6006 continue;
6007 case 41: // )
6008 if (--parenLevel < 0) {
6009 return fail('missing opening `(`', chunkerCurrentIndex);
6010 }
6011 continue;
6012 case 59: // ;
6013 if (!parenLevel) {
6014 emitChunk();
6015 }
6016 continue;
6017 case 123: // {
6018 level++;
6019 lastOpening = chunkerCurrentIndex;
6020 continue;
6021 case 125: // }
6022 if (--level < 0) {
6023 return fail('missing opening `{`', chunkerCurrentIndex);
6024 }
6025 if (!level && !parenLevel) {
6026 emitChunk();
6027 }
6028 continue;
6029 case 92: // \
6030 if (chunkerCurrentIndex < len - 1) {
6031 chunkerCurrentIndex++;
6032 continue;
6033 }
6034 return fail('unescaped `\\`', chunkerCurrentIndex);
6035 case 34:
6036 case 39:
6037 case 96: // ", ' and `
6038 matched = 0;
6039 currentChunkStartIndex = chunkerCurrentIndex;
6040 for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
6041 cc2 = input.charCodeAt(chunkerCurrentIndex);
6042 if (cc2 > 96) {
6043 continue;
6044 }
6045 if (cc2 == cc) {
6046 matched = 1;
6047 break;
6048 }
6049 if (cc2 == 92) { // \
6050 if (chunkerCurrentIndex == len - 1) {
6051 return fail('unescaped `\\`', chunkerCurrentIndex);
6052 }
6053 chunkerCurrentIndex++;
6054 }
6055 }
6056 if (matched) {
6057 continue;
6058 }
6059 return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
6060 case 47: // /, check for comment
6061 if (parenLevel || (chunkerCurrentIndex == len - 1)) {
6062 continue;
6063 }
6064 cc2 = input.charCodeAt(chunkerCurrentIndex + 1);
6065 if (cc2 == 47) {
6066 // //, find lnfeed
6067 for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
6068 cc2 = input.charCodeAt(chunkerCurrentIndex);
6069 if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) {
6070 break;
6071 }
6072 }
6073 }
6074 else if (cc2 == 42) {
6075 // /*, find */
6076 lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex;
6077 for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) {
6078 cc2 = input.charCodeAt(chunkerCurrentIndex);
6079 if (cc2 == 125) {
6080 lastMultiCommentEndBrace = chunkerCurrentIndex;
6081 }
6082 if (cc2 != 42) {
6083 continue;
6084 }
6085 if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) {
6086 break;
6087 }
6088 }
6089 if (chunkerCurrentIndex == len - 1) {
6090 return fail('missing closing `*/`', currentChunkStartIndex);
6091 }
6092 chunkerCurrentIndex++;
6093 }
6094 continue;
6095 case 42: // *, check for unmatched */
6096 if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) {
6097 return fail('unmatched `/*`', chunkerCurrentIndex);
6098 }
6099 continue;
6100 }
6101 }
6102 if (level !== 0) {
6103 if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
6104 return fail('missing closing `}` or `*/`', lastOpening);
6105 }
6106 else {
6107 return fail('missing closing `}`', lastOpening);
6108 }
6109 }
6110 else if (parenLevel !== 0) {
6111 return fail('missing closing `)`', lastOpeningParen);
6112 }
6113 emitChunk(true);
6114 return chunks;
6115 }
6116
6117 var getParserInput = (function () {
6118 var // Less input string
6119 input;
6120 var // current chunk
6121 j;
6122 var // holds state for backtracking
6123 saveStack = [];
6124 var // furthest index the parser has gone to
6125 furthest;
6126 var // if this is furthest we got to, this is the probably cause
6127 furthestPossibleErrorMessage;
6128 var // chunkified input
6129 chunks;
6130 var // current chunk
6131 current;
6132 var // index of current chunk, in `input`
6133 currentPos;
6134 var parserInput = {};
6135 var CHARCODE_SPACE = 32;
6136 var CHARCODE_TAB = 9;
6137 var CHARCODE_LF = 10;
6138 var CHARCODE_CR = 13;
6139 var CHARCODE_PLUS = 43;
6140 var CHARCODE_COMMA = 44;
6141 var CHARCODE_FORWARD_SLASH = 47;
6142 var CHARCODE_9 = 57;
6143 function skipWhitespace(length) {
6144 var oldi = parserInput.i;
6145 var oldj = j;
6146 var curr = parserInput.i - currentPos;
6147 var endIndex = parserInput.i + current.length - curr;
6148 var mem = (parserInput.i += length);
6149 var inp = input;
6150 var c;
6151 var nextChar;
6152 var comment;
6153 for (; parserInput.i < endIndex; parserInput.i++) {
6154 c = inp.charCodeAt(parserInput.i);
6155 if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
6156 nextChar = inp.charAt(parserInput.i + 1);
6157 if (nextChar === '/') {
6158 comment = { index: parserInput.i, isLineComment: true };
6159 var nextNewLine = inp.indexOf('\n', parserInput.i + 2);
6160 if (nextNewLine < 0) {
6161 nextNewLine = endIndex;
6162 }
6163 parserInput.i = nextNewLine;
6164 comment.text = inp.substr(comment.index, parserInput.i - comment.index);
6165 parserInput.commentStore.push(comment);
6166 continue;
6167 }
6168 else if (nextChar === '*') {
6169 var nextStarSlash = inp.indexOf('*/', parserInput.i + 2);
6170 if (nextStarSlash >= 0) {
6171 comment = {
6172 index: parserInput.i,
6173 text: inp.substr(parserInput.i, nextStarSlash + 2 - parserInput.i),
6174 isLineComment: false
6175 };
6176 parserInput.i += comment.text.length - 1;
6177 parserInput.commentStore.push(comment);
6178 continue;
6179 }
6180 }
6181 break;
6182 }
6183 if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
6184 break;
6185 }
6186 }
6187 current = current.slice(length + parserInput.i - mem + curr);
6188 currentPos = parserInput.i;
6189 if (!current.length) {
6190 if (j < chunks.length - 1) {
6191 current = chunks[++j];
6192 skipWhitespace(0); // skip space at the beginning of a chunk
6193 return true; // things changed
6194 }
6195 parserInput.finished = true;
6196 }
6197 return oldi !== parserInput.i || oldj !== j;
6198 }
6199 parserInput.save = function () {
6200 currentPos = parserInput.i;
6201 saveStack.push({ current: current, i: parserInput.i, j: j });
6202 };
6203 parserInput.restore = function (possibleErrorMessage) {
6204 if (parserInput.i > furthest || (parserInput.i === furthest && possibleErrorMessage && !furthestPossibleErrorMessage)) {
6205 furthest = parserInput.i;
6206 furthestPossibleErrorMessage = possibleErrorMessage;
6207 }
6208 var state = saveStack.pop();
6209 current = state.current;
6210 currentPos = parserInput.i = state.i;
6211 j = state.j;
6212 };
6213 parserInput.forget = function () {
6214 saveStack.pop();
6215 };
6216 parserInput.isWhitespace = function (offset) {
6217 var pos = parserInput.i + (offset || 0);
6218 var code = input.charCodeAt(pos);
6219 return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF);
6220 };
6221 // Specialization of $(tok)
6222 parserInput.$re = function (tok) {
6223 if (parserInput.i > currentPos) {
6224 current = current.slice(parserInput.i - currentPos);
6225 currentPos = parserInput.i;
6226 }
6227 var m = tok.exec(current);
6228 if (!m) {
6229 return null;
6230 }
6231 skipWhitespace(m[0].length);
6232 if (typeof m === 'string') {
6233 return m;
6234 }
6235 return m.length === 1 ? m[0] : m;
6236 };
6237 parserInput.$char = function (tok) {
6238 if (input.charAt(parserInput.i) !== tok) {
6239 return null;
6240 }
6241 skipWhitespace(1);
6242 return tok;
6243 };
6244 parserInput.$str = function (tok) {
6245 var tokLength = tok.length;
6246 // https://jsperf.com/string-startswith/21
6247 for (var i = 0; i < tokLength; i++) {
6248 if (input.charAt(parserInput.i + i) !== tok.charAt(i)) {
6249 return null;
6250 }
6251 }
6252 skipWhitespace(tokLength);
6253 return tok;
6254 };
6255 parserInput.$quoted = function (loc) {
6256 var pos = loc || parserInput.i;
6257 var startChar = input.charAt(pos);
6258 if (startChar !== '\'' && startChar !== '"') {
6259 return;
6260 }
6261 var length = input.length;
6262 var currentPosition = pos;
6263 for (var i = 1; i + currentPosition < length; i++) {
6264 var nextChar = input.charAt(i + currentPosition);
6265 switch (nextChar) {
6266 case '\\':
6267 i++;
6268 continue;
6269 case '\r':
6270 case '\n':
6271 break;
6272 case startChar:
6273 var str = input.substr(currentPosition, i + 1);
6274 if (!loc && loc !== 0) {
6275 skipWhitespace(i + 1);
6276 return str;
6277 }
6278 return [startChar, str];
6279 }
6280 }
6281 return null;
6282 };
6283 /**
6284 * Permissive parsing. Ignores everything except matching {} [] () and quotes
6285 * until matching token (outside of blocks)
6286 */
6287 parserInput.$parseUntil = function (tok) {
6288 var quote = '';
6289 var returnVal = null;
6290 var inComment = false;
6291 var blockDepth = 0;
6292 var blockStack = [];
6293 var parseGroups = [];
6294 var length = input.length;
6295 var startPos = parserInput.i;
6296 var lastPos = parserInput.i;
6297 var i = parserInput.i;
6298 var loop = true;
6299 var testChar;
6300 if (typeof tok === 'string') {
6301 testChar = function (char) { return char === tok; };
6302 }
6303 else {
6304 testChar = function (char) { return tok.test(char); };
6305 }
6306 do {
6307 var nextChar = input.charAt(i);
6308 if (blockDepth === 0 && testChar(nextChar)) {
6309 returnVal = input.substr(lastPos, i - lastPos);
6310 if (returnVal) {
6311 parseGroups.push(returnVal);
6312 }
6313 else {
6314 parseGroups.push(' ');
6315 }
6316 returnVal = parseGroups;
6317 skipWhitespace(i - startPos);
6318 loop = false;
6319 }
6320 else {
6321 if (inComment) {
6322 if (nextChar === '*' &&
6323 input.charAt(i + 1) === '/') {
6324 i++;
6325 blockDepth--;
6326 inComment = false;
6327 }
6328 i++;
6329 continue;
6330 }
6331 switch (nextChar) {
6332 case '\\':
6333 i++;
6334 nextChar = input.charAt(i);
6335 parseGroups.push(input.substr(lastPos, i - lastPos + 1));
6336 lastPos = i + 1;
6337 break;
6338 case '/':
6339 if (input.charAt(i + 1) === '*') {
6340 i++;
6341 inComment = true;
6342 blockDepth++;
6343 }
6344 break;
6345 case '\'':
6346 case '"':
6347 quote = parserInput.$quoted(i);
6348 if (quote) {
6349 parseGroups.push(input.substr(lastPos, i - lastPos), quote);
6350 i += quote[1].length - 1;
6351 lastPos = i + 1;
6352 }
6353 else {
6354 skipWhitespace(i - startPos);
6355 returnVal = nextChar;
6356 loop = false;
6357 }
6358 break;
6359 case '{':
6360 blockStack.push('}');
6361 blockDepth++;
6362 break;
6363 case '(':
6364 blockStack.push(')');
6365 blockDepth++;
6366 break;
6367 case '[':
6368 blockStack.push(']');
6369 blockDepth++;
6370 break;
6371 case '}':
6372 case ')':
6373 case ']':
6374 var expected = blockStack.pop();
6375 if (nextChar === expected) {
6376 blockDepth--;
6377 }
6378 else {
6379 // move the parser to the error and return expected
6380 skipWhitespace(i - startPos);
6381 returnVal = expected;
6382 loop = false;
6383 }
6384 }
6385 i++;
6386 if (i > length) {
6387 loop = false;
6388 }
6389 }
6390 } while (loop);
6391 return returnVal ? returnVal : null;
6392 };
6393 parserInput.autoCommentAbsorb = true;
6394 parserInput.commentStore = [];
6395 parserInput.finished = false;
6396 // Same as $(), but don't change the state of the parser,
6397 // just return the match.
6398 parserInput.peek = function (tok) {
6399 if (typeof tok === 'string') {
6400 // https://jsperf.com/string-startswith/21
6401 for (var i = 0; i < tok.length; i++) {
6402 if (input.charAt(parserInput.i + i) !== tok.charAt(i)) {
6403 return false;
6404 }
6405 }
6406 return true;
6407 }
6408 else {
6409 return tok.test(current);
6410 }
6411 };
6412 // Specialization of peek()
6413 // TODO remove or change some currentChar calls to peekChar
6414 parserInput.peekChar = function (tok) { return input.charAt(parserInput.i) === tok; };
6415 parserInput.currentChar = function () { return input.charAt(parserInput.i); };
6416 parserInput.prevChar = function () { return input.charAt(parserInput.i - 1); };
6417 parserInput.getInput = function () { return input; };
6418 parserInput.peekNotNumeric = function () {
6419 var c = input.charCodeAt(parserInput.i);
6420 // Is the first char of the dimension 0-9, '.', '+' or '-'
6421 return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
6422 };
6423 parserInput.start = function (str, chunkInput, failFunction) {
6424 input = str;
6425 parserInput.i = j = currentPos = furthest = 0;
6426 // chunking apparently makes things quicker (but my tests indicate
6427 // it might actually make things slower in node at least)
6428 // and it is a non-perfect parse - it can't recognise
6429 // unquoted urls, meaning it can't distinguish comments
6430 // meaning comments with quotes or {}() in them get 'counted'
6431 // and then lead to parse errors.
6432 // In addition if the chunking chunks in the wrong place we might
6433 // not be able to parse a parser statement in one go
6434 // this is officially deprecated but can be switched on via an option
6435 // in the case it causes too much performance issues.
6436 if (chunkInput) {
6437 chunks = chunker(str, failFunction);
6438 }
6439 else {
6440 chunks = [str];
6441 }
6442 current = chunks[0];
6443 skipWhitespace(0);
6444 };
6445 parserInput.end = function () {
6446 var message;
6447 var isFinished = parserInput.i >= input.length;
6448 if (parserInput.i < furthest) {
6449 message = furthestPossibleErrorMessage;
6450 parserInput.i = furthest;
6451 }
6452 return {
6453 isFinished: isFinished,
6454 furthest: parserInput.i,
6455 furthestPossibleErrorMessage: message,
6456 furthestReachedEnd: parserInput.i >= input.length - 1,
6457 furthestChar: input[parserInput.i]
6458 };
6459 };
6460 return parserInput;
6461 });
6462
6463 //
6464 // less.js - parser
6465 //
6466 // A relatively straight-forward predictive parser.
6467 // There is no tokenization/lexing stage, the input is parsed
6468 // in one sweep.
6469 //
6470 // To make the parser fast enough to run in the browser, several
6471 // optimization had to be made:
6472 //
6473 // - Matching and slicing on a huge input is often cause of slowdowns.
6474 // The solution is to chunkify the input into smaller strings.
6475 // The chunks are stored in the `chunks` var,
6476 // `j` holds the current chunk index, and `currentPos` holds
6477 // the index of the current chunk in relation to `input`.
6478 // This gives us an almost 4x speed-up.
6479 //
6480 // - In many cases, we don't need to match individual tokens;
6481 // for example, if a value doesn't hold any variables, operations
6482 // or dynamic references, the parser can effectively 'skip' it,
6483 // treating it as a literal.
6484 // An example would be '1px solid #000' - which evaluates to itself,
6485 // we don't need to know what the individual components are.
6486 // The drawback, of course is that you don't get the benefits of
6487 // syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
6488 // and a smaller speed-up in the code-gen.
6489 //
6490 //
6491 // Token matching is done with the `$` function, which either takes
6492 // a terminal string or regexp, or a non-terminal function to call.
6493 // It also takes care of moving all the indices forwards.
6494 //
6495 var Parser = function Parser(context, imports, fileInfo) {
6496 var parsers;
6497 var parserInput = getParserInput();
6498 function error(msg, type) {
6499 throw new LessError({
6500 index: parserInput.i,
6501 filename: fileInfo.filename,
6502 type: type || 'Syntax',
6503 message: msg
6504 }, imports);
6505 }
6506 function expect(arg, msg) {
6507 // some older browsers return typeof 'function' for RegExp
6508 var result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
6509 if (result) {
6510 return result;
6511 }
6512 error(msg || (typeof arg === 'string'
6513 ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'"
6514 : 'unexpected token'));
6515 }
6516 // Specialization of expect()
6517 function expectChar(arg, msg) {
6518 if (parserInput.$char(arg)) {
6519 return arg;
6520 }
6521 error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'");
6522 }
6523 function getDebugInfo(index) {
6524 var filename = fileInfo.filename;
6525 return {
6526 lineNumber: getLocation(index, parserInput.getInput()).line + 1,
6527 fileName: filename
6528 };
6529 }
6530 /**
6531 * Used after initial parsing to create nodes on the fly
6532 *
6533 * @param {String} str - string to parse
6534 * @param {Array} parseList - array of parsers to run input through e.g. ["value", "important"]
6535 * @param {Number} currentIndex - start number to begin indexing
6536 * @param {Object} fileInfo - fileInfo to attach to created nodes
6537 */
6538 function parseNode(str, parseList, currentIndex, fileInfo, callback) {
6539 var result;
6540 var returnNodes = [];
6541 var parser = parserInput;
6542 try {
6543 parser.start(str, false, function fail(msg, index) {
6544 callback({
6545 message: msg,
6546 index: index + currentIndex
6547 });
6548 });
6549 for (var x = 0, p = void 0, i = void 0; (p = parseList[x]); x++) {
6550 i = parser.i;
6551 result = parsers[p]();
6552 if (result) {
6553 try {
6554 result._index = i + currentIndex;
6555 result._fileInfo = fileInfo;
6556 }
6557 catch (e) { }
6558 returnNodes.push(result);
6559 }
6560 else {
6561 returnNodes.push(null);
6562 }
6563 }
6564 var endInfo = parser.end();
6565 if (endInfo.isFinished) {
6566 callback(null, returnNodes);
6567 }
6568 else {
6569 callback(true, null);
6570 }
6571 }
6572 catch (e) {
6573 throw new LessError({
6574 index: e.index + currentIndex,
6575 message: e.message
6576 }, imports, fileInfo.filename);
6577 }
6578 }
6579 //
6580 // The Parser
6581 //
6582 return {
6583 parserInput: parserInput,
6584 imports: imports,
6585 fileInfo: fileInfo,
6586 parseNode: parseNode,
6587 //
6588 // Parse an input string into an abstract syntax tree,
6589 // @param str A string containing 'less' markup
6590 // @param callback call `callback` when done.
6591 // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
6592 //
6593 parse: function (str, callback, additionalData) {
6594 var root;
6595 var error = null;
6596 var globalVars;
6597 var modifyVars;
6598 var ignored;
6599 var preText = '';
6600 globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + "\n" : '';
6601 modifyVars = (additionalData && additionalData.modifyVars) ? "\n" + Parser.serializeVars(additionalData.modifyVars) : '';
6602 if (context.pluginManager) {
6603 var preProcessors = context.pluginManager.getPreProcessors();
6604 for (var i = 0; i < preProcessors.length; i++) {
6605 str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo });
6606 }
6607 }
6608 if (globalVars || (additionalData && additionalData.banner)) {
6609 preText = ((additionalData && additionalData.banner) ? additionalData.banner : '') + globalVars;
6610 ignored = imports.contentsIgnoredChars;
6611 ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0;
6612 ignored[fileInfo.filename] += preText.length;
6613 }
6614 str = str.replace(/\r\n?/g, '\n');
6615 // Remove potential UTF Byte Order Mark
6616 str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
6617 imports.contents[fileInfo.filename] = str;
6618 // Start with the primary rule.
6619 // The whole syntax tree is held under a Ruleset node,
6620 // with the `root` property set to true, so no `{}` are
6621 // output. The callback is called when the input is parsed.
6622 try {
6623 parserInput.start(str, context.chunkInput, function fail(msg, index) {
6624 throw new LessError({
6625 index: index,
6626 type: 'Parse',
6627 message: msg,
6628 filename: fileInfo.filename
6629 }, imports);
6630 });
6631 tree.Node.prototype.parse = this;
6632 root = new tree.Ruleset(null, this.parsers.primary());
6633 tree.Node.prototype.rootNode = root;
6634 root.root = true;
6635 root.firstRoot = true;
6636 root.functionRegistry = functionRegistry.inherit();
6637 }
6638 catch (e) {
6639 return callback(new LessError(e, imports, fileInfo.filename));
6640 }
6641 // If `i` is smaller than the `input.length - 1`,
6642 // it means the parser wasn't able to parse the whole
6643 // string, so we've got a parsing error.
6644 //
6645 // We try to extract a \n delimited string,
6646 // showing the line where the parse error occurred.
6647 // We split it up into two parts (the part which parsed,
6648 // and the part which didn't), so we can color them differently.
6649 var endInfo = parserInput.end();
6650 if (!endInfo.isFinished) {
6651 var message = endInfo.furthestPossibleErrorMessage;
6652 if (!message) {
6653 message = 'Unrecognised input';
6654 if (endInfo.furthestChar === '}') {
6655 message += '. Possibly missing opening \'{\'';
6656 }
6657 else if (endInfo.furthestChar === ')') {
6658 message += '. Possibly missing opening \'(\'';
6659 }
6660 else if (endInfo.furthestReachedEnd) {
6661 message += '. Possibly missing something';
6662 }
6663 }
6664 error = new LessError({
6665 type: 'Parse',
6666 message: message,
6667 index: endInfo.furthest,
6668 filename: fileInfo.filename
6669 }, imports);
6670 }
6671 var finish = function (e) {
6672 e = error || e || imports.error;
6673 if (e) {
6674 if (!(e instanceof LessError)) {
6675 e = new LessError(e, imports, fileInfo.filename);
6676 }
6677 return callback(e);
6678 }
6679 else {
6680 return callback(null, root);
6681 }
6682 };
6683 if (context.processImports !== false) {
6684 new visitors.ImportVisitor(imports, finish)
6685 .run(root);
6686 }
6687 else {
6688 return finish();
6689 }
6690 },
6691 //
6692 // Here in, the parsing rules/functions
6693 //
6694 // The basic structure of the syntax tree generated is as follows:
6695 //
6696 // Ruleset -> Declaration -> Value -> Expression -> Entity
6697 //
6698 // Here's some Less code:
6699 //
6700 // .class {
6701 // color: #fff;
6702 // border: 1px solid #000;
6703 // width: @w + 4px;
6704 // > .child {...}
6705 // }
6706 //
6707 // And here's what the parse tree might look like:
6708 //
6709 // Ruleset (Selector '.class', [
6710 // Declaration ("color", Value ([Expression [Color #fff]]))
6711 // Declaration ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
6712 // Declaration ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
6713 // Ruleset (Selector [Element '>', '.child'], [...])
6714 // ])
6715 //
6716 // In general, most rules will try to parse a token with the `$re()` function, and if the return
6717 // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
6718 // first, before parsing, that's when we use `peek()`.
6719 //
6720 parsers: parsers = {
6721 //
6722 // The `primary` rule is the *entry* and *exit* point of the parser.
6723 // The rules here can appear at any level of the parse tree.
6724 //
6725 // The recursive nature of the grammar is an interplay between the `block`
6726 // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
6727 // as represented by this simplified grammar:
6728 //
6729 // primary → (ruleset | declaration)+
6730 // ruleset → selector+ block
6731 // block → '{' primary '}'
6732 //
6733 // Only at one point is the primary rule not called from the
6734 // block rule: at the root level.
6735 //
6736 primary: function () {
6737 var mixin = this.mixin;
6738 var root = [];
6739 var node;
6740 while (true) {
6741 while (true) {
6742 node = this.comment();
6743 if (!node) {
6744 break;
6745 }
6746 root.push(node);
6747 }
6748 // always process comments before deciding if finished
6749 if (parserInput.finished) {
6750 break;
6751 }
6752 if (parserInput.peek('}')) {
6753 break;
6754 }
6755 node = this.extendRule();
6756 if (node) {
6757 root = root.concat(node);
6758 continue;
6759 }
6760 node = mixin.definition() || this.declaration() || mixin.call(false, false) ||
6761 this.ruleset() || this.variableCall() || this.entities.call() || this.atrule();
6762 if (node) {
6763 root.push(node);
6764 }
6765 else {
6766 var foundSemiColon = false;
6767 while (parserInput.$char(';')) {
6768 foundSemiColon = true;
6769 }
6770 if (!foundSemiColon) {
6771 break;
6772 }
6773 }
6774 }
6775 return root;
6776 },
6777 // comments are collected by the main parsing mechanism and then assigned to nodes
6778 // where the current structure allows it
6779 comment: function () {
6780 if (parserInput.commentStore.length) {
6781 var comment = parserInput.commentStore.shift();
6782 return new (tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo);
6783 }
6784 },
6785 //
6786 // Entities are tokens which can be found inside an Expression
6787 //
6788 entities: {
6789 mixinLookup: function () {
6790 return parsers.mixin.call(true, true);
6791 },
6792 //
6793 // A string, which supports escaping " and '
6794 //
6795 // "milky way" 'he\'s the one!'
6796 //
6797 quoted: function (forceEscaped) {
6798 var str;
6799 var index = parserInput.i;
6800 var isEscaped = false;
6801 parserInput.save();
6802 if (parserInput.$char('~')) {
6803 isEscaped = true;
6804 }
6805 else if (forceEscaped) {
6806 parserInput.restore();
6807 return;
6808 }
6809 str = parserInput.$quoted();
6810 if (!str) {
6811 parserInput.restore();
6812 return;
6813 }
6814 parserInput.forget();
6815 return new (tree.Quoted)(str.charAt(0), str.substr(1, str.length - 2), isEscaped, index, fileInfo);
6816 },
6817 //
6818 // A catch-all word, such as:
6819 //
6820 // black border-collapse
6821 //
6822 keyword: function () {
6823 var k = parserInput.$char('%') || parserInput.$re(/^\[?(?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+\]?/);
6824 if (k) {
6825 return tree.Color.fromKeyword(k) || new (tree.Keyword)(k);
6826 }
6827 },
6828 //
6829 // A function call
6830 //
6831 // rgb(255, 0, 255)
6832 //
6833 // The arguments are parsed with the `entities.arguments` parser.
6834 //
6835 call: function () {
6836 var name;
6837 var args;
6838 var func;
6839 var index = parserInput.i;
6840 // http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
6841 if (parserInput.peek(/^url\(/i)) {
6842 return;
6843 }
6844 parserInput.save();
6845 name = parserInput.$re(/^([\w-]+|%|~|progid:[\w\.]+)\(/);
6846 if (!name) {
6847 parserInput.forget();
6848 return;
6849 }
6850 name = name[1];
6851 func = this.customFuncCall(name);
6852 if (func) {
6853 args = func.parse();
6854 if (args && func.stop) {
6855 parserInput.forget();
6856 return args;
6857 }
6858 }
6859 args = this.arguments(args);
6860 if (!parserInput.$char(')')) {
6861 parserInput.restore('Could not parse call arguments or missing \')\'');
6862 return;
6863 }
6864 parserInput.forget();
6865 return new (tree.Call)(name, args, index, fileInfo);
6866 },
6867 //
6868 // Parsing rules for functions with non-standard args, e.g.:
6869 //
6870 // boolean(not(2 > 1))
6871 //
6872 // This is a quick prototype, to be modified/improved when
6873 // more custom-parsed funcs come (e.g. `selector(...)`)
6874 //
6875 customFuncCall: function (name) {
6876 /* Ideally the table is to be moved out of here for faster perf.,
6877 but it's quite tricky since it relies on all these `parsers`
6878 and `expect` available only here */
6879 return {
6880 alpha: f(parsers.ieAlpha, true),
6881 boolean: f(condition),
6882 'if': f(condition)
6883 }[name.toLowerCase()];
6884 function f(parse, stop) {
6885 return {
6886 parse: parse,
6887 stop: stop // when true - stop after parse() and return its result,
6888 // otherwise continue for plain args
6889 };
6890 }
6891 function condition() {
6892 return [expect(parsers.condition, 'expected condition')];
6893 }
6894 },
6895 arguments: function (prevArgs) {
6896 var argsComma = prevArgs || [];
6897 var argsSemiColon = [];
6898 var isSemiColonSeparated;
6899 var value;
6900 parserInput.save();
6901 while (true) {
6902 if (prevArgs) {
6903 prevArgs = false;
6904 }
6905 else {
6906 value = parsers.detachedRuleset() || this.assignment() || parsers.expression();
6907 if (!value) {
6908 break;
6909 }
6910 if (value.value && value.value.length == 1) {
6911 value = value.value[0];
6912 }
6913 argsComma.push(value);
6914 }
6915 if (parserInput.$char(',')) {
6916 continue;
6917 }
6918 if (parserInput.$char(';') || isSemiColonSeparated) {
6919 isSemiColonSeparated = true;
6920 value = (argsComma.length < 1) ? argsComma[0]
6921 : new tree.Value(argsComma);
6922 argsSemiColon.push(value);
6923 argsComma = [];
6924 }
6925 }
6926 parserInput.forget();
6927 return isSemiColonSeparated ? argsSemiColon : argsComma;
6928 },
6929 literal: function () {
6930 return this.dimension() ||
6931 this.color() ||
6932 this.quoted() ||
6933 this.unicodeDescriptor();
6934 },
6935 // Assignments are argument entities for calls.
6936 // They are present in ie filter properties as shown below.
6937 //
6938 // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
6939 //
6940 assignment: function () {
6941 var key;
6942 var value;
6943 parserInput.save();
6944 key = parserInput.$re(/^\w+(?=\s?=)/i);
6945 if (!key) {
6946 parserInput.restore();
6947 return;
6948 }
6949 if (!parserInput.$char('=')) {
6950 parserInput.restore();
6951 return;
6952 }
6953 value = parsers.entity();
6954 if (value) {
6955 parserInput.forget();
6956 return new (tree.Assignment)(key, value);
6957 }
6958 else {
6959 parserInput.restore();
6960 }
6961 },
6962 //
6963 // Parse url() tokens
6964 //
6965 // We use a specific rule for urls, because they don't really behave like
6966 // standard function calls. The difference is that the argument doesn't have
6967 // to be enclosed within a string, so it can't be parsed as an Expression.
6968 //
6969 url: function () {
6970 var value;
6971 var index = parserInput.i;
6972 parserInput.autoCommentAbsorb = false;
6973 if (!parserInput.$str('url(')) {
6974 parserInput.autoCommentAbsorb = true;
6975 return;
6976 }
6977 value = this.quoted() || this.variable() || this.property() ||
6978 parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || '';
6979 parserInput.autoCommentAbsorb = true;
6980 expectChar(')');
6981 return new (tree.URL)((value.value != null ||
6982 value instanceof tree.Variable ||
6983 value instanceof tree.Property) ?
6984 value : new (tree.Anonymous)(value, index), index, fileInfo);
6985 },
6986 //
6987 // A Variable entity, such as `@fink`, in
6988 //
6989 // width: @fink + 2px
6990 //
6991 // We use a different parser for variable definitions,
6992 // see `parsers.variable`.
6993 //
6994 variable: function () {
6995 var ch;
6996 var name;
6997 var index = parserInput.i;
6998 parserInput.save();
6999 if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
7000 ch = parserInput.currentChar();
7001 if (ch === '(' || ch === '[' && !parserInput.prevChar().match(/^\s/)) {
7002 // this may be a VariableCall lookup
7003 var result = parsers.variableCall(name);
7004 if (result) {
7005 parserInput.forget();
7006 return result;
7007 }
7008 }
7009 parserInput.forget();
7010 return new (tree.Variable)(name, index, fileInfo);
7011 }
7012 parserInput.restore();
7013 },
7014 // A variable entity using the protective {} e.g. @{var}
7015 variableCurly: function () {
7016 var curly;
7017 var index = parserInput.i;
7018 if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
7019 return new (tree.Variable)("@" + curly[1], index, fileInfo);
7020 }
7021 },
7022 //
7023 // A Property accessor, such as `$color`, in
7024 //
7025 // background-color: $color
7026 //
7027 property: function () {
7028 var name;
7029 var index = parserInput.i;
7030 if (parserInput.currentChar() === '$' && (name = parserInput.$re(/^\$[\w-]+/))) {
7031 return new (tree.Property)(name, index, fileInfo);
7032 }
7033 },
7034 // A property entity useing the protective {} e.g. ${prop}
7035 propertyCurly: function () {
7036 var curly;
7037 var index = parserInput.i;
7038 if (parserInput.currentChar() === '$' && (curly = parserInput.$re(/^\$\{([\w-]+)\}/))) {
7039 return new (tree.Property)("$" + curly[1], index, fileInfo);
7040 }
7041 },
7042 //
7043 // A Hexadecimal color
7044 //
7045 // #4F3C2F
7046 //
7047 // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
7048 //
7049 color: function () {
7050 var rgb;
7051 parserInput.save();
7052 if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})([\w.#\[])?/))) {
7053 if (!rgb[2]) {
7054 parserInput.forget();
7055 return new (tree.Color)(rgb[1], undefined, rgb[0]);
7056 }
7057 }
7058 parserInput.restore();
7059 },
7060 colorKeyword: function () {
7061 parserInput.save();
7062 var autoCommentAbsorb = parserInput.autoCommentAbsorb;
7063 parserInput.autoCommentAbsorb = false;
7064 var k = parserInput.$re(/^[_A-Za-z-][_A-Za-z0-9-]+/);
7065 parserInput.autoCommentAbsorb = autoCommentAbsorb;
7066 if (!k) {
7067 parserInput.forget();
7068 return;
7069 }
7070 parserInput.restore();
7071 var color = tree.Color.fromKeyword(k);
7072 if (color) {
7073 parserInput.$str(k);
7074 return color;
7075 }
7076 },
7077 //
7078 // A Dimension, that is, a number and a unit
7079 //
7080 // 0.5em 95%
7081 //
7082 dimension: function () {
7083 if (parserInput.peekNotNumeric()) {
7084 return;
7085 }
7086 var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z_]+)?/i);
7087 if (value) {
7088 return new (tree.Dimension)(value[1], value[2]);
7089 }
7090 },
7091 //
7092 // A unicode descriptor, as is used in unicode-range
7093 //
7094 // U+0?? or U+00A1-00A9
7095 //
7096 unicodeDescriptor: function () {
7097 var ud;
7098 ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
7099 if (ud) {
7100 return new (tree.UnicodeDescriptor)(ud[0]);
7101 }
7102 },
7103 //
7104 // JavaScript code to be evaluated
7105 //
7106 // `window.location.href`
7107 //
7108 javascript: function () {
7109 var js;
7110 var index = parserInput.i;
7111 parserInput.save();
7112 var escape = parserInput.$char('~');
7113 var jsQuote = parserInput.$char('`');
7114 if (!jsQuote) {
7115 parserInput.restore();
7116 return;
7117 }
7118 js = parserInput.$re(/^[^`]*`/);
7119 if (js) {
7120 parserInput.forget();
7121 return new (tree.JavaScript)(js.substr(0, js.length - 1), Boolean(escape), index, fileInfo);
7122 }
7123 parserInput.restore('invalid javascript definition');
7124 }
7125 },
7126 //
7127 // The variable part of a variable definition. Used in the `rule` parser
7128 //
7129 // @fink:
7130 //
7131 variable: function () {
7132 var name;
7133 if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) {
7134 return name[1];
7135 }
7136 },
7137 //
7138 // Call a variable value to retrieve a detached ruleset
7139 // or a value from a detached ruleset's rules.
7140 //
7141 // @fink();
7142 // @fink;
7143 // color: @fink[@color];
7144 //
7145 variableCall: function (parsedName) {
7146 var lookups;
7147 var i = parserInput.i;
7148 var inValue = !!parsedName;
7149 var name = parsedName;
7150 parserInput.save();
7151 if (name || (parserInput.currentChar() === '@'
7152 && (name = parserInput.$re(/^(@[\w-]+)(\(\s*\))?/)))) {
7153 lookups = this.mixin.ruleLookups();
7154 if (!lookups && ((inValue && parserInput.$str('()') !== '()') || (name[2] !== '()'))) {
7155 parserInput.restore('Missing \'[...]\' lookup in variable call');
7156 return;
7157 }
7158 if (!inValue) {
7159 name = name[1];
7160 }
7161 var call = new tree.VariableCall(name, i, fileInfo);
7162 if (!inValue && parsers.end()) {
7163 parserInput.forget();
7164 return call;
7165 }
7166 else {
7167 parserInput.forget();
7168 return new tree.NamespaceValue(call, lookups, i, fileInfo);
7169 }
7170 }
7171 parserInput.restore();
7172 },
7173 //
7174 // extend syntax - used to extend selectors
7175 //
7176 extend: function (isRule) {
7177 var elements;
7178 var e;
7179 var index = parserInput.i;
7180 var option;
7181 var extendList;
7182 var extend;
7183 if (!parserInput.$str(isRule ? '&:extend(' : ':extend(')) {
7184 return;
7185 }
7186 do {
7187 option = null;
7188 elements = null;
7189 while (!(option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
7190 e = this.element();
7191 if (!e) {
7192 break;
7193 }
7194 if (elements) {
7195 elements.push(e);
7196 }
7197 else {
7198 elements = [e];
7199 }
7200 }
7201 option = option && option[1];
7202 if (!elements) {
7203 error('Missing target selector for :extend().');
7204 }
7205 extend = new (tree.Extend)(new (tree.Selector)(elements), option, index, fileInfo);
7206 if (extendList) {
7207 extendList.push(extend);
7208 }
7209 else {
7210 extendList = [extend];
7211 }
7212 } while (parserInput.$char(','));
7213 expect(/^\)/);
7214 if (isRule) {
7215 expect(/^;/);
7216 }
7217 return extendList;
7218 },
7219 //
7220 // extendRule - used in a rule to extend all the parent selectors
7221 //
7222 extendRule: function () {
7223 return this.extend(true);
7224 },
7225 //
7226 // Mixins
7227 //
7228 mixin: {
7229 //
7230 // A Mixin call, with an optional argument list
7231 //
7232 // #mixins > .square(#fff);
7233 // #mixins.square(#fff);
7234 // .rounded(4px, black);
7235 // .button;
7236 //
7237 // We can lookup / return a value using the lookup syntax:
7238 //
7239 // color: #mixin.square(#fff)[@color];
7240 //
7241 // The `while` loop is there because mixins can be
7242 // namespaced, but we only support the child and descendant
7243 // selector for now.
7244 //
7245 call: function (inValue, getLookup) {
7246 var s = parserInput.currentChar();
7247 var important = false;
7248 var lookups;
7249 var index = parserInput.i;
7250 var elements;
7251 var args;
7252 var hasParens;
7253 if (s !== '.' && s !== '#') {
7254 return;
7255 }
7256 parserInput.save(); // stop us absorbing part of an invalid selector
7257 elements = this.elements();
7258 if (elements) {
7259 if (parserInput.$char('(')) {
7260 args = this.args(true).args;
7261 expectChar(')');
7262 hasParens = true;
7263 }
7264 if (getLookup !== false) {
7265 lookups = this.ruleLookups();
7266 }
7267 if (getLookup === true && !lookups) {
7268 parserInput.restore();
7269 return;
7270 }
7271 if (inValue && !lookups && !hasParens) {
7272 // This isn't a valid in-value mixin call
7273 parserInput.restore();
7274 return;
7275 }
7276 if (!inValue && parsers.important()) {
7277 important = true;
7278 }
7279 if (inValue || parsers.end()) {
7280 parserInput.forget();
7281 var mixin = new (tree.mixin.Call)(elements, args, index, fileInfo, !lookups && important);
7282 if (lookups) {
7283 return new tree.NamespaceValue(mixin, lookups);
7284 }
7285 else {
7286 return mixin;
7287 }
7288 }
7289 }
7290 parserInput.restore();
7291 },
7292 /**
7293 * Matching elements for mixins
7294 * (Start with . or # and can have > )
7295 */
7296 elements: function () {
7297 var elements;
7298 var e;
7299 var c;
7300 var elem;
7301 var elemIndex;
7302 var re = /^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/;
7303 while (true) {
7304 elemIndex = parserInput.i;
7305 e = parserInput.$re(re);
7306 if (!e) {
7307 break;
7308 }
7309 elem = new (tree.Element)(c, e, false, elemIndex, fileInfo);
7310 if (elements) {
7311 elements.push(elem);
7312 }
7313 else {
7314 elements = [elem];
7315 }
7316 c = parserInput.$char('>');
7317 }
7318 return elements;
7319 },
7320 args: function (isCall) {
7321 var entities = parsers.entities;
7322 var returner = { args: null, variadic: false };
7323 var expressions = [];
7324 var argsSemiColon = [];
7325 var argsComma = [];
7326 var isSemiColonSeparated;
7327 var expressionContainsNamed;
7328 var name;
7329 var nameLoop;
7330 var value;
7331 var arg;
7332 var expand;
7333 var hasSep = true;
7334 parserInput.save();
7335 while (true) {
7336 if (isCall) {
7337 arg = parsers.detachedRuleset() || parsers.expression();
7338 }
7339 else {
7340 parserInput.commentStore.length = 0;
7341 if (parserInput.$str('...')) {
7342 returner.variadic = true;
7343 if (parserInput.$char(';') && !isSemiColonSeparated) {
7344 isSemiColonSeparated = true;
7345 }
7346 (isSemiColonSeparated ? argsSemiColon : argsComma)
7347 .push({ variadic: true });
7348 break;
7349 }
7350 arg = entities.variable() || entities.property() || entities.literal() || entities.keyword() || this.call(true);
7351 }
7352 if (!arg || !hasSep) {
7353 break;
7354 }
7355 nameLoop = null;
7356 if (arg.throwAwayComments) {
7357 arg.throwAwayComments();
7358 }
7359 value = arg;
7360 var val = null;
7361 if (isCall) {
7362 // Variable
7363 if (arg.value && arg.value.length == 1) {
7364 val = arg.value[0];
7365 }
7366 }
7367 else {
7368 val = arg;
7369 }
7370 if (val && (val instanceof tree.Variable || val instanceof tree.Property)) {
7371 if (parserInput.$char(':')) {
7372 if (expressions.length > 0) {
7373 if (isSemiColonSeparated) {
7374 error('Cannot mix ; and , as delimiter types');
7375 }
7376 expressionContainsNamed = true;
7377 }
7378 value = parsers.detachedRuleset() || parsers.expression();
7379 if (!value) {
7380 if (isCall) {
7381 error('could not understand value for named argument');
7382 }
7383 else {
7384 parserInput.restore();
7385 returner.args = [];
7386 return returner;
7387 }
7388 }
7389 nameLoop = (name = val.name);
7390 }
7391 else if (parserInput.$str('...')) {
7392 if (!isCall) {
7393 returner.variadic = true;
7394 if (parserInput.$char(';') && !isSemiColonSeparated) {
7395 isSemiColonSeparated = true;
7396 }
7397 (isSemiColonSeparated ? argsSemiColon : argsComma)
7398 .push({ name: arg.name, variadic: true });
7399 break;
7400 }
7401 else {
7402 expand = true;
7403 }
7404 }
7405 else if (!isCall) {
7406 name = nameLoop = val.name;
7407 value = null;
7408 }
7409 }
7410 if (value) {
7411 expressions.push(value);
7412 }
7413 argsComma.push({ name: nameLoop, value: value, expand: expand });
7414 if (parserInput.$char(',')) {
7415 hasSep = true;
7416 continue;
7417 }
7418 hasSep = parserInput.$char(';') === ';';
7419 if (hasSep || isSemiColonSeparated) {
7420 if (expressionContainsNamed) {
7421 error('Cannot mix ; and , as delimiter types');
7422 }
7423 isSemiColonSeparated = true;
7424 if (expressions.length > 1) {
7425 value = new (tree.Value)(expressions);
7426 }
7427 argsSemiColon.push({ name: name, value: value, expand: expand });
7428 name = null;
7429 expressions = [];
7430 expressionContainsNamed = false;
7431 }
7432 }
7433 parserInput.forget();
7434 returner.args = isSemiColonSeparated ? argsSemiColon : argsComma;
7435 return returner;
7436 },
7437 //
7438 // A Mixin definition, with a list of parameters
7439 //
7440 // .rounded (@radius: 2px, @color) {
7441 // ...
7442 // }
7443 //
7444 // Until we have a finer grained state-machine, we have to
7445 // do a look-ahead, to make sure we don't have a mixin call.
7446 // See the `rule` function for more information.
7447 //
7448 // We start by matching `.rounded (`, and then proceed on to
7449 // the argument list, which has optional default values.
7450 // We store the parameters in `params`, with a `value` key,
7451 // if there is a value, such as in the case of `@radius`.
7452 //
7453 // Once we've got our params list, and a closing `)`, we parse
7454 // the `{...}` block.
7455 //
7456 definition: function () {
7457 var name;
7458 var params = [];
7459 var match;
7460 var ruleset;
7461 var cond;
7462 var variadic = false;
7463 if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
7464 parserInput.peek(/^[^{]*\}/)) {
7465 return;
7466 }
7467 parserInput.save();
7468 match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
7469 if (match) {
7470 name = match[1];
7471 var argInfo = this.args(false);
7472 params = argInfo.args;
7473 variadic = argInfo.variadic;
7474 // .mixincall("@{a}");
7475 // looks a bit like a mixin definition..
7476 // also
7477 // .mixincall(@a: {rule: set;});
7478 // so we have to be nice and restore
7479 if (!parserInput.$char(')')) {
7480 parserInput.restore('Missing closing \')\'');
7481 return;
7482 }
7483 parserInput.commentStore.length = 0;
7484 if (parserInput.$str('when')) { // Guard
7485 cond = expect(parsers.conditions, 'expected condition');
7486 }
7487 ruleset = parsers.block();
7488 if (ruleset) {
7489 parserInput.forget();
7490 return new (tree.mixin.Definition)(name, params, ruleset, cond, variadic);
7491 }
7492 else {
7493 parserInput.restore();
7494 }
7495 }
7496 else {
7497 parserInput.restore();
7498 }
7499 },
7500 ruleLookups: function () {
7501 var rule;
7502 var lookups = [];
7503 if (parserInput.currentChar() !== '[') {
7504 return;
7505 }
7506 while (true) {
7507 parserInput.save();
7508 rule = this.lookupValue();
7509 if (!rule && rule !== '') {
7510 parserInput.restore();
7511 break;
7512 }
7513 lookups.push(rule);
7514 parserInput.forget();
7515 }
7516 if (lookups.length > 0) {
7517 return lookups;
7518 }
7519 },
7520 lookupValue: function () {
7521 parserInput.save();
7522 if (!parserInput.$char('[')) {
7523 parserInput.restore();
7524 return;
7525 }
7526 var name = parserInput.$re(/^(?:[@$]{0,2})[_a-zA-Z0-9-]*/);
7527 if (!parserInput.$char(']')) {
7528 parserInput.restore();
7529 return;
7530 }
7531 if (name || name === '') {
7532 parserInput.forget();
7533 return name;
7534 }
7535 parserInput.restore();
7536 }
7537 },
7538 //
7539 // Entities are the smallest recognized token,
7540 // and can be found inside a rule's value.
7541 //
7542 entity: function () {
7543 var entities = this.entities;
7544 return this.comment() || entities.literal() || entities.variable() || entities.url() ||
7545 entities.property() || entities.call() || entities.keyword() || this.mixin.call(true) ||
7546 entities.javascript();
7547 },
7548 //
7549 // A Declaration terminator. Note that we use `peek()` to check for '}',
7550 // because the `block` rule will be expecting it, but we still need to make sure
7551 // it's there, if ';' was omitted.
7552 //
7553 end: function () {
7554 return parserInput.$char(';') || parserInput.peek('}');
7555 },
7556 //
7557 // IE's alpha function
7558 //
7559 // alpha(opacity=88)
7560 //
7561 ieAlpha: function () {
7562 var value;
7563 // http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
7564 if (!parserInput.$re(/^opacity=/i)) {
7565 return;
7566 }
7567 value = parserInput.$re(/^\d+/);
7568 if (!value) {
7569 value = expect(parsers.entities.variable, 'Could not parse alpha');
7570 value = "@{" + value.name.slice(1) + "}";
7571 }
7572 expectChar(')');
7573 return new tree.Quoted('', "alpha(opacity=" + value + ")");
7574 },
7575 //
7576 // A Selector Element
7577 //
7578 // div
7579 // + h1
7580 // #socks
7581 // input[type="text"]
7582 //
7583 // Elements are the building blocks for Selectors,
7584 // they are made out of a `Combinator` (see combinator rule),
7585 // and an element name, such as a tag a class, or `*`.
7586 //
7587 element: function () {
7588 var e;
7589 var c;
7590 var v;
7591 var index = parserInput.i;
7592 c = this.combinator();
7593 e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
7594 parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
7595 parserInput.$char('*') || parserInput.$char('&') || this.attribute() ||
7596 parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
7597 this.entities.variableCurly();
7598 if (!e) {
7599 parserInput.save();
7600 if (parserInput.$char('(')) {
7601 if ((v = this.selector(false)) && parserInput.$char(')')) {
7602 e = new (tree.Paren)(v);
7603 parserInput.forget();
7604 }
7605 else {
7606 parserInput.restore('Missing closing \')\'');
7607 }
7608 }
7609 else {
7610 parserInput.forget();
7611 }
7612 }
7613 if (e) {
7614 return new (tree.Element)(c, e, e instanceof tree.Variable, index, fileInfo);
7615 }
7616 },
7617 //
7618 // Combinators combine elements together, in a Selector.
7619 //
7620 // Because our parser isn't white-space sensitive, special care
7621 // has to be taken, when parsing the descendant combinator, ` `,
7622 // as it's an empty space. We have to check the previous character
7623 // in the input, to see if it's a ` ` character. More info on how
7624 // we deal with this in *combinator.js*.
7625 //
7626 combinator: function () {
7627 var c = parserInput.currentChar();
7628 if (c === '/') {
7629 parserInput.save();
7630 var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i);
7631 if (slashedCombinator) {
7632 parserInput.forget();
7633 return new (tree.Combinator)(slashedCombinator);
7634 }
7635 parserInput.restore();
7636 }
7637 if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
7638 parserInput.i++;
7639 if (c === '^' && parserInput.currentChar() === '^') {
7640 c = '^^';
7641 parserInput.i++;
7642 }
7643 while (parserInput.isWhitespace()) {
7644 parserInput.i++;
7645 }
7646 return new (tree.Combinator)(c);
7647 }
7648 else if (parserInput.isWhitespace(-1)) {
7649 return new (tree.Combinator)(' ');
7650 }
7651 else {
7652 return new (tree.Combinator)(null);
7653 }
7654 },
7655 //
7656 // A CSS Selector
7657 // with less extensions e.g. the ability to extend and guard
7658 //
7659 // .class > div + h1
7660 // li a:hover
7661 //
7662 // Selectors are made out of one or more Elements, see above.
7663 //
7664 selector: function (isLess) {
7665 var index = parserInput.i;
7666 var elements;
7667 var extendList;
7668 var c;
7669 var e;
7670 var allExtends;
7671 var when;
7672 var condition;
7673 isLess = isLess !== false;
7674 while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$str('when'))) || (e = this.element())) {
7675 if (when) {
7676 condition = expect(this.conditions, 'expected condition');
7677 }
7678 else if (condition) {
7679 error('CSS guard can only be used at the end of selector');
7680 }
7681 else if (extendList) {
7682 if (allExtends) {
7683 allExtends = allExtends.concat(extendList);
7684 }
7685 else {
7686 allExtends = extendList;
7687 }
7688 }
7689 else {
7690 if (allExtends) {
7691 error('Extend can only be used at the end of selector');
7692 }
7693 c = parserInput.currentChar();
7694 if (elements) {
7695 elements.push(e);
7696 }
7697 else {
7698 elements = [e];
7699 }
7700 e = null;
7701 }
7702 if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
7703 break;
7704 }
7705 }
7706 if (elements) {
7707 return new (tree.Selector)(elements, allExtends, condition, index, fileInfo);
7708 }
7709 if (allExtends) {
7710 error('Extend must be used to extend a selector, it cannot be used on its own');
7711 }
7712 },
7713 selectors: function () {
7714 var s;
7715 var selectors;
7716 while (true) {
7717 s = this.selector();
7718 if (!s) {
7719 break;
7720 }
7721 if (selectors) {
7722 selectors.push(s);
7723 }
7724 else {
7725 selectors = [s];
7726 }
7727 parserInput.commentStore.length = 0;
7728 if (s.condition && selectors.length > 1) {
7729 error("Guards are only currently allowed on a single selector.");
7730 }
7731 if (!parserInput.$char(',')) {
7732 break;
7733 }
7734 if (s.condition) {
7735 error("Guards are only currently allowed on a single selector.");
7736 }
7737 parserInput.commentStore.length = 0;
7738 }
7739 return selectors;
7740 },
7741 attribute: function () {
7742 if (!parserInput.$char('[')) {
7743 return;
7744 }
7745 var entities = this.entities;
7746 var key;
7747 var val;
7748 var op;
7749 if (!(key = entities.variableCurly())) {
7750 key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
7751 }
7752 op = parserInput.$re(/^[|~*$^]?=/);
7753 if (op) {
7754 val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly();
7755 }
7756 expectChar(']');
7757 return new (tree.Attribute)(key, op, val);
7758 },
7759 //
7760 // The `block` rule is used by `ruleset` and `mixin.definition`.
7761 // It's a wrapper around the `primary` rule, with added `{}`.
7762 //
7763 block: function () {
7764 var content;
7765 if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) {
7766 return content;
7767 }
7768 },
7769 blockRuleset: function () {
7770 var block = this.block();
7771 if (block) {
7772 block = new tree.Ruleset(null, block);
7773 }
7774 return block;
7775 },
7776 detachedRuleset: function () {
7777 var argInfo;
7778 var params;
7779 var variadic;
7780 parserInput.save();
7781 if (parserInput.$re(/^[.#]\(/)) {
7782 /**
7783 * DR args currently only implemented for each() function, and not
7784 * yet settable as `@dr: #(@arg) {}`
7785 * This should be done when DRs are merged with mixins.
7786 * See: https://github.com/less/less-meta/issues/16
7787 */
7788 argInfo = this.mixin.args(false);
7789 params = argInfo.args;
7790 variadic = argInfo.variadic;
7791 if (!parserInput.$char(')')) {
7792 parserInput.restore();
7793 return;
7794 }
7795 }
7796 var blockRuleset = this.blockRuleset();
7797 if (blockRuleset) {
7798 parserInput.forget();
7799 if (params) {
7800 return new tree.mixin.Definition(null, params, blockRuleset, null, variadic);
7801 }
7802 return new tree.DetachedRuleset(blockRuleset);
7803 }
7804 parserInput.restore();
7805 },
7806 //
7807 // div, .class, body > p {...}
7808 //
7809 ruleset: function () {
7810 var selectors;
7811 var rules;
7812 var debugInfo;
7813 parserInput.save();
7814 if (context.dumpLineNumbers) {
7815 debugInfo = getDebugInfo(parserInput.i);
7816 }
7817 selectors = this.selectors();
7818 if (selectors && (rules = this.block())) {
7819 parserInput.forget();
7820 var ruleset = new (tree.Ruleset)(selectors, rules, context.strictImports);
7821 if (context.dumpLineNumbers) {
7822 ruleset.debugInfo = debugInfo;
7823 }
7824 return ruleset;
7825 }
7826 else {
7827 parserInput.restore();
7828 }
7829 },
7830 declaration: function () {
7831 var name;
7832 var value;
7833 var index = parserInput.i;
7834 var hasDR;
7835 var c = parserInput.currentChar();
7836 var important;
7837 var merge;
7838 var isVariable;
7839 if (c === '.' || c === '#' || c === '&' || c === ':') {
7840 return;
7841 }
7842 parserInput.save();
7843 name = this.variable() || this.ruleProperty();
7844 if (name) {
7845 isVariable = typeof name === 'string';
7846 if (isVariable) {
7847 value = this.detachedRuleset();
7848 if (value) {
7849 hasDR = true;
7850 }
7851 }
7852 parserInput.commentStore.length = 0;
7853 if (!value) {
7854 // a name returned by this.ruleProperty() is always an array of the form:
7855 // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
7856 // where each item is a tree.Keyword or tree.Variable
7857 merge = !isVariable && name.length > 1 && name.pop().value;
7858 // Custom property values get permissive parsing
7859 if (name[0].value && name[0].value.slice(0, 2) === '--') {
7860 value = this.permissiveValue();
7861 }
7862 // Try to store values as anonymous
7863 // If we need the value later we'll re-parse it in ruleset.parseValue
7864 else {
7865 value = this.anonymousValue();
7866 }
7867 if (value) {
7868 parserInput.forget();
7869 // anonymous values absorb the end ';' which is required for them to work
7870 return new (tree.Declaration)(name, value, false, merge, index, fileInfo);
7871 }
7872 if (!value) {
7873 value = this.value();
7874 }
7875 if (value) {
7876 important = this.important();
7877 }
7878 else if (isVariable) {
7879 // As a last resort, try permissiveValue
7880 value = this.permissiveValue();
7881 }
7882 }
7883 if (value && (this.end() || hasDR)) {
7884 parserInput.forget();
7885 return new (tree.Declaration)(name, value, important, merge, index, fileInfo);
7886 }
7887 else {
7888 parserInput.restore();
7889 }
7890 }
7891 else {
7892 parserInput.restore();
7893 }
7894 },
7895 anonymousValue: function () {
7896 var index = parserInput.i;
7897 var match = parserInput.$re(/^([^.#@\$+\/'"*`(;{}-]*);/);
7898 if (match) {
7899 return new (tree.Anonymous)(match[1], index);
7900 }
7901 },
7902 /**
7903 * Used for custom properties, at-rules, and variables (as fallback)
7904 * Parses almost anything inside of {} [] () "" blocks
7905 * until it reaches outer-most tokens.
7906 *
7907 * First, it will try to parse comments and entities to reach
7908 * the end. This is mostly like the Expression parser except no
7909 * math is allowed.
7910 */
7911 permissiveValue: function (untilTokens) {
7912 var i;
7913 var e;
7914 var done;
7915 var value;
7916 var tok = untilTokens || ';';
7917 var index = parserInput.i;
7918 var result = [];
7919 function testCurrentChar() {
7920 var char = parserInput.currentChar();
7921 if (typeof tok === 'string') {
7922 return char === tok;
7923 }
7924 else {
7925 return tok.test(char);
7926 }
7927 }
7928 if (testCurrentChar()) {
7929 return;
7930 }
7931 value = [];
7932 do {
7933 e = this.comment();
7934 if (e) {
7935 value.push(e);
7936 continue;
7937 }
7938 e = this.entity();
7939 if (e) {
7940 value.push(e);
7941 }
7942 } while (e);
7943 done = testCurrentChar();
7944 if (value.length > 0) {
7945 value = new (tree.Expression)(value);
7946 if (done) {
7947 return value;
7948 }
7949 else {
7950 result.push(value);
7951 }
7952 // Preserve space before $parseUntil as it will not
7953 if (parserInput.prevChar() === ' ') {
7954 result.push(new tree.Anonymous(' ', index));
7955 }
7956 }
7957 parserInput.save();
7958 value = parserInput.$parseUntil(tok);
7959 if (value) {
7960 if (typeof value === 'string') {
7961 error("Expected '" + value + "'", 'Parse');
7962 }
7963 if (value.length === 1 && value[0] === ' ') {
7964 parserInput.forget();
7965 return new tree.Anonymous('', index);
7966 }
7967 var item = void 0;
7968 for (i = 0; i < value.length; i++) {
7969 item = value[i];
7970 if (Array.isArray(item)) {
7971 // Treat actual quotes as normal quoted values
7972 result.push(new tree.Quoted(item[0], item[1], true, index, fileInfo));
7973 }
7974 else {
7975 if (i === value.length - 1) {
7976 item = item.trim();
7977 }
7978 // Treat like quoted values, but replace vars like unquoted expressions
7979 var quote = new tree.Quoted('\'', item, true, index, fileInfo);
7980 quote.variableRegex = /@([\w-]+)/g;
7981 quote.propRegex = /\$([\w-]+)/g;
7982 result.push(quote);
7983 }
7984 }
7985 parserInput.forget();
7986 return new tree.Expression(result, true);
7987 }
7988 parserInput.restore();
7989 },
7990 //
7991 // An @import atrule
7992 //
7993 // @import "lib";
7994 //
7995 // Depending on our environment, importing is done differently:
7996 // In the browser, it's an XHR request, in Node, it would be a
7997 // file-system operation. The function used for importing is
7998 // stored in `import`, which we pass to the Import constructor.
7999 //
8000 'import': function () {
8001 var path;
8002 var features;
8003 var index = parserInput.i;
8004 var dir = parserInput.$re(/^@import?\s+/);
8005 if (dir) {
8006 var options = (dir ? this.importOptions() : null) || {};
8007 if ((path = this.entities.quoted() || this.entities.url())) {
8008 features = this.mediaFeatures();
8009 if (!parserInput.$char(';')) {
8010 parserInput.i = index;
8011 error('missing semi-colon or unrecognised media features on import');
8012 }
8013 features = features && new (tree.Value)(features);
8014 return new (tree.Import)(path, features, options, index, fileInfo);
8015 }
8016 else {
8017 parserInput.i = index;
8018 error('malformed import statement');
8019 }
8020 }
8021 },
8022 importOptions: function () {
8023 var o;
8024 var options = {};
8025 var optionName;
8026 var value;
8027 // list of options, surrounded by parens
8028 if (!parserInput.$char('(')) {
8029 return null;
8030 }
8031 do {
8032 o = this.importOption();
8033 if (o) {
8034 optionName = o;
8035 value = true;
8036 switch (optionName) {
8037 case 'css':
8038 optionName = 'less';
8039 value = false;
8040 break;
8041 case 'once':
8042 optionName = 'multiple';
8043 value = false;
8044 break;
8045 }
8046 options[optionName] = value;
8047 if (!parserInput.$char(',')) {
8048 break;
8049 }
8050 }
8051 } while (o);
8052 expectChar(')');
8053 return options;
8054 },
8055 importOption: function () {
8056 var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
8057 if (opt) {
8058 return opt[1];
8059 }
8060 },
8061 mediaFeature: function () {
8062 var entities = this.entities;
8063 var nodes = [];
8064 var e;
8065 var p;
8066 parserInput.save();
8067 do {
8068 e = entities.keyword() || entities.variable() || entities.mixinLookup();
8069 if (e) {
8070 nodes.push(e);
8071 }
8072 else if (parserInput.$char('(')) {
8073 p = this.property();
8074 e = this.value();
8075 if (parserInput.$char(')')) {
8076 if (p && e) {
8077 nodes.push(new (tree.Paren)(new (tree.Declaration)(p, e, null, null, parserInput.i, fileInfo, true)));
8078 }
8079 else if (e) {
8080 nodes.push(new (tree.Paren)(e));
8081 }
8082 else {
8083 error('badly formed media feature definition');
8084 }
8085 }
8086 else {
8087 error('Missing closing \')\'', 'Parse');
8088 }
8089 }
8090 } while (e);
8091 parserInput.forget();
8092 if (nodes.length > 0) {
8093 return new (tree.Expression)(nodes);
8094 }
8095 },
8096 mediaFeatures: function () {
8097 var entities = this.entities;
8098 var features = [];
8099 var e;
8100 do {
8101 e = this.mediaFeature();
8102 if (e) {
8103 features.push(e);
8104 if (!parserInput.$char(',')) {
8105 break;
8106 }
8107 }
8108 else {
8109 e = entities.variable() || entities.mixinLookup();
8110 if (e) {
8111 features.push(e);
8112 if (!parserInput.$char(',')) {
8113 break;
8114 }
8115 }
8116 }
8117 } while (e);
8118 return features.length > 0 ? features : null;
8119 },
8120 media: function () {
8121 var features;
8122 var rules;
8123 var media;
8124 var debugInfo;
8125 var index = parserInput.i;
8126 if (context.dumpLineNumbers) {
8127 debugInfo = getDebugInfo(index);
8128 }
8129 parserInput.save();
8130 if (parserInput.$str('@media')) {
8131 features = this.mediaFeatures();
8132 rules = this.block();
8133 if (!rules) {
8134 error('media definitions require block statements after any features');
8135 }
8136 parserInput.forget();
8137 media = new (tree.Media)(rules, features, index, fileInfo);
8138 if (context.dumpLineNumbers) {
8139 media.debugInfo = debugInfo;
8140 }
8141 return media;
8142 }
8143 parserInput.restore();
8144 },
8145 //
8146 // A @plugin directive, used to import plugins dynamically.
8147 //
8148 // @plugin (args) "lib";
8149 //
8150 plugin: function () {
8151 var path;
8152 var args;
8153 var options;
8154 var index = parserInput.i;
8155 var dir = parserInput.$re(/^@plugin?\s+/);
8156 if (dir) {
8157 args = this.pluginArgs();
8158 if (args) {
8159 options = {
8160 pluginArgs: args,
8161 isPlugin: true
8162 };
8163 }
8164 else {
8165 options = { isPlugin: true };
8166 }
8167 if ((path = this.entities.quoted() || this.entities.url())) {
8168 if (!parserInput.$char(';')) {
8169 parserInput.i = index;
8170 error('missing semi-colon on @plugin');
8171 }
8172 return new (tree.Import)(path, null, options, index, fileInfo);
8173 }
8174 else {
8175 parserInput.i = index;
8176 error('malformed @plugin statement');
8177 }
8178 }
8179 },
8180 pluginArgs: function () {
8181 // list of options, surrounded by parens
8182 parserInput.save();
8183 if (!parserInput.$char('(')) {
8184 parserInput.restore();
8185 return null;
8186 }
8187 var args = parserInput.$re(/^\s*([^\);]+)\)\s*/);
8188 if (args[1]) {
8189 parserInput.forget();
8190 return args[1].trim();
8191 }
8192 else {
8193 parserInput.restore();
8194 return null;
8195 }
8196 },
8197 //
8198 // A CSS AtRule
8199 //
8200 // @charset "utf-8";
8201 //
8202 atrule: function () {
8203 var index = parserInput.i;
8204 var name;
8205 var value;
8206 var rules;
8207 var nonVendorSpecificName;
8208 var hasIdentifier;
8209 var hasExpression;
8210 var hasUnknown;
8211 var hasBlock = true;
8212 var isRooted = true;
8213 if (parserInput.currentChar() !== '@') {
8214 return;
8215 }
8216 value = this['import']() || this.plugin() || this.media();
8217 if (value) {
8218 return value;
8219 }
8220 parserInput.save();
8221 name = parserInput.$re(/^@[a-z-]+/);
8222 if (!name) {
8223 return;
8224 }
8225 nonVendorSpecificName = name;
8226 if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
8227 nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
8228 }
8229 switch (nonVendorSpecificName) {
8230 case '@charset':
8231 hasIdentifier = true;
8232 hasBlock = false;
8233 break;
8234 case '@namespace':
8235 hasExpression = true;
8236 hasBlock = false;
8237 break;
8238 case '@keyframes':
8239 case '@counter-style':
8240 hasIdentifier = true;
8241 break;
8242 case '@document':
8243 case '@supports':
8244 hasUnknown = true;
8245 isRooted = false;
8246 break;
8247 default:
8248 hasUnknown = true;
8249 break;
8250 }
8251 parserInput.commentStore.length = 0;
8252 if (hasIdentifier) {
8253 value = this.entity();
8254 if (!value) {
8255 error("expected " + name + " identifier");
8256 }
8257 }
8258 else if (hasExpression) {
8259 value = this.expression();
8260 if (!value) {
8261 error("expected " + name + " expression");
8262 }
8263 }
8264 else if (hasUnknown) {
8265 value = this.permissiveValue(/^[{;]/);
8266 hasBlock = (parserInput.currentChar() === '{');
8267 if (!value) {
8268 if (!hasBlock && parserInput.currentChar() !== ';') {
8269 error(name + " rule is missing block or ending semi-colon");
8270 }
8271 }
8272 else if (!value.value) {
8273 value = null;
8274 }
8275 }
8276 if (hasBlock) {
8277 rules = this.blockRuleset();
8278 }
8279 if (rules || (!hasBlock && value && parserInput.$char(';'))) {
8280 parserInput.forget();
8281 return new (tree.AtRule)(name, value, rules, index, fileInfo, context.dumpLineNumbers ? getDebugInfo(index) : null, isRooted);
8282 }
8283 parserInput.restore('at-rule options not recognised');
8284 },
8285 //
8286 // A Value is a comma-delimited list of Expressions
8287 //
8288 // font-family: Baskerville, Georgia, serif;
8289 //
8290 // In a Rule, a Value represents everything after the `:`,
8291 // and before the `;`.
8292 //
8293 value: function () {
8294 var e;
8295 var expressions = [];
8296 var index = parserInput.i;
8297 do {
8298 e = this.expression();
8299 if (e) {
8300 expressions.push(e);
8301 if (!parserInput.$char(',')) {
8302 break;
8303 }
8304 }
8305 } while (e);
8306 if (expressions.length > 0) {
8307 return new (tree.Value)(expressions, index);
8308 }
8309 },
8310 important: function () {
8311 if (parserInput.currentChar() === '!') {
8312 return parserInput.$re(/^! *important/);
8313 }
8314 },
8315 sub: function () {
8316 var a;
8317 var e;
8318 parserInput.save();
8319 if (parserInput.$char('(')) {
8320 a = this.addition();
8321 if (a && parserInput.$char(')')) {
8322 parserInput.forget();
8323 e = new (tree.Expression)([a]);
8324 e.parens = true;
8325 return e;
8326 }
8327 parserInput.restore('Expected \')\'');
8328 return;
8329 }
8330 parserInput.restore();
8331 },
8332 multiplication: function () {
8333 var m;
8334 var a;
8335 var op;
8336 var operation;
8337 var isSpaced;
8338 m = this.operand();
8339 if (m) {
8340 isSpaced = parserInput.isWhitespace(-1);
8341 while (true) {
8342 if (parserInput.peek(/^\/[*\/]/)) {
8343 break;
8344 }
8345 parserInput.save();
8346 op = parserInput.$char('/') || parserInput.$char('*') || parserInput.$str('./');
8347 if (!op) {
8348 parserInput.forget();
8349 break;
8350 }
8351 a = this.operand();
8352 if (!a) {
8353 parserInput.restore();
8354 break;
8355 }
8356 parserInput.forget();
8357 m.parensInOp = true;
8358 a.parensInOp = true;
8359 operation = new (tree.Operation)(op, [operation || m, a], isSpaced);
8360 isSpaced = parserInput.isWhitespace(-1);
8361 }
8362 return operation || m;
8363 }
8364 },
8365 addition: function () {
8366 var m;
8367 var a;
8368 var op;
8369 var operation;
8370 var isSpaced;
8371 m = this.multiplication();
8372 if (m) {
8373 isSpaced = parserInput.isWhitespace(-1);
8374 while (true) {
8375 op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-')));
8376 if (!op) {
8377 break;
8378 }
8379 a = this.multiplication();
8380 if (!a) {
8381 break;
8382 }
8383 m.parensInOp = true;
8384 a.parensInOp = true;
8385 operation = new (tree.Operation)(op, [operation || m, a], isSpaced);
8386 isSpaced = parserInput.isWhitespace(-1);
8387 }
8388 return operation || m;
8389 }
8390 },
8391 conditions: function () {
8392 var a;
8393 var b;
8394 var index = parserInput.i;
8395 var condition;
8396 a = this.condition(true);
8397 if (a) {
8398 while (true) {
8399 if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) {
8400 break;
8401 }
8402 b = this.condition(true);
8403 if (!b) {
8404 break;
8405 }
8406 condition = new (tree.Condition)('or', condition || a, b, index);
8407 }
8408 return condition || a;
8409 }
8410 },
8411 condition: function (needsParens) {
8412 var result;
8413 var logical;
8414 var next;
8415 function or() {
8416 return parserInput.$str('or');
8417 }
8418 result = this.conditionAnd(needsParens);
8419 if (!result) {
8420 return;
8421 }
8422 logical = or();
8423 if (logical) {
8424 next = this.condition(needsParens);
8425 if (next) {
8426 result = new (tree.Condition)(logical, result, next);
8427 }
8428 else {
8429 return;
8430 }
8431 }
8432 return result;
8433 },
8434 conditionAnd: function (needsParens) {
8435 var result;
8436 var logical;
8437 var next;
8438 var self = this;
8439 function insideCondition() {
8440 var cond = self.negatedCondition(needsParens) || self.parenthesisCondition(needsParens);
8441 if (!cond && !needsParens) {
8442 return self.atomicCondition(needsParens);
8443 }
8444 return cond;
8445 }
8446 function and() {
8447 return parserInput.$str('and');
8448 }
8449 result = insideCondition();
8450 if (!result) {
8451 return;
8452 }
8453 logical = and();
8454 if (logical) {
8455 next = this.conditionAnd(needsParens);
8456 if (next) {
8457 result = new (tree.Condition)(logical, result, next);
8458 }
8459 else {
8460 return;
8461 }
8462 }
8463 return result;
8464 },
8465 negatedCondition: function (needsParens) {
8466 if (parserInput.$str('not')) {
8467 var result = this.parenthesisCondition(needsParens);
8468 if (result) {
8469 result.negate = !result.negate;
8470 }
8471 return result;
8472 }
8473 },
8474 parenthesisCondition: function (needsParens) {
8475 function tryConditionFollowedByParenthesis(me) {
8476 var body;
8477 parserInput.save();
8478 body = me.condition(needsParens);
8479 if (!body) {
8480 parserInput.restore();
8481 return;
8482 }
8483 if (!parserInput.$char(')')) {
8484 parserInput.restore();
8485 return;
8486 }
8487 parserInput.forget();
8488 return body;
8489 }
8490 var body;
8491 parserInput.save();
8492 if (!parserInput.$str('(')) {
8493 parserInput.restore();
8494 return;
8495 }
8496 body = tryConditionFollowedByParenthesis(this);
8497 if (body) {
8498 parserInput.forget();
8499 return body;
8500 }
8501 body = this.atomicCondition(needsParens);
8502 if (!body) {
8503 parserInput.restore();
8504 return;
8505 }
8506 if (!parserInput.$char(')')) {
8507 parserInput.restore("expected ')' got '" + parserInput.currentChar() + "'");
8508 return;
8509 }
8510 parserInput.forget();
8511 return body;
8512 },
8513 atomicCondition: function (needsParens) {
8514 var entities = this.entities;
8515 var index = parserInput.i;
8516 var a;
8517 var b;
8518 var c;
8519 var op;
8520 function cond() {
8521 return this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
8522 }
8523 cond = cond.bind(this);
8524 a = cond();
8525 if (a) {
8526 if (parserInput.$char('>')) {
8527 if (parserInput.$char('=')) {
8528 op = '>=';
8529 }
8530 else {
8531 op = '>';
8532 }
8533 }
8534 else if (parserInput.$char('<')) {
8535 if (parserInput.$char('=')) {
8536 op = '<=';
8537 }
8538 else {
8539 op = '<';
8540 }
8541 }
8542 else if (parserInput.$char('=')) {
8543 if (parserInput.$char('>')) {
8544 op = '=>';
8545 }
8546 else if (parserInput.$char('<')) {
8547 op = '=<';
8548 }
8549 else {
8550 op = '=';
8551 }
8552 }
8553 if (op) {
8554 b = cond();
8555 if (b) {
8556 c = new (tree.Condition)(op, a, b, index, false);
8557 }
8558 else {
8559 error('expected expression');
8560 }
8561 }
8562 else {
8563 c = new (tree.Condition)('=', a, new (tree.Keyword)('true'), index, false);
8564 }
8565 return c;
8566 }
8567 },
8568 //
8569 // An operand is anything that can be part of an operation,
8570 // such as a Color, or a Variable
8571 //
8572 operand: function () {
8573 var entities = this.entities;
8574 var negate;
8575 if (parserInput.peek(/^-[@\$\(]/)) {
8576 negate = parserInput.$char('-');
8577 }
8578 var o = this.sub() || entities.dimension() ||
8579 entities.color() || entities.variable() ||
8580 entities.property() || entities.call() ||
8581 entities.quoted(true) || entities.colorKeyword() ||
8582 entities.mixinLookup();
8583 if (negate) {
8584 o.parensInOp = true;
8585 o = new (tree.Negative)(o);
8586 }
8587 return o;
8588 },
8589 //
8590 // Expressions either represent mathematical operations,
8591 // or white-space delimited Entities.
8592 //
8593 // 1px solid black
8594 // @var * 2
8595 //
8596 expression: function () {
8597 var entities = [];
8598 var e;
8599 var delim;
8600 var index = parserInput.i;
8601 do {
8602 e = this.comment();
8603 if (e) {
8604 entities.push(e);
8605 continue;
8606 }
8607 e = this.addition() || this.entity();
8608 if (e instanceof tree.Comment) {
8609 e = null;
8610 }
8611 if (e) {
8612 entities.push(e);
8613 // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
8614 if (!parserInput.peek(/^\/[\/*]/)) {
8615 delim = parserInput.$char('/');
8616 if (delim) {
8617 entities.push(new (tree.Anonymous)(delim, index));
8618 }
8619 }
8620 }
8621 } while (e);
8622 if (entities.length > 0) {
8623 return new (tree.Expression)(entities);
8624 }
8625 },
8626 property: function () {
8627 var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
8628 if (name) {
8629 return name[1];
8630 }
8631 },
8632 ruleProperty: function () {
8633 var name = [];
8634 var index = [];
8635 var s;
8636 var k;
8637 parserInput.save();
8638 var simpleProperty = parserInput.$re(/^([_a-zA-Z0-9-]+)\s*:/);
8639 if (simpleProperty) {
8640 name = [new (tree.Keyword)(simpleProperty[1])];
8641 parserInput.forget();
8642 return name;
8643 }
8644 function match(re) {
8645 var i = parserInput.i;
8646 var chunk = parserInput.$re(re);
8647 if (chunk) {
8648 index.push(i);
8649 return name.push(chunk[1]);
8650 }
8651 }
8652 match(/^(\*?)/);
8653 while (true) {
8654 if (!match(/^((?:[\w-]+)|(?:[@\$]\{[\w-]+\}))/)) {
8655 break;
8656 }
8657 }
8658 if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) {
8659 parserInput.forget();
8660 // at last, we have the complete match now. move forward,
8661 // convert name particles to tree objects and return:
8662 if (name[0] === '') {
8663 name.shift();
8664 index.shift();
8665 }
8666 for (k = 0; k < name.length; k++) {
8667 s = name[k];
8668 name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
8669 new (tree.Keyword)(s) :
8670 (s.charAt(0) === '@' ?
8671 new (tree.Variable)("@" + s.slice(2, -1), index[k], fileInfo) :
8672 new (tree.Property)("$" + s.slice(2, -1), index[k], fileInfo));
8673 }
8674 return name;
8675 }
8676 parserInput.restore();
8677 }
8678 }
8679 };
8680 };
8681 Parser.serializeVars = function (vars) {
8682 var s = '';
8683 for (var name_1 in vars) {
8684 if (Object.hasOwnProperty.call(vars, name_1)) {
8685 var value = vars[name_1];
8686 s += ((name_1[0] === '@') ? '' : '@') + name_1 + ": " + value + ((String(value).slice(-1) === ';') ? '' : ';');
8687 }
8688 }
8689 return s;
8690 };
8691
8692 function boolean(condition) {
8693 return condition ? Keyword.True : Keyword.False;
8694 }
8695 /**
8696 * Functions with evalArgs set to false are sent context
8697 * as the first argument.
8698 */
8699 function If(context, condition, trueValue, falseValue) {
8700 return condition.eval(context) ? trueValue.eval(context)
8701 : (falseValue ? falseValue.eval(context) : new Anonymous);
8702 }
8703 If.evalArgs = false;
8704 function isdefined(context, variable) {
8705 try {
8706 variable.eval(context);
8707 return Keyword.True;
8708 }
8709 catch (e) {
8710 return Keyword.False;
8711 }
8712 }
8713 isdefined.evalArgs = false;
8714 var boolean$1 = { isdefined: isdefined, boolean: boolean, 'if': If };
8715
8716 var colorFunctions;
8717 function clamp$1(val) {
8718 return Math.min(1, Math.max(0, val));
8719 }
8720 function hsla(origColor, hsl) {
8721 var color = colorFunctions.hsla(hsl.h, hsl.s, hsl.l, hsl.a);
8722 if (color) {
8723 if (origColor.value &&
8724 /^(rgb|hsl)/.test(origColor.value)) {
8725 color.value = origColor.value;
8726 }
8727 else {
8728 color.value = 'rgb';
8729 }
8730 return color;
8731 }
8732 }
8733 function toHSL(color) {
8734 if (color.toHSL) {
8735 return color.toHSL();
8736 }
8737 else {
8738 throw new Error('Argument cannot be evaluated to a color');
8739 }
8740 }
8741 function toHSV(color) {
8742 if (color.toHSV) {
8743 return color.toHSV();
8744 }
8745 else {
8746 throw new Error('Argument cannot be evaluated to a color');
8747 }
8748 }
8749 function number(n) {
8750 if (n instanceof Dimension) {
8751 return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
8752 }
8753 else if (typeof n === 'number') {
8754 return n;
8755 }
8756 else {
8757 throw {
8758 type: 'Argument',
8759 message: 'color functions take numbers as parameters'
8760 };
8761 }
8762 }
8763 function scaled(n, size) {
8764 if (n instanceof Dimension && n.unit.is('%')) {
8765 return parseFloat(n.value * size / 100);
8766 }
8767 else {
8768 return number(n);
8769 }
8770 }
8771 colorFunctions = {
8772 rgb: function (r, g, b) {
8773 var a = 1;
8774 /**
8775 * Comma-less syntax
8776 * e.g. rgb(0 128 255 / 50%)
8777 */
8778 if (r instanceof Expression) {
8779 var val = r.value;
8780 r = val[0];
8781 g = val[1];
8782 b = val[2];
8783 /**
8784 * @todo - should this be normalized in
8785 * function caller? Or parsed differently?
8786 */
8787 if (b instanceof Operation) {
8788 var op = b;
8789 b = op.operands[0];
8790 a = op.operands[1];
8791 }
8792 }
8793 var color = colorFunctions.rgba(r, g, b, a);
8794 if (color) {
8795 color.value = 'rgb';
8796 return color;
8797 }
8798 },
8799 rgba: function (r, g, b, a) {
8800 try {
8801 if (r instanceof Color) {
8802 if (g) {
8803 a = number(g);
8804 }
8805 else {
8806 a = r.alpha;
8807 }
8808 return new Color(r.rgb, a, 'rgba');
8809 }
8810 var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
8811 a = number(a);
8812 return new Color(rgb, a, 'rgba');
8813 }
8814 catch (e) { }
8815 },
8816 hsl: function (h, s, l) {
8817 var a = 1;
8818 if (h instanceof Expression) {
8819 var val = h.value;
8820 h = val[0];
8821 s = val[1];
8822 l = val[2];
8823 if (l instanceof Operation) {
8824 var op = l;
8825 l = op.operands[0];
8826 a = op.operands[1];
8827 }
8828 }
8829 var color = colorFunctions.hsla(h, s, l, a);
8830 if (color) {
8831 color.value = 'hsl';
8832 return color;
8833 }
8834 },
8835 hsla: function (h, s, l, a) {
8836 try {
8837 if (h instanceof Color) {
8838 if (s) {
8839 a = number(s);
8840 }
8841 else {
8842 a = h.alpha;
8843 }
8844 return new Color(h.rgb, a, 'hsla');
8845 }
8846 var m1_1;
8847 var m2_1;
8848 function hue(h) {
8849 h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
8850 if (h * 6 < 1) {
8851 return m1_1 + (m2_1 - m1_1) * h * 6;
8852 }
8853 else if (h * 2 < 1) {
8854 return m2_1;
8855 }
8856 else if (h * 3 < 2) {
8857 return m1_1 + (m2_1 - m1_1) * (2 / 3 - h) * 6;
8858 }
8859 else {
8860 return m1_1;
8861 }
8862 }
8863 h = (number(h) % 360) / 360;
8864 s = clamp$1(number(s));
8865 l = clamp$1(number(l));
8866 a = clamp$1(number(a));
8867 m2_1 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
8868 m1_1 = l * 2 - m2_1;
8869 var rgb = [
8870 hue(h + 1 / 3) * 255,
8871 hue(h) * 255,
8872 hue(h - 1 / 3) * 255
8873 ];
8874 a = number(a);
8875 return new Color(rgb, a, 'hsla');
8876 }
8877 catch (e) { }
8878 },
8879 hsv: function (h, s, v) {
8880 return colorFunctions.hsva(h, s, v, 1.0);
8881 },
8882 hsva: function (h, s, v, a) {
8883 h = ((number(h) % 360) / 360) * 360;
8884 s = number(s);
8885 v = number(v);
8886 a = number(a);
8887 var i;
8888 var f;
8889 i = Math.floor((h / 60) % 6);
8890 f = (h / 60) - i;
8891 var vs = [v,
8892 v * (1 - s),
8893 v * (1 - f * s),
8894 v * (1 - (1 - f) * s)];
8895 var perm = [[0, 3, 1],
8896 [2, 0, 1],
8897 [1, 0, 3],
8898 [1, 2, 0],
8899 [3, 1, 0],
8900 [0, 1, 2]];
8901 return colorFunctions.rgba(vs[perm[i][0]] * 255, vs[perm[i][1]] * 255, vs[perm[i][2]] * 255, a);
8902 },
8903 hue: function (color) {
8904 return new Dimension(toHSL(color).h);
8905 },
8906 saturation: function (color) {
8907 return new Dimension(toHSL(color).s * 100, '%');
8908 },
8909 lightness: function (color) {
8910 return new Dimension(toHSL(color).l * 100, '%');
8911 },
8912 hsvhue: function (color) {
8913 return new Dimension(toHSV(color).h);
8914 },
8915 hsvsaturation: function (color) {
8916 return new Dimension(toHSV(color).s * 100, '%');
8917 },
8918 hsvvalue: function (color) {
8919 return new Dimension(toHSV(color).v * 100, '%');
8920 },
8921 red: function (color) {
8922 return new Dimension(color.rgb[0]);
8923 },
8924 green: function (color) {
8925 return new Dimension(color.rgb[1]);
8926 },
8927 blue: function (color) {
8928 return new Dimension(color.rgb[2]);
8929 },
8930 alpha: function (color) {
8931 return new Dimension(toHSL(color).a);
8932 },
8933 luma: function (color) {
8934 return new Dimension(color.luma() * color.alpha * 100, '%');
8935 },
8936 luminance: function (color) {
8937 var luminance = (0.2126 * color.rgb[0] / 255) +
8938 (0.7152 * color.rgb[1] / 255) +
8939 (0.0722 * color.rgb[2] / 255);
8940 return new Dimension(luminance * color.alpha * 100, '%');
8941 },
8942 saturate: function (color, amount, method) {
8943 // filter: saturate(3.2);
8944 // should be kept as is, so check for color
8945 if (!color.rgb) {
8946 return null;
8947 }
8948 var hsl = toHSL(color);
8949 if (typeof method !== 'undefined' && method.value === 'relative') {
8950 hsl.s += hsl.s * amount.value / 100;
8951 }
8952 else {
8953 hsl.s += amount.value / 100;
8954 }
8955 hsl.s = clamp$1(hsl.s);
8956 return hsla(color, hsl);
8957 },
8958 desaturate: function (color, amount, method) {
8959 var hsl = toHSL(color);
8960 if (typeof method !== 'undefined' && method.value === 'relative') {
8961 hsl.s -= hsl.s * amount.value / 100;
8962 }
8963 else {
8964 hsl.s -= amount.value / 100;
8965 }
8966 hsl.s = clamp$1(hsl.s);
8967 return hsla(color, hsl);
8968 },
8969 lighten: function (color, amount, method) {
8970 var hsl = toHSL(color);
8971 if (typeof method !== 'undefined' && method.value === 'relative') {
8972 hsl.l += hsl.l * amount.value / 100;
8973 }
8974 else {
8975 hsl.l += amount.value / 100;
8976 }
8977 hsl.l = clamp$1(hsl.l);
8978 return hsla(color, hsl);
8979 },
8980 darken: function (color, amount, method) {
8981 var hsl = toHSL(color);
8982 if (typeof method !== 'undefined' && method.value === 'relative') {
8983 hsl.l -= hsl.l * amount.value / 100;
8984 }
8985 else {
8986 hsl.l -= amount.value / 100;
8987 }
8988 hsl.l = clamp$1(hsl.l);
8989 return hsla(color, hsl);
8990 },
8991 fadein: function (color, amount, method) {
8992 var hsl = toHSL(color);
8993 if (typeof method !== 'undefined' && method.value === 'relative') {
8994 hsl.a += hsl.a * amount.value / 100;
8995 }
8996 else {
8997 hsl.a += amount.value / 100;
8998 }
8999 hsl.a = clamp$1(hsl.a);
9000 return hsla(color, hsl);
9001 },
9002 fadeout: function (color, amount, method) {
9003 var hsl = toHSL(color);
9004 if (typeof method !== 'undefined' && method.value === 'relative') {
9005 hsl.a -= hsl.a * amount.value / 100;
9006 }
9007 else {
9008 hsl.a -= amount.value / 100;
9009 }
9010 hsl.a = clamp$1(hsl.a);
9011 return hsla(color, hsl);
9012 },
9013 fade: function (color, amount) {
9014 var hsl = toHSL(color);
9015 hsl.a = amount.value / 100;
9016 hsl.a = clamp$1(hsl.a);
9017 return hsla(color, hsl);
9018 },
9019 spin: function (color, amount) {
9020 var hsl = toHSL(color);
9021 var hue = (hsl.h + amount.value) % 360;
9022 hsl.h = hue < 0 ? 360 + hue : hue;
9023 return hsla(color, hsl);
9024 },
9025 //
9026 // Copyright (c) 2006-2009 Hampton Catlin, Natalie Weizenbaum, and Chris Eppstein
9027 // http://sass-lang.com
9028 //
9029 mix: function (color1, color2, weight) {
9030 if (!weight) {
9031 weight = new Dimension(50);
9032 }
9033 var p = weight.value / 100.0;
9034 var w = p * 2 - 1;
9035 var a = toHSL(color1).a - toHSL(color2).a;
9036 var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
9037 var w2 = 1 - w1;
9038 var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
9039 color1.rgb[1] * w1 + color2.rgb[1] * w2,
9040 color1.rgb[2] * w1 + color2.rgb[2] * w2];
9041 var alpha = color1.alpha * p + color2.alpha * (1 - p);
9042 return new Color(rgb, alpha);
9043 },
9044 greyscale: function (color) {
9045 return colorFunctions.desaturate(color, new Dimension(100));
9046 },
9047 contrast: function (color, dark, light, threshold) {
9048 // filter: contrast(3.2);
9049 // should be kept as is, so check for color
9050 if (!color.rgb) {
9051 return null;
9052 }
9053 if (typeof light === 'undefined') {
9054 light = colorFunctions.rgba(255, 255, 255, 1.0);
9055 }
9056 if (typeof dark === 'undefined') {
9057 dark = colorFunctions.rgba(0, 0, 0, 1.0);
9058 }
9059 // Figure out which is actually light and dark:
9060 if (dark.luma() > light.luma()) {
9061 var t = light;
9062 light = dark;
9063 dark = t;
9064 }
9065 if (typeof threshold === 'undefined') {
9066 threshold = 0.43;
9067 }
9068 else {
9069 threshold = number(threshold);
9070 }
9071 if (color.luma() < threshold) {
9072 return light;
9073 }
9074 else {
9075 return dark;
9076 }
9077 },
9078 // Changes made in 2.7.0 - Reverted in 3.0.0
9079 // contrast: function (color, color1, color2, threshold) {
9080 // // Return which of `color1` and `color2` has the greatest contrast with `color`
9081 // // according to the standard WCAG contrast ratio calculation.
9082 // // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
9083 // // The threshold param is no longer used, in line with SASS.
9084 // // filter: contrast(3.2);
9085 // // should be kept as is, so check for color
9086 // if (!color.rgb) {
9087 // return null;
9088 // }
9089 // if (typeof color1 === 'undefined') {
9090 // color1 = colorFunctions.rgba(0, 0, 0, 1.0);
9091 // }
9092 // if (typeof color2 === 'undefined') {
9093 // color2 = colorFunctions.rgba(255, 255, 255, 1.0);
9094 // }
9095 // var contrast1, contrast2;
9096 // var luma = color.luma();
9097 // var luma1 = color1.luma();
9098 // var luma2 = color2.luma();
9099 // // Calculate contrast ratios for each color
9100 // if (luma > luma1) {
9101 // contrast1 = (luma + 0.05) / (luma1 + 0.05);
9102 // } else {
9103 // contrast1 = (luma1 + 0.05) / (luma + 0.05);
9104 // }
9105 // if (luma > luma2) {
9106 // contrast2 = (luma + 0.05) / (luma2 + 0.05);
9107 // } else {
9108 // contrast2 = (luma2 + 0.05) / (luma + 0.05);
9109 // }
9110 // if (contrast1 > contrast2) {
9111 // return color1;
9112 // } else {
9113 // return color2;
9114 // }
9115 // },
9116 argb: function (color) {
9117 return new Anonymous(color.toARGB());
9118 },
9119 color: function (c) {
9120 if ((c instanceof Quoted) &&
9121 (/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})$/i.test(c.value))) {
9122 var val = c.value.slice(1);
9123 return new Color(val, undefined, "#" + val);
9124 }
9125 if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
9126 c.value = undefined;
9127 return c;
9128 }
9129 throw {
9130 type: 'Argument',
9131 message: 'argument must be a color keyword or 3|4|6|8 digit hex e.g. #FFF'
9132 };
9133 },
9134 tint: function (color, amount) {
9135 return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
9136 },
9137 shade: function (color, amount) {
9138 return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
9139 }
9140 };
9141 var color = colorFunctions;
9142
9143 // Color Blending
9144 // ref: http://www.w3.org/TR/compositing-1
9145 function colorBlend(mode, color1, color2) {
9146 var ab = color1.alpha; // result
9147 var // backdrop
9148 cb;
9149 var as = color2.alpha;
9150 var // source
9151 cs;
9152 var ar;
9153 var cr;
9154 var r = [];
9155 ar = as + ab * (1 - as);
9156 for (var i = 0; i < 3; i++) {
9157 cb = color1.rgb[i] / 255;
9158 cs = color2.rgb[i] / 255;
9159 cr = mode(cb, cs);
9160 if (ar) {
9161 cr = (as * cs + ab * (cb -
9162 as * (cb + cs - cr))) / ar;
9163 }
9164 r[i] = cr * 255;
9165 }
9166 return new Color(r, ar);
9167 }
9168 var colorBlendModeFunctions = {
9169 multiply: function (cb, cs) {
9170 return cb * cs;
9171 },
9172 screen: function (cb, cs) {
9173 return cb + cs - cb * cs;
9174 },
9175 overlay: function (cb, cs) {
9176 cb *= 2;
9177 return (cb <= 1) ?
9178 colorBlendModeFunctions.multiply(cb, cs) :
9179 colorBlendModeFunctions.screen(cb - 1, cs);
9180 },
9181 softlight: function (cb, cs) {
9182 var d = 1;
9183 var e = cb;
9184 if (cs > 0.5) {
9185 e = 1;
9186 d = (cb > 0.25) ? Math.sqrt(cb)
9187 : ((16 * cb - 12) * cb + 4) * cb;
9188 }
9189 return cb - (1 - 2 * cs) * e * (d - cb);
9190 },
9191 hardlight: function (cb, cs) {
9192 return colorBlendModeFunctions.overlay(cs, cb);
9193 },
9194 difference: function (cb, cs) {
9195 return Math.abs(cb - cs);
9196 },
9197 exclusion: function (cb, cs) {
9198 return cb + cs - 2 * cb * cs;
9199 },
9200 // non-w3c functions:
9201 average: function (cb, cs) {
9202 return (cb + cs) / 2;
9203 },
9204 negation: function (cb, cs) {
9205 return 1 - Math.abs(cb + cs - 1);
9206 }
9207 };
9208 for (var f in colorBlendModeFunctions) {
9209 if (colorBlendModeFunctions.hasOwnProperty(f)) {
9210 colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]);
9211 }
9212 }
9213
9214 var dataUri = (function (environment) {
9215 var fallback = function (functionThis, node) { return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context); };
9216 return { 'data-uri': function (mimetypeNode, filePathNode) {
9217 if (!filePathNode) {
9218 filePathNode = mimetypeNode;
9219 mimetypeNode = null;
9220 }
9221 var mimetype = mimetypeNode && mimetypeNode.value;
9222 var filePath = filePathNode.value;
9223 var currentFileInfo = this.currentFileInfo;
9224 var currentDirectory = currentFileInfo.rewriteUrls ?
9225 currentFileInfo.currentDirectory : currentFileInfo.entryPath;
9226 var fragmentStart = filePath.indexOf('#');
9227 var fragment = '';
9228 if (fragmentStart !== -1) {
9229 fragment = filePath.slice(fragmentStart);
9230 filePath = filePath.slice(0, fragmentStart);
9231 }
9232 var context = clone(this.context);
9233 context.rawBuffer = true;
9234 var fileManager = environment.getFileManager(filePath, currentDirectory, context, environment, true);
9235 if (!fileManager) {
9236 return fallback(this, filePathNode);
9237 }
9238 var useBase64 = false;
9239 // detect the mimetype if not given
9240 if (!mimetypeNode) {
9241 mimetype = environment.mimeLookup(filePath);
9242 if (mimetype === 'image/svg+xml') {
9243 useBase64 = false;
9244 }
9245 else {
9246 // use base 64 unless it's an ASCII or UTF-8 format
9247 var charset = environment.charsetLookup(mimetype);
9248 useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
9249 }
9250 if (useBase64) {
9251 mimetype += ';base64';
9252 }
9253 }
9254 else {
9255 useBase64 = /;base64$/.test(mimetype);
9256 }
9257 var fileSync = fileManager.loadFileSync(filePath, currentDirectory, context, environment);
9258 if (!fileSync.contents) {
9259 logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
9260 return fallback(this, filePathNode || mimetypeNode);
9261 }
9262 var buf = fileSync.contents;
9263 if (useBase64 && !environment.encodeBase64) {
9264 return fallback(this, filePathNode);
9265 }
9266 buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
9267 var uri = "data:" + mimetype + "," + buf + fragment;
9268 return new URL(new Quoted("\"" + uri + "\"", uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
9269 } };
9270 });
9271
9272 var getItemsFromNode = function (node) {
9273 // handle non-array values as an array of length 1
9274 // return 'undefined' if index is invalid
9275 var items = Array.isArray(node.value) ?
9276 node.value : Array(node);
9277 return items;
9278 };
9279 var list = {
9280 _SELF: function (n) {
9281 return n;
9282 },
9283 '~': function () {
9284 var expr = [];
9285 for (var _i = 0; _i < arguments.length; _i++) {
9286 expr[_i] = arguments[_i];
9287 }
9288 if (expr.length === 1) {
9289 return expr[0];
9290 }
9291 return new Value(expr);
9292 },
9293 extract: function (values, index) {
9294 // (1-based index)
9295 index = index.value - 1;
9296 return getItemsFromNode(values)[index];
9297 },
9298 length: function (values) {
9299 return new Dimension(getItemsFromNode(values).length);
9300 },
9301 /**
9302 * Creates a Less list of incremental values.
9303 * Modeled after Lodash's range function, also exists natively in PHP
9304 *
9305 * @param {Dimension} [start=1]
9306 * @param {Dimension} end - e.g. 10 or 10px - unit is added to output
9307 * @param {Dimension} [step=1]
9308 */
9309 range: function (start, end, step) {
9310 var from;
9311 var to;
9312 var stepValue = 1;
9313 var list = [];
9314 if (end) {
9315 to = end;
9316 from = start.value;
9317 if (step) {
9318 stepValue = step.value;
9319 }
9320 }
9321 else {
9322 from = 1;
9323 to = start;
9324 }
9325 for (var i = from; i <= to.value; i += stepValue) {
9326 list.push(new Dimension(i, to.unit));
9327 }
9328 return new Expression(list);
9329 },
9330 each: function (list, rs) {
9331 var _this = this;
9332 var rules = [];
9333 var newRules;
9334 var iterator;
9335 var tryEval = function (val) {
9336 if (val instanceof Node) {
9337 return val.eval(_this.context);
9338 }
9339 return val;
9340 };
9341 if (list.value && !(list instanceof Quoted)) {
9342 if (Array.isArray(list.value)) {
9343 iterator = list.value.map(tryEval);
9344 }
9345 else {
9346 iterator = [tryEval(list.value)];
9347 }
9348 }
9349 else if (list.ruleset) {
9350 iterator = tryEval(list.ruleset).rules;
9351 }
9352 else if (list.rules) {
9353 iterator = list.rules.map(tryEval);
9354 }
9355 else if (Array.isArray(list)) {
9356 iterator = list.map(tryEval);
9357 }
9358 else {
9359 iterator = [tryEval(list)];
9360 }
9361 var valueName = '@value';
9362 var keyName = '@key';
9363 var indexName = '@index';
9364 if (rs.params) {
9365 valueName = rs.params[0] && rs.params[0].name;
9366 keyName = rs.params[1] && rs.params[1].name;
9367 indexName = rs.params[2] && rs.params[2].name;
9368 rs = rs.rules;
9369 }
9370 else {
9371 rs = rs.ruleset;
9372 }
9373 for (var i = 0; i < iterator.length; i++) {
9374 var key = void 0;
9375 var value = void 0;
9376 var item = iterator[i];
9377 if (item instanceof Declaration) {
9378 key = typeof item.name === 'string' ? item.name : item.name[0].value;
9379 value = item.value;
9380 }
9381 else {
9382 key = new Dimension(i + 1);
9383 value = item;
9384 }
9385 if (item instanceof Comment) {
9386 continue;
9387 }
9388 newRules = rs.rules.slice(0);
9389 if (valueName) {
9390 newRules.push(new Declaration(valueName, value, false, false, this.index, this.currentFileInfo));
9391 }
9392 if (indexName) {
9393 newRules.push(new Declaration(indexName, new Dimension(i + 1), false, false, this.index, this.currentFileInfo));
9394 }
9395 if (keyName) {
9396 newRules.push(new Declaration(keyName, key, false, false, this.index, this.currentFileInfo));
9397 }
9398 rules.push(new Ruleset([new (Selector)([new Element("", '&')])], newRules, rs.strictImports, rs.visibilityInfo()));
9399 }
9400 return new Ruleset([new (Selector)([new Element("", '&')])], rules, rs.strictImports, rs.visibilityInfo()).eval(this.context);
9401 }
9402 };
9403
9404 var MathHelper = function (fn, unit, n) {
9405 if (!(n instanceof Dimension)) {
9406 throw { type: 'Argument', message: 'argument must be a number' };
9407 }
9408 if (unit == null) {
9409 unit = n.unit;
9410 }
9411 else {
9412 n = n.unify();
9413 }
9414 return new Dimension(fn(parseFloat(n.value)), unit);
9415 };
9416
9417 var mathFunctions = {
9418 // name, unit
9419 ceil: null,
9420 floor: null,
9421 sqrt: null,
9422 abs: null,
9423 tan: '',
9424 sin: '',
9425 cos: '',
9426 atan: 'rad',
9427 asin: 'rad',
9428 acos: 'rad'
9429 };
9430 for (var f$1 in mathFunctions) {
9431 if (mathFunctions.hasOwnProperty(f$1)) {
9432 mathFunctions[f$1] = MathHelper.bind(null, Math[f$1], mathFunctions[f$1]);
9433 }
9434 }
9435 mathFunctions.round = function (n, f) {
9436 var fraction = typeof f === 'undefined' ? 0 : f.value;
9437 return MathHelper(function (num) { return num.toFixed(fraction); }, null, n);
9438 };
9439
9440 var minMax = function (isMin, args) {
9441 args = Array.prototype.slice.call(args);
9442 switch (args.length) {
9443 case 0: throw { type: 'Argument', message: 'one or more arguments required' };
9444 }
9445 var i; // key is the unit.toString() for unified Dimension values,
9446 var j;
9447 var current;
9448 var currentUnified;
9449 var referenceUnified;
9450 var unit;
9451 var unitStatic;
9452 var unitClone;
9453 var // elems only contains original argument values.
9454 order = [];
9455 var values = {};
9456 // value is the index into the order array.
9457 for (i = 0; i < args.length; i++) {
9458 current = args[i];
9459 if (!(current instanceof Dimension)) {
9460 if (Array.isArray(args[i].value)) {
9461 Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
9462 }
9463 continue;
9464 }
9465 currentUnified = current.unit.toString() === '' && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify();
9466 unit = currentUnified.unit.toString() === '' && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
9467 unitStatic = unit !== '' && unitStatic === undefined || unit !== '' && order[0].unify().unit.toString() === '' ? unit : unitStatic;
9468 unitClone = unit !== '' && unitClone === undefined ? current.unit.toString() : unitClone;
9469 j = values[''] !== undefined && unit !== '' && unit === unitStatic ? values[''] : values[unit];
9470 if (j === undefined) {
9471 if (unitStatic !== undefined && unit !== unitStatic) {
9472 throw { type: 'Argument', message: 'incompatible types' };
9473 }
9474 values[unit] = order.length;
9475 order.push(current);
9476 continue;
9477 }
9478 referenceUnified = order[j].unit.toString() === '' && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify();
9479 if (isMin && currentUnified.value < referenceUnified.value ||
9480 !isMin && currentUnified.value > referenceUnified.value) {
9481 order[j] = current;
9482 }
9483 }
9484 if (order.length == 1) {
9485 return order[0];
9486 }
9487 args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? ',' : ', ');
9488 return new Anonymous((isMin ? 'min' : 'max') + "(" + args + ")");
9489 };
9490 var number$1 = {
9491 min: function () {
9492 var args = [];
9493 for (var _i = 0; _i < arguments.length; _i++) {
9494 args[_i] = arguments[_i];
9495 }
9496 try {
9497 return minMax(true, args);
9498 }
9499 catch (e) { }
9500 },
9501 max: function () {
9502 var args = [];
9503 for (var _i = 0; _i < arguments.length; _i++) {
9504 args[_i] = arguments[_i];
9505 }
9506 try {
9507 return minMax(false, args);
9508 }
9509 catch (e) { }
9510 },
9511 convert: function (val, unit) {
9512 return val.convertTo(unit.value);
9513 },
9514 pi: function () {
9515 return new Dimension(Math.PI);
9516 },
9517 mod: function (a, b) {
9518 return new Dimension(a.value % b.value, a.unit);
9519 },
9520 pow: function (x, y) {
9521 if (typeof x === 'number' && typeof y === 'number') {
9522 x = new Dimension(x);
9523 y = new Dimension(y);
9524 }
9525 else if (!(x instanceof Dimension) || !(y instanceof Dimension)) {
9526 throw { type: 'Argument', message: 'arguments must be numbers' };
9527 }
9528 return new Dimension(Math.pow(x.value, y.value), x.unit);
9529 },
9530 percentage: function (n) {
9531 var result = MathHelper(function (num) { return num * 100; }, '%', n);
9532 return result;
9533 }
9534 };
9535
9536 var string = {
9537 e: function (str) {
9538 return new Quoted('"', str instanceof JavaScript ? str.evaluated : str.value, true);
9539 },
9540 escape: function (str) {
9541 return new Anonymous(encodeURI(str.value).replace(/=/g, '%3D').replace(/:/g, '%3A').replace(/#/g, '%23').replace(/;/g, '%3B')
9542 .replace(/\(/g, '%28').replace(/\)/g, '%29'));
9543 },
9544 replace: function (string, pattern, replacement, flags) {
9545 var result = string.value;
9546 replacement = (replacement.type === 'Quoted') ?
9547 replacement.value : replacement.toCSS();
9548 result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement);
9549 return new Quoted(string.quote || '', result, string.escaped);
9550 },
9551 '%': function (string /* arg, arg, ... */) {
9552 var args = Array.prototype.slice.call(arguments, 1);
9553 var result = string.value;
9554 var _loop_1 = function (i) {
9555 /* jshint loopfunc:true */
9556 result = result.replace(/%[sda]/i, function (token) {
9557 var value = ((args[i].type === 'Quoted') &&
9558 token.match(/s/i)) ? args[i].value : args[i].toCSS();
9559 return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
9560 });
9561 };
9562 for (var i = 0; i < args.length; i++) {
9563 _loop_1(i);
9564 }
9565 result = result.replace(/%%/g, '%');
9566 return new Quoted(string.quote || '', result, string.escaped);
9567 }
9568 };
9569
9570 var svg = (function (environment) {
9571 return { 'svg-gradient': function (direction) {
9572 var stops;
9573 var gradientDirectionSvg;
9574 var gradientType = 'linear';
9575 var rectangleDimension = 'x="0" y="0" width="1" height="1"';
9576 var renderEnv = { compress: false };
9577 var returner;
9578 var directionValue = direction.toCSS(renderEnv);
9579 var i;
9580 var color;
9581 var position;
9582 var positionValue;
9583 var alpha;
9584 function throwArgumentDescriptor() {
9585 throw { type: 'Argument',
9586 message: 'svg-gradient expects direction, start_color [start_position], [color position,]...,' +
9587 ' end_color [end_position] or direction, color list' };
9588 }
9589 if (arguments.length == 2) {
9590 if (arguments[1].value.length < 2) {
9591 throwArgumentDescriptor();
9592 }
9593 stops = arguments[1].value;
9594 }
9595 else if (arguments.length < 3) {
9596 throwArgumentDescriptor();
9597 }
9598 else {
9599 stops = Array.prototype.slice.call(arguments, 1);
9600 }
9601 switch (directionValue) {
9602 case 'to bottom':
9603 gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
9604 break;
9605 case 'to right':
9606 gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
9607 break;
9608 case 'to bottom right':
9609 gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
9610 break;
9611 case 'to top right':
9612 gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
9613 break;
9614 case 'ellipse':
9615 case 'ellipse at center':
9616 gradientType = 'radial';
9617 gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
9618 rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
9619 break;
9620 default:
9621 throw { type: 'Argument', message: 'svg-gradient direction must be \'to bottom\', \'to right\',' +
9622 ' \'to bottom right\', \'to top right\' or \'ellipse at center\'' };
9623 }
9624 returner = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1 1\"><" + gradientType + "Gradient id=\"g\" " + gradientDirectionSvg + ">";
9625 for (i = 0; i < stops.length; i += 1) {
9626 if (stops[i] instanceof Expression) {
9627 color = stops[i].value[0];
9628 position = stops[i].value[1];
9629 }
9630 else {
9631 color = stops[i];
9632 position = undefined;
9633 }
9634 if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
9635 throwArgumentDescriptor();
9636 }
9637 positionValue = position ? position.toCSS(renderEnv) : i === 0 ? '0%' : '100%';
9638 alpha = color.alpha;
9639 returner += "<stop offset=\"" + positionValue + "\" stop-color=\"" + color.toRGB() + "\"" + (alpha < 1 ? " stop-opacity=\"" + alpha + "\"" : '') + "/>";
9640 }
9641 returner += "</" + gradientType + "Gradient><rect " + rectangleDimension + " fill=\"url(#g)\" /></svg>";
9642 returner = encodeURIComponent(returner);
9643 returner = "data:image/svg+xml," + returner;
9644 return new URL(new Quoted("'" + returner + "'", returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
9645 } };
9646 });
9647
9648 var isa = function (n, Type) { return (n instanceof Type) ? Keyword.True : Keyword.False; };
9649 var isunit = function (n, unit) {
9650 if (unit === undefined) {
9651 throw { type: 'Argument', message: 'missing the required second argument to isunit.' };
9652 }
9653 unit = typeof unit.value === 'string' ? unit.value : unit;
9654 if (typeof unit !== 'string') {
9655 throw { type: 'Argument', message: 'Second argument to isunit should be a unit or a string.' };
9656 }
9657 return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
9658 };
9659 var types = {
9660 isruleset: function (n) {
9661 return isa(n, DetachedRuleset);
9662 },
9663 iscolor: function (n) {
9664 return isa(n, Color);
9665 },
9666 isnumber: function (n) {
9667 return isa(n, Dimension);
9668 },
9669 isstring: function (n) {
9670 return isa(n, Quoted);
9671 },
9672 iskeyword: function (n) {
9673 return isa(n, Keyword);
9674 },
9675 isurl: function (n) {
9676 return isa(n, URL);
9677 },
9678 ispixel: function (n) {
9679 return isunit(n, 'px');
9680 },
9681 ispercentage: function (n) {
9682 return isunit(n, '%');
9683 },
9684 isem: function (n) {
9685 return isunit(n, 'em');
9686 },
9687 isunit: isunit,
9688 unit: function (val, unit) {
9689 if (!(val instanceof Dimension)) {
9690 throw { type: 'Argument', message: "the first argument to unit must be a number" + (val instanceof Operation ? '. Have you forgotten parenthesis?' : '') };
9691 }
9692 if (unit) {
9693 if (unit instanceof Keyword) {
9694 unit = unit.value;
9695 }
9696 else {
9697 unit = unit.toCSS();
9698 }
9699 }
9700 else {
9701 unit = '';
9702 }
9703 return new Dimension(val.value, unit);
9704 },
9705 'get-unit': function (n) {
9706 return new Anonymous(n.unit);
9707 }
9708 };
9709
9710 var functions = (function (environment) {
9711 var functions = { functionRegistry: functionRegistry, functionCaller: functionCaller };
9712 // register functions
9713 functionRegistry.addMultiple(boolean$1);
9714 functionRegistry.add('default', defaultFunc.eval.bind(defaultFunc));
9715 functionRegistry.addMultiple(color);
9716 functionRegistry.addMultiple(colorBlend);
9717 functionRegistry.addMultiple(dataUri(environment));
9718 functionRegistry.addMultiple(list);
9719 functionRegistry.addMultiple(mathFunctions);
9720 functionRegistry.addMultiple(number$1);
9721 functionRegistry.addMultiple(string);
9722 functionRegistry.addMultiple(svg());
9723 functionRegistry.addMultiple(types);
9724 return functions;
9725 });
9726
9727 function transformTree (root, options) {
9728 options = options || {};
9729 var evaldRoot;
9730 var variables = options.variables;
9731 var evalEnv = new contexts.Eval(options);
9732 //
9733 // Allows setting variables with a hash, so:
9734 //
9735 // `{ color: new tree.Color('#f01') }` will become:
9736 //
9737 // new tree.Declaration('@color',
9738 // new tree.Value([
9739 // new tree.Expression([
9740 // new tree.Color('#f01')
9741 // ])
9742 // ])
9743 // )
9744 //
9745 if (typeof variables === 'object' && !Array.isArray(variables)) {
9746 variables = Object.keys(variables).map(function (k) {
9747 var value = variables[k];
9748 if (!(value instanceof tree.Value)) {
9749 if (!(value instanceof tree.Expression)) {
9750 value = new tree.Expression([value]);
9751 }
9752 value = new tree.Value([value]);
9753 }
9754 return new tree.Declaration("@" + k, value, false, null, 0);
9755 });
9756 evalEnv.frames = [new tree.Ruleset(null, variables)];
9757 }
9758 var visitors$1 = [
9759 new visitors.JoinSelectorVisitor(),
9760 new visitors.MarkVisibleSelectorsVisitor(true),
9761 new visitors.ExtendVisitor(),
9762 new visitors.ToCSSVisitor({ compress: Boolean(options.compress) })
9763 ];
9764 var preEvalVisitors = [];
9765 var v;
9766 var visitorIterator;
9767 /**
9768 * first() / get() allows visitors to be added while visiting
9769 *
9770 * @todo Add scoping for visitors just like functions for @plugin; right now they're global
9771 */
9772 if (options.pluginManager) {
9773 visitorIterator = options.pluginManager.visitor();
9774 for (var i = 0; i < 2; i++) {
9775 visitorIterator.first();
9776 while ((v = visitorIterator.get())) {
9777 if (v.isPreEvalVisitor) {
9778 if (i === 0 || preEvalVisitors.indexOf(v) === -1) {
9779 preEvalVisitors.push(v);
9780 v.run(root);
9781 }
9782 }
9783 else {
9784 if (i === 0 || visitors$1.indexOf(v) === -1) {
9785 if (v.isPreVisitor) {
9786 visitors$1.unshift(v);
9787 }
9788 else {
9789 visitors$1.push(v);
9790 }
9791 }
9792 }
9793 }
9794 }
9795 }
9796 evaldRoot = root.eval(evalEnv);
9797 for (var i = 0; i < visitors$1.length; i++) {
9798 visitors$1[i].run(evaldRoot);
9799 }
9800 // Run any remaining visitors added after eval pass
9801 if (options.pluginManager) {
9802 visitorIterator.first();
9803 while ((v = visitorIterator.get())) {
9804 if (visitors$1.indexOf(v) === -1 && preEvalVisitors.indexOf(v) === -1) {
9805 v.run(evaldRoot);
9806 }
9807 }
9808 }
9809 return evaldRoot;
9810 }
9811
9812 /**
9813 * Plugin Manager
9814 */
9815 var PluginManager = /** @class */ (function () {
9816 function PluginManager(less) {
9817 this.less = less;
9818 this.visitors = [];
9819 this.preProcessors = [];
9820 this.postProcessors = [];
9821 this.installedPlugins = [];
9822 this.fileManagers = [];
9823 this.iterator = -1;
9824 this.pluginCache = {};
9825 this.Loader = new less.PluginLoader(less);
9826 }
9827 /**
9828 * Adds all the plugins in the array
9829 * @param {Array} plugins
9830 */
9831 PluginManager.prototype.addPlugins = function (plugins) {
9832 if (plugins) {
9833 for (var i = 0; i < plugins.length; i++) {
9834 this.addPlugin(plugins[i]);
9835 }
9836 }
9837 };
9838 /**
9839 *
9840 * @param plugin
9841 * @param {String} filename
9842 */
9843 PluginManager.prototype.addPlugin = function (plugin, filename, functionRegistry) {
9844 this.installedPlugins.push(plugin);
9845 if (filename) {
9846 this.pluginCache[filename] = plugin;
9847 }
9848 if (plugin.install) {
9849 plugin.install(this.less, this, functionRegistry || this.less.functions.functionRegistry);
9850 }
9851 };
9852 /**
9853 *
9854 * @param filename
9855 */
9856 PluginManager.prototype.get = function (filename) {
9857 return this.pluginCache[filename];
9858 };
9859 /**
9860 * Adds a visitor. The visitor object has options on itself to determine
9861 * when it should run.
9862 * @param visitor
9863 */
9864 PluginManager.prototype.addVisitor = function (visitor) {
9865 this.visitors.push(visitor);
9866 };
9867 /**
9868 * Adds a pre processor object
9869 * @param {object} preProcessor
9870 * @param {number} priority - guidelines 1 = before import, 1000 = import, 2000 = after import
9871 */
9872 PluginManager.prototype.addPreProcessor = function (preProcessor, priority) {
9873 var indexToInsertAt;
9874 for (indexToInsertAt = 0; indexToInsertAt < this.preProcessors.length; indexToInsertAt++) {
9875 if (this.preProcessors[indexToInsertAt].priority >= priority) {
9876 break;
9877 }
9878 }
9879 this.preProcessors.splice(indexToInsertAt, 0, { preProcessor: preProcessor, priority: priority });
9880 };
9881 /**
9882 * Adds a post processor object
9883 * @param {object} postProcessor
9884 * @param {number} priority - guidelines 1 = before compression, 1000 = compression, 2000 = after compression
9885 */
9886 PluginManager.prototype.addPostProcessor = function (postProcessor, priority) {
9887 var indexToInsertAt;
9888 for (indexToInsertAt = 0; indexToInsertAt < this.postProcessors.length; indexToInsertAt++) {
9889 if (this.postProcessors[indexToInsertAt].priority >= priority) {
9890 break;
9891 }
9892 }
9893 this.postProcessors.splice(indexToInsertAt, 0, { postProcessor: postProcessor, priority: priority });
9894 };
9895 /**
9896 *
9897 * @param manager
9898 */
9899 PluginManager.prototype.addFileManager = function (manager) {
9900 this.fileManagers.push(manager);
9901 };
9902 /**
9903 *
9904 * @returns {Array}
9905 * @private
9906 */
9907 PluginManager.prototype.getPreProcessors = function () {
9908 var preProcessors = [];
9909 for (var i = 0; i < this.preProcessors.length; i++) {
9910 preProcessors.push(this.preProcessors[i].preProcessor);
9911 }
9912 return preProcessors;
9913 };
9914 /**
9915 *
9916 * @returns {Array}
9917 * @private
9918 */
9919 PluginManager.prototype.getPostProcessors = function () {
9920 var postProcessors = [];
9921 for (var i = 0; i < this.postProcessors.length; i++) {
9922 postProcessors.push(this.postProcessors[i].postProcessor);
9923 }
9924 return postProcessors;
9925 };
9926 /**
9927 *
9928 * @returns {Array}
9929 * @private
9930 */
9931 PluginManager.prototype.getVisitors = function () {
9932 return this.visitors;
9933 };
9934 PluginManager.prototype.visitor = function () {
9935 var self = this;
9936 return {
9937 first: function () {
9938 self.iterator = -1;
9939 return self.visitors[self.iterator];
9940 },
9941 get: function () {
9942 self.iterator += 1;
9943 return self.visitors[self.iterator];
9944 }
9945 };
9946 };
9947 /**
9948 *
9949 * @returns {Array}
9950 * @private
9951 */
9952 PluginManager.prototype.getFileManagers = function () {
9953 return this.fileManagers;
9954 };
9955 return PluginManager;
9956 }());
9957 var pm;
9958 var PluginManagerFactory = function (less, newFactory) {
9959 if (newFactory || !pm) {
9960 pm = new PluginManager(less);
9961 }
9962 return pm;
9963 };
9964
9965 function SourceMapOutput (environment) {
9966 var SourceMapOutput = /** @class */ (function () {
9967 function SourceMapOutput(options) {
9968 this._css = [];
9969 this._rootNode = options.rootNode;
9970 this._contentsMap = options.contentsMap;
9971 this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap;
9972 if (options.sourceMapFilename) {
9973 this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/');
9974 }
9975 this._outputFilename = options.outputFilename;
9976 this.sourceMapURL = options.sourceMapURL;
9977 if (options.sourceMapBasepath) {
9978 this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
9979 }
9980 if (options.sourceMapRootpath) {
9981 this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/');
9982 if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') {
9983 this._sourceMapRootpath += '/';
9984 }
9985 }
9986 else {
9987 this._sourceMapRootpath = '';
9988 }
9989 this._outputSourceFiles = options.outputSourceFiles;
9990 this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator();
9991 this._lineNumber = 0;
9992 this._column = 0;
9993 }
9994 SourceMapOutput.prototype.removeBasepath = function (path) {
9995 if (this._sourceMapBasepath && path.indexOf(this._sourceMapBasepath) === 0) {
9996 path = path.substring(this._sourceMapBasepath.length);
9997 if (path.charAt(0) === '\\' || path.charAt(0) === '/') {
9998 path = path.substring(1);
9999 }
10000 }
10001 return path;
10002 };
10003 SourceMapOutput.prototype.normalizeFilename = function (filename) {
10004 filename = filename.replace(/\\/g, '/');
10005 filename = this.removeBasepath(filename);
10006 return (this._sourceMapRootpath || '') + filename;
10007 };
10008 SourceMapOutput.prototype.add = function (chunk, fileInfo, index, mapLines) {
10009 // ignore adding empty strings
10010 if (!chunk) {
10011 return;
10012 }
10013 var lines, sourceLines, columns, sourceColumns, i;
10014 if (fileInfo && fileInfo.filename) {
10015 var inputSource = this._contentsMap[fileInfo.filename];
10016 // remove vars/banner added to the top of the file
10017 if (this._contentsIgnoredCharsMap[fileInfo.filename]) {
10018 // adjust the index
10019 index -= this._contentsIgnoredCharsMap[fileInfo.filename];
10020 if (index < 0) {
10021 index = 0;
10022 }
10023 // adjust the source
10024 inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]);
10025 }
10026 /**
10027 * ignore empty content, or failsafe
10028 * if contents map is incorrect
10029 */
10030 if (inputSource === undefined) {
10031 this._css.push(chunk);
10032 return;
10033 }
10034 inputSource = inputSource.substring(0, index);
10035 sourceLines = inputSource.split('\n');
10036 sourceColumns = sourceLines[sourceLines.length - 1];
10037 }
10038 lines = chunk.split('\n');
10039 columns = lines[lines.length - 1];
10040 if (fileInfo && fileInfo.filename) {
10041 if (!mapLines) {
10042 this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column },
10043 original: { line: sourceLines.length, column: sourceColumns.length },
10044 source: this.normalizeFilename(fileInfo.filename) });
10045 }
10046 else {
10047 for (i = 0; i < lines.length; i++) {
10048 this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0 },
10049 original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0 },
10050 source: this.normalizeFilename(fileInfo.filename) });
10051 }
10052 }
10053 }
10054 if (lines.length === 1) {
10055 this._column += columns.length;
10056 }
10057 else {
10058 this._lineNumber += lines.length - 1;
10059 this._column = columns.length;
10060 }
10061 this._css.push(chunk);
10062 };
10063 SourceMapOutput.prototype.isEmpty = function () {
10064 return this._css.length === 0;
10065 };
10066 SourceMapOutput.prototype.toCSS = function (context) {
10067 this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
10068 if (this._outputSourceFiles) {
10069 for (var filename in this._contentsMap) {
10070 if (this._contentsMap.hasOwnProperty(filename)) {
10071 var source = this._contentsMap[filename];
10072 if (this._contentsIgnoredCharsMap[filename]) {
10073 source = source.slice(this._contentsIgnoredCharsMap[filename]);
10074 }
10075 this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
10076 }
10077 }
10078 }
10079 this._rootNode.genCSS(context, this);
10080 if (this._css.length > 0) {
10081 var sourceMapURL = void 0;
10082 var sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
10083 if (this.sourceMapURL) {
10084 sourceMapURL = this.sourceMapURL;
10085 }
10086 else if (this._sourceMapFilename) {
10087 sourceMapURL = this._sourceMapFilename;
10088 }
10089 this.sourceMapURL = sourceMapURL;
10090 this.sourceMap = sourceMapContent;
10091 }
10092 return this._css.join('');
10093 };
10094 return SourceMapOutput;
10095 }());
10096 return SourceMapOutput;
10097 }
10098
10099 function SourceMapBuilder (SourceMapOutput, environment) {
10100 var SourceMapBuilder = /** @class */ (function () {
10101 function SourceMapBuilder(options) {
10102 this.options = options;
10103 }
10104 SourceMapBuilder.prototype.toCSS = function (rootNode, options, imports) {
10105 var sourceMapOutput = new SourceMapOutput({
10106 contentsIgnoredCharsMap: imports.contentsIgnoredChars,
10107 rootNode: rootNode,
10108 contentsMap: imports.contents,
10109 sourceMapFilename: this.options.sourceMapFilename,
10110 sourceMapURL: this.options.sourceMapURL,
10111 outputFilename: this.options.sourceMapOutputFilename,
10112 sourceMapBasepath: this.options.sourceMapBasepath,
10113 sourceMapRootpath: this.options.sourceMapRootpath,
10114 outputSourceFiles: this.options.outputSourceFiles,
10115 sourceMapGenerator: this.options.sourceMapGenerator,
10116 sourceMapFileInline: this.options.sourceMapFileInline,
10117 disableSourcemapAnnotation: this.options.disableSourcemapAnnotation
10118 });
10119 var css = sourceMapOutput.toCSS(options);
10120 this.sourceMap = sourceMapOutput.sourceMap;
10121 this.sourceMapURL = sourceMapOutput.sourceMapURL;
10122 if (this.options.sourceMapInputFilename) {
10123 this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename);
10124 }
10125 if (this.options.sourceMapBasepath !== undefined && this.sourceMapURL !== undefined) {
10126 this.sourceMapURL = sourceMapOutput.removeBasepath(this.sourceMapURL);
10127 }
10128 return css + this.getCSSAppendage();
10129 };
10130 SourceMapBuilder.prototype.getCSSAppendage = function () {
10131 var sourceMapURL = this.sourceMapURL;
10132 if (this.options.sourceMapFileInline) {
10133 if (this.sourceMap === undefined) {
10134 return '';
10135 }
10136 sourceMapURL = "data:application/json;base64," + environment.encodeBase64(this.sourceMap);
10137 }
10138 if (this.options.disableSourcemapAnnotation) {
10139 return '';
10140 }
10141 if (sourceMapURL) {
10142 return "/*# sourceMappingURL=" + sourceMapURL + " */";
10143 }
10144 return '';
10145 };
10146 SourceMapBuilder.prototype.getExternalSourceMap = function () {
10147 return this.sourceMap;
10148 };
10149 SourceMapBuilder.prototype.setExternalSourceMap = function (sourceMap) {
10150 this.sourceMap = sourceMap;
10151 };
10152 SourceMapBuilder.prototype.isInline = function () {
10153 return this.options.sourceMapFileInline;
10154 };
10155 SourceMapBuilder.prototype.getSourceMapURL = function () {
10156 return this.sourceMapURL;
10157 };
10158 SourceMapBuilder.prototype.getOutputFilename = function () {
10159 return this.options.sourceMapOutputFilename;
10160 };
10161 SourceMapBuilder.prototype.getInputFilename = function () {
10162 return this.sourceMapInputFilename;
10163 };
10164 return SourceMapBuilder;
10165 }());
10166 return SourceMapBuilder;
10167 }
10168
10169 function ParseTree (SourceMapBuilder) {
10170 var ParseTree = /** @class */ (function () {
10171 function ParseTree(root, imports) {
10172 this.root = root;
10173 this.imports = imports;
10174 }
10175 ParseTree.prototype.toCSS = function (options) {
10176 var evaldRoot;
10177 var result = {};
10178 var sourceMapBuilder;
10179 try {
10180 evaldRoot = transformTree(this.root, options);
10181 }
10182 catch (e) {
10183 throw new LessError(e, this.imports);
10184 }
10185 try {
10186 var compress = Boolean(options.compress);
10187 if (compress) {
10188 logger.warn('The compress option has been deprecated. ' +
10189 'We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.');
10190 }
10191 var toCSSOptions = {
10192 compress: compress,
10193 dumpLineNumbers: options.dumpLineNumbers,
10194 strictUnits: Boolean(options.strictUnits),
10195 numPrecision: 8
10196 };
10197 if (options.sourceMap) {
10198 sourceMapBuilder = new SourceMapBuilder(options.sourceMap);
10199 result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports);
10200 }
10201 else {
10202 result.css = evaldRoot.toCSS(toCSSOptions);
10203 }
10204 }
10205 catch (e) {
10206 throw new LessError(e, this.imports);
10207 }
10208 if (options.pluginManager) {
10209 var postProcessors = options.pluginManager.getPostProcessors();
10210 for (var i = 0; i < postProcessors.length; i++) {
10211 result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports });
10212 }
10213 }
10214 if (options.sourceMap) {
10215 result.map = sourceMapBuilder.getExternalSourceMap();
10216 }
10217 result.imports = [];
10218 for (var file in this.imports.files) {
10219 if (this.imports.files.hasOwnProperty(file) && file !== this.imports.rootFilename) {
10220 result.imports.push(file);
10221 }
10222 }
10223 return result;
10224 };
10225 return ParseTree;
10226 }());
10227 return ParseTree;
10228 }
10229
10230 function ImportManager (environment) {
10231 // FileInfo = {
10232 // 'rewriteUrls' - option - whether to adjust URL's to be relative
10233 // 'filename' - full resolved filename of current file
10234 // 'rootpath' - path to append to normal URLs for this node
10235 // 'currentDirectory' - path to the current file, absolute
10236 // 'rootFilename' - filename of the base file
10237 // 'entryPath' - absolute path to the entry file
10238 // 'reference' - whether the file should not be output and only output parts that are referenced
10239 var ImportManager = /** @class */ (function () {
10240 function ImportManager(less, context, rootFileInfo) {
10241 this.less = less;
10242 this.rootFilename = rootFileInfo.filename;
10243 this.paths = context.paths || []; // Search paths, when importing
10244 this.contents = {}; // map - filename to contents of all the files
10245 this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore
10246 this.mime = context.mime;
10247 this.error = null;
10248 this.context = context;
10249 // Deprecated? Unused outside of here, could be useful.
10250 this.queue = []; // Files which haven't been imported yet
10251 this.files = {}; // Holds the imported parse trees.
10252 }
10253 /**
10254 * Add an import to be imported
10255 * @param path - the raw path
10256 * @param tryAppendExtension - whether to try appending a file extension (.less or .js if the path has no extension)
10257 * @param currentFileInfo - the current file info (used for instance to work out relative paths)
10258 * @param importOptions - import options
10259 * @param callback - callback for when it is imported
10260 */
10261 ImportManager.prototype.push = function (path, tryAppendExtension, currentFileInfo, importOptions, callback) {
10262 var importManager = this, pluginLoader = this.context.pluginManager.Loader;
10263 this.queue.push(path);
10264 var fileParsedFunc = function (e, root, fullPath) {
10265 importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
10266 var importedEqualsRoot = fullPath === importManager.rootFilename;
10267 if (importOptions.optional && e) {
10268 callback(null, { rules: [] }, false, null);
10269 logger.info("The file " + fullPath + " was skipped because it was not found and the import was marked optional.");
10270 }
10271 else {
10272 // Inline imports aren't cached here.
10273 // If we start to cache them, please make sure they won't conflict with non-inline imports of the
10274 // same name as they used to do before this comment and the condition below have been added.
10275 if (!importManager.files[fullPath] && !importOptions.inline) {
10276 importManager.files[fullPath] = { root: root, options: importOptions };
10277 }
10278 if (e && !importManager.error) {
10279 importManager.error = e;
10280 }
10281 callback(e, root, importedEqualsRoot, fullPath);
10282 }
10283 };
10284 var newFileInfo = {
10285 rewriteUrls: this.context.rewriteUrls,
10286 entryPath: currentFileInfo.entryPath,
10287 rootpath: currentFileInfo.rootpath,
10288 rootFilename: currentFileInfo.rootFilename
10289 };
10290 var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment);
10291 if (!fileManager) {
10292 fileParsedFunc({ message: "Could not find a file-manager for " + path });
10293 return;
10294 }
10295 var loadFileCallback = function (loadedFile) {
10296 var plugin;
10297 var resolvedFilename = loadedFile.filename;
10298 var contents = loadedFile.contents.replace(/^\uFEFF/, '');
10299 // Pass on an updated rootpath if path of imported file is relative and file
10300 // is in a (sub|sup) directory
10301 //
10302 // Examples:
10303 // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
10304 // then rootpath should become 'less/module/nav/'
10305 // - If path of imported file is '../mixins.less' and rootpath is 'less/',
10306 // then rootpath should become 'less/../'
10307 newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
10308 if (newFileInfo.rewriteUrls) {
10309 newFileInfo.rootpath = fileManager.join((importManager.context.rootpath || ''), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
10310 if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
10311 newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
10312 }
10313 }
10314 newFileInfo.filename = resolvedFilename;
10315 var newEnv = new contexts.Parse(importManager.context);
10316 newEnv.processImports = false;
10317 importManager.contents[resolvedFilename] = contents;
10318 if (currentFileInfo.reference || importOptions.reference) {
10319 newFileInfo.reference = true;
10320 }
10321 if (importOptions.isPlugin) {
10322 plugin = pluginLoader.evalPlugin(contents, newEnv, importManager, importOptions.pluginArgs, newFileInfo);
10323 if (plugin instanceof LessError) {
10324 fileParsedFunc(plugin, null, resolvedFilename);
10325 }
10326 else {
10327 fileParsedFunc(null, plugin, resolvedFilename);
10328 }
10329 }
10330 else if (importOptions.inline) {
10331 fileParsedFunc(null, contents, resolvedFilename);
10332 }
10333 else {
10334 // import (multiple) parse trees apparently get altered and can't be cached.
10335 // TODO: investigate why this is
10336 if (importManager.files[resolvedFilename]
10337 && !importManager.files[resolvedFilename].options.multiple
10338 && !importOptions.multiple) {
10339 fileParsedFunc(null, importManager.files[resolvedFilename].root, resolvedFilename);
10340 }
10341 else {
10342 new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) {
10343 fileParsedFunc(e, root, resolvedFilename);
10344 });
10345 }
10346 }
10347 };
10348 var loadedFile;
10349 var promise;
10350 var context = clone(this.context);
10351 if (tryAppendExtension) {
10352 context.ext = importOptions.isPlugin ? '.js' : '.less';
10353 }
10354 if (importOptions.isPlugin) {
10355 context.mime = 'application/javascript';
10356 if (context.syncImport) {
10357 loadedFile = pluginLoader.loadPluginSync(path, currentFileInfo.currentDirectory, context, environment, fileManager);
10358 }
10359 else {
10360 promise = pluginLoader.loadPlugin(path, currentFileInfo.currentDirectory, context, environment, fileManager);
10361 }
10362 }
10363 else {
10364 if (context.syncImport) {
10365 loadedFile = fileManager.loadFileSync(path, currentFileInfo.currentDirectory, context, environment);
10366 }
10367 else {
10368 promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, context, environment, function (err, loadedFile) {
10369 if (err) {
10370 fileParsedFunc(err);
10371 }
10372 else {
10373 loadFileCallback(loadedFile);
10374 }
10375 });
10376 }
10377 }
10378 if (loadedFile) {
10379 if (!loadedFile.filename) {
10380 fileParsedFunc(loadedFile);
10381 }
10382 else {
10383 loadFileCallback(loadedFile);
10384 }
10385 }
10386 else if (promise) {
10387 promise.then(loadFileCallback, fileParsedFunc);
10388 }
10389 };
10390 return ImportManager;
10391 }());
10392 return ImportManager;
10393 }
10394
10395 function Parse (environment, ParseTree, ImportManager) {
10396 var parse = function (input, options, callback) {
10397 if (typeof options === 'function') {
10398 callback = options;
10399 options = copyOptions(this.options, {});
10400 }
10401 else {
10402 options = copyOptions(this.options, options || {});
10403 }
10404 if (!callback) {
10405 var self_1 = this;
10406 return new Promise(function (resolve, reject) {
10407 parse.call(self_1, input, options, function (err, output) {
10408 if (err) {
10409 reject(err);
10410 }
10411 else {
10412 resolve(output);
10413 }
10414 });
10415 });
10416 }
10417 else {
10418 var context_1;
10419 var rootFileInfo = void 0;
10420 var pluginManager_1 = new PluginManagerFactory(this, !options.reUsePluginManager);
10421 options.pluginManager = pluginManager_1;
10422 context_1 = new contexts.Parse(options);
10423 if (options.rootFileInfo) {
10424 rootFileInfo = options.rootFileInfo;
10425 }
10426 else {
10427 var filename = options.filename || 'input';
10428 var entryPath = filename.replace(/[^\/\\]*$/, '');
10429 rootFileInfo = {
10430 filename: filename,
10431 rewriteUrls: context_1.rewriteUrls,
10432 rootpath: context_1.rootpath || '',
10433 currentDirectory: entryPath,
10434 entryPath: entryPath,
10435 rootFilename: filename
10436 };
10437 // add in a missing trailing slash
10438 if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== '/') {
10439 rootFileInfo.rootpath += '/';
10440 }
10441 }
10442 var imports_1 = new ImportManager(this, context_1, rootFileInfo);
10443 this.importManager = imports_1;
10444 // TODO: allow the plugins to be just a list of paths or names
10445 // Do an async plugin queue like lessc
10446 if (options.plugins) {
10447 options.plugins.forEach(function (plugin) {
10448 var evalResult, contents;
10449 if (plugin.fileContent) {
10450 contents = plugin.fileContent.replace(/^\uFEFF/, '');
10451 evalResult = pluginManager_1.Loader.evalPlugin(contents, context_1, imports_1, plugin.options, plugin.filename);
10452 if (evalResult instanceof LessError) {
10453 return callback(evalResult);
10454 }
10455 }
10456 else {
10457 pluginManager_1.addPlugin(plugin);
10458 }
10459 });
10460 }
10461 new Parser(context_1, imports_1, rootFileInfo)
10462 .parse(input, function (e, root) {
10463 if (e) {
10464 return callback(e);
10465 }
10466 callback(null, root, imports_1, options);
10467 }, options);
10468 }
10469 };
10470 return parse;
10471 }
10472
10473 function Render (environment, ParseTree, ImportManager) {
10474 var render = function (input, options, callback) {
10475 if (typeof options === 'function') {
10476 callback = options;
10477 options = copyOptions(this.options, {});
10478 }
10479 else {
10480 options = copyOptions(this.options, options || {});
10481 }
10482 if (!callback) {
10483 var self_1 = this;
10484 return new Promise(function (resolve, reject) {
10485 render.call(self_1, input, options, function (err, output) {
10486 if (err) {
10487 reject(err);
10488 }
10489 else {
10490 resolve(output);
10491 }
10492 });
10493 });
10494 }
10495 else {
10496 this.parse(input, options, function (err, root, imports, options) {
10497 if (err) {
10498 return callback(err);
10499 }
10500 var result;
10501 try {
10502 var parseTree = new ParseTree(root, imports);
10503 result = parseTree.toCSS(options);
10504 }
10505 catch (err) {
10506 return callback(err);
10507 }
10508 callback(null, result);
10509 });
10510 }
10511 };
10512 return render;
10513 }
10514
10515 var version = "4.1.1";
10516
10517 function parseNodeVersion(version) {
10518 var match = version.match(/^v(\d{1,2})\.(\d{1,2})\.(\d{1,2})(?:-([0-9A-Za-z-.]+))?(?:\+([0-9A-Za-z-.]+))?$/); // eslint-disable-line max-len
10519 if (!match) {
10520 throw new Error('Unable to parse: ' + version);
10521 }
10522
10523 var res = {
10524 major: parseInt(match[1], 10),
10525 minor: parseInt(match[2], 10),
10526 patch: parseInt(match[3], 10),
10527 pre: match[4] || '',
10528 build: match[5] || '',
10529 };
10530
10531 return res;
10532 }
10533
10534 var parseNodeVersion_1 = parseNodeVersion;
10535
10536 function lessRoot (environment, fileManagers) {
10537 var sourceMapOutput, sourceMapBuilder, parseTree, importManager;
10538 environment = new Environment(environment, fileManagers);
10539 sourceMapOutput = SourceMapOutput(environment);
10540 sourceMapBuilder = SourceMapBuilder(sourceMapOutput, environment);
10541 parseTree = ParseTree(sourceMapBuilder);
10542 importManager = ImportManager(environment);
10543 var render = Render(environment, parseTree);
10544 var parse = Parse(environment, parseTree, importManager);
10545 var v = parseNodeVersion_1("v" + version);
10546 var initial = {
10547 version: [v.major, v.minor, v.patch],
10548 data: data,
10549 tree: tree,
10550 Environment: Environment,
10551 AbstractFileManager: AbstractFileManager,
10552 AbstractPluginLoader: AbstractPluginLoader,
10553 environment: environment,
10554 visitors: visitors,
10555 Parser: Parser,
10556 functions: functions(environment),
10557 contexts: contexts,
10558 SourceMapOutput: sourceMapOutput,
10559 SourceMapBuilder: sourceMapBuilder,
10560 ParseTree: parseTree,
10561 ImportManager: importManager,
10562 render: render,
10563 parse: parse,
10564 LessError: LessError,
10565 transformTree: transformTree,
10566 utils: utils,
10567 PluginManager: PluginManagerFactory,
10568 logger: logger
10569 };
10570 // Create a public API
10571 var ctor = function (t) {
10572 return function () {
10573 var obj = Object.create(t.prototype);
10574 t.apply(obj, Array.prototype.slice.call(arguments, 0));
10575 return obj;
10576 };
10577 };
10578 var t;
10579 var api = Object.create(initial);
10580 for (var n in initial.tree) {
10581 /* eslint guard-for-in: 0 */
10582 t = initial.tree[n];
10583 if (typeof t === 'function') {
10584 api[n.toLowerCase()] = ctor(t);
10585 }
10586 else {
10587 api[n] = Object.create(null);
10588 for (var o in t) {
10589 /* eslint guard-for-in: 0 */
10590 api[n][o.toLowerCase()] = ctor(t[o]);
10591 }
10592 }
10593 }
10594 /**
10595 * Some of the functions assume a `this` context of the API object,
10596 * which causes it to fail when wrapped for ES6 imports.
10597 *
10598 * An assumed `this` should be removed in the future.
10599 */
10600 initial.parse = initial.parse.bind(api);
10601 initial.render = initial.render.bind(api);
10602 return api;
10603 }
10604
10605 /* global window, XMLHttpRequest */
10606 var options;
10607 var logger$1;
10608 var fileCache = {};
10609 // TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
10610 var FileManager = function () { };
10611 FileManager.prototype = Object.assign(new AbstractFileManager(), {
10612 alwaysMakePathsAbsolute: function () {
10613 return true;
10614 },
10615 join: function (basePath, laterPath) {
10616 if (!basePath) {
10617 return laterPath;
10618 }
10619 return this.extractUrlParts(laterPath, basePath).path;
10620 },
10621 doXHR: function (url, type, callback, errback) {
10622 var xhr = new XMLHttpRequest();
10623 var async = options.isFileProtocol ? options.fileAsync : true;
10624 if (typeof xhr.overrideMimeType === 'function') {
10625 xhr.overrideMimeType('text/css');
10626 }
10627 logger$1.debug("XHR: Getting '" + url + "'");
10628 xhr.open('GET', url, async);
10629 xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
10630 xhr.send(null);
10631 function handleResponse(xhr, callback, errback) {
10632 if (xhr.status >= 200 && xhr.status < 300) {
10633 callback(xhr.responseText, xhr.getResponseHeader('Last-Modified'));
10634 }
10635 else if (typeof errback === 'function') {
10636 errback(xhr.status, url);
10637 }
10638 }
10639 if (options.isFileProtocol && !options.fileAsync) {
10640 if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
10641 callback(xhr.responseText);
10642 }
10643 else {
10644 errback(xhr.status, url);
10645 }
10646 }
10647 else if (async) {
10648 xhr.onreadystatechange = function () {
10649 if (xhr.readyState == 4) {
10650 handleResponse(xhr, callback, errback);
10651 }
10652 };
10653 }
10654 else {
10655 handleResponse(xhr, callback, errback);
10656 }
10657 },
10658 supports: function () {
10659 return true;
10660 },
10661 clearFileCache: function () {
10662 fileCache = {};
10663 },
10664 loadFile: function (filename, currentDirectory, options, environment) {
10665 // TODO: Add prefix support like less-node?
10666 // What about multiple paths?
10667 if (currentDirectory && !this.isPathAbsolute(filename)) {
10668 filename = currentDirectory + filename;
10669 }
10670 filename = options.ext ? this.tryAppendExtension(filename, options.ext) : filename;
10671 options = options || {};
10672 // sheet may be set to the stylesheet for the initial load or a collection of properties including
10673 // some context variables for imports
10674 var hrefParts = this.extractUrlParts(filename, window.location.href);
10675 var href = hrefParts.url;
10676 var self = this;
10677 return new Promise(function (resolve, reject) {
10678 if (options.useFileCache && fileCache[href]) {
10679 try {
10680 var lessText = fileCache[href];
10681 return resolve({ contents: lessText, filename: href, webInfo: { lastModified: new Date() } });
10682 }
10683 catch (e) {
10684 return reject({ filename: href, message: "Error loading file " + href + " error was " + e.message });
10685 }
10686 }
10687 self.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
10688 // per file cache
10689 fileCache[href] = data;
10690 // Use remote copy (re-parse)
10691 resolve({ contents: data, filename: href, webInfo: { lastModified: lastModified } });
10692 }, function doXHRError(status, url) {
10693 reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href });
10694 });
10695 });
10696 }
10697 });
10698 var FM = (function (opts, log) {
10699 options = opts;
10700 logger$1 = log;
10701 return FileManager;
10702 });
10703
10704 // TODO: Add tests for browser @plugin
10705 /**
10706 * Browser Plugin Loader
10707 */
10708 var PluginLoader = function (less) {
10709 this.less = less;
10710 // Should we shim this.require for browser? Probably not?
10711 };
10712 PluginLoader.prototype = Object.assign(new AbstractPluginLoader(), {
10713 loadPlugin: function (filename, basePath, context, environment, fileManager) {
10714 return new Promise(function (fulfill, reject) {
10715 fileManager.loadFile(filename, basePath, context, environment)
10716 .then(fulfill).catch(reject);
10717 });
10718 }
10719 });
10720
10721 var LogListener = (function (less, options) {
10722 var logLevel_debug = 4;
10723 var logLevel_info = 3;
10724 var logLevel_warn = 2;
10725 var logLevel_error = 1;
10726 // The amount of logging in the javascript console.
10727 // 3 - Debug, information and errors
10728 // 2 - Information and errors
10729 // 1 - Errors
10730 // 0 - None
10731 // Defaults to 2
10732 options.logLevel = typeof options.logLevel !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_info : logLevel_error);
10733 if (!options.loggers) {
10734 options.loggers = [{
10735 debug: function (msg) {
10736 if (options.logLevel >= logLevel_debug) {
10737 console.log(msg);
10738 }
10739 },
10740 info: function (msg) {
10741 if (options.logLevel >= logLevel_info) {
10742 console.log(msg);
10743 }
10744 },
10745 warn: function (msg) {
10746 if (options.logLevel >= logLevel_warn) {
10747 console.warn(msg);
10748 }
10749 },
10750 error: function (msg) {
10751 if (options.logLevel >= logLevel_error) {
10752 console.error(msg);
10753 }
10754 }
10755 }];
10756 }
10757 for (var i = 0; i < options.loggers.length; i++) {
10758 less.logger.addListener(options.loggers[i]);
10759 }
10760 });
10761
10762 var ErrorReporting = (function (window, less, options) {
10763 function errorHTML(e, rootHref) {
10764 var id = "less-error-message:" + extractId(rootHref || '');
10765 var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
10766 var elem = window.document.createElement('div');
10767 var timer;
10768 var content;
10769 var errors = [];
10770 var filename = e.filename || rootHref;
10771 var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1];
10772 elem.id = id;
10773 elem.className = 'less-error-message';
10774 content = "<h3>" + (e.type || 'Syntax') + "Error: " + (e.message || 'There is an error in your .less file') +
10775 ("</h3><p>in <a href=\"" + filename + "\">" + filenameNoPath + "</a> ");
10776 var errorline = function (e, i, classname) {
10777 if (e.extract[i] !== undefined) {
10778 errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
10779 .replace(/\{class\}/, classname)
10780 .replace(/\{content\}/, e.extract[i]));
10781 }
10782 };
10783 if (e.line) {
10784 errorline(e, 0, '');
10785 errorline(e, 1, 'line');
10786 errorline(e, 2, '');
10787 content += "on line " + e.line + ", column " + (e.column + 1) + ":</p><ul>" + errors.join('') + "</ul>";
10788 }
10789 if (e.stack && (e.extract || options.logLevel >= 4)) {
10790 content += "<br/>Stack Trace</br />" + e.stack.split('\n').slice(1).join('<br/>');
10791 }
10792 elem.innerHTML = content;
10793 // CSS for error messages
10794 browser.createCSS(window.document, [
10795 '.less-error-message ul, .less-error-message li {',
10796 'list-style-type: none;',
10797 'margin-right: 15px;',
10798 'padding: 4px 0;',
10799 'margin: 0;',
10800 '}',
10801 '.less-error-message label {',
10802 'font-size: 12px;',
10803 'margin-right: 15px;',
10804 'padding: 4px 0;',
10805 'color: #cc7777;',
10806 '}',
10807 '.less-error-message pre {',
10808 'color: #dd6666;',
10809 'padding: 4px 0;',
10810 'margin: 0;',
10811 'display: inline-block;',
10812 '}',
10813 '.less-error-message pre.line {',
10814 'color: #ff0000;',
10815 '}',
10816 '.less-error-message h3 {',
10817 'font-size: 20px;',
10818 'font-weight: bold;',
10819 'padding: 15px 0 5px 0;',
10820 'margin: 0;',
10821 '}',
10822 '.less-error-message a {',
10823 'color: #10a',
10824 '}',
10825 '.less-error-message .error {',
10826 'color: red;',
10827 'font-weight: bold;',
10828 'padding-bottom: 2px;',
10829 'border-bottom: 1px dashed red;',
10830 '}'
10831 ].join('\n'), { title: 'error-message' });
10832 elem.style.cssText = [
10833 'font-family: Arial, sans-serif',
10834 'border: 1px solid #e00',
10835 'background-color: #eee',
10836 'border-radius: 5px',
10837 '-webkit-border-radius: 5px',
10838 '-moz-border-radius: 5px',
10839 'color: #e00',
10840 'padding: 15px',
10841 'margin-bottom: 15px'
10842 ].join(';');
10843 if (options.env === 'development') {
10844 timer = setInterval(function () {
10845 var document = window.document;
10846 var body = document.body;
10847 if (body) {
10848 if (document.getElementById(id)) {
10849 body.replaceChild(elem, document.getElementById(id));
10850 }
10851 else {
10852 body.insertBefore(elem, body.firstChild);
10853 }
10854 clearInterval(timer);
10855 }
10856 }, 10);
10857 }
10858 }
10859 function removeErrorHTML(path) {
10860 var node = window.document.getElementById("less-error-message:" + extractId(path));
10861 if (node) {
10862 node.parentNode.removeChild(node);
10863 }
10864 }
10865 function removeError(path) {
10866 if (!options.errorReporting || options.errorReporting === 'html') {
10867 removeErrorHTML(path);
10868 }
10869 else if (options.errorReporting === 'console') ;
10870 else if (typeof options.errorReporting === 'function') {
10871 options.errorReporting('remove', path);
10872 }
10873 }
10874 function errorConsole(e, rootHref) {
10875 var template = '{line} {content}';
10876 var filename = e.filename || rootHref;
10877 var errors = [];
10878 var content = (e.type || 'Syntax') + "Error: " + (e.message || 'There is an error in your .less file') + " in " + filename;
10879 var errorline = function (e, i, classname) {
10880 if (e.extract[i] !== undefined) {
10881 errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
10882 .replace(/\{class\}/, classname)
10883 .replace(/\{content\}/, e.extract[i]));
10884 }
10885 };
10886 if (e.line) {
10887 errorline(e, 0, '');
10888 errorline(e, 1, 'line');
10889 errorline(e, 2, '');
10890 content += " on line " + e.line + ", column " + (e.column + 1) + ":\n" + errors.join('\n');
10891 }
10892 if (e.stack && (e.extract || options.logLevel >= 4)) {
10893 content += "\nStack Trace\n" + e.stack;
10894 }
10895 less.logger.error(content);
10896 }
10897 function error(e, rootHref) {
10898 if (!options.errorReporting || options.errorReporting === 'html') {
10899 errorHTML(e, rootHref);
10900 }
10901 else if (options.errorReporting === 'console') {
10902 errorConsole(e, rootHref);
10903 }
10904 else if (typeof options.errorReporting === 'function') {
10905 options.errorReporting('add', e, rootHref);
10906 }
10907 }
10908 return {
10909 add: error,
10910 remove: removeError
10911 };
10912 });
10913
10914 // Cache system is a bit outdated and could do with work
10915 var Cache = (function (window, options, logger) {
10916 var cache = null;
10917 if (options.env !== 'development') {
10918 try {
10919 cache = (typeof window.localStorage === 'undefined') ? null : window.localStorage;
10920 }
10921 catch (_) { }
10922 }
10923 return {
10924 setCSS: function (path, lastModified, modifyVars, styles) {
10925 if (cache) {
10926 logger.info("saving " + path + " to cache.");
10927 try {
10928 cache.setItem(path, styles);
10929 cache.setItem(path + ":timestamp", lastModified);
10930 if (modifyVars) {
10931 cache.setItem(path + ":vars", JSON.stringify(modifyVars));
10932 }
10933 }
10934 catch (e) {
10935 // TODO - could do with adding more robust error handling
10936 logger.error("failed to save \"" + path + "\" to local storage for caching.");
10937 }
10938 }
10939 },
10940 getCSS: function (path, webInfo, modifyVars) {
10941 var css = cache && cache.getItem(path);
10942 var timestamp = cache && cache.getItem(path + ":timestamp");
10943 var vars = cache && cache.getItem(path + ":vars");
10944 modifyVars = modifyVars || {};
10945 vars = vars || "{}"; // if not set, treat as the JSON representation of an empty object
10946 if (timestamp && webInfo.lastModified &&
10947 (new Date(webInfo.lastModified).valueOf() ===
10948 new Date(timestamp).valueOf()) &&
10949 JSON.stringify(modifyVars) === vars) {
10950 // Use local copy
10951 return css;
10952 }
10953 }
10954 };
10955 });
10956
10957 var ImageSize = (function () {
10958 function imageSize() {
10959 throw {
10960 type: 'Runtime',
10961 message: 'Image size functions are not supported in browser version of less'
10962 };
10963 }
10964 var imageFunctions = {
10965 'image-size': function (filePathNode) {
10966 imageSize();
10967 return -1;
10968 },
10969 'image-width': function (filePathNode) {
10970 imageSize();
10971 return -1;
10972 },
10973 'image-height': function (filePathNode) {
10974 imageSize();
10975 return -1;
10976 }
10977 };
10978 functionRegistry.addMultiple(imageFunctions);
10979 });
10980
10981 //
10982 var root = (function (window, options) {
10983 var document = window.document;
10984 var less = lessRoot();
10985 less.options = options;
10986 var environment = less.environment;
10987 var FileManager = FM(options, less.logger);
10988 var fileManager = new FileManager();
10989 environment.addFileManager(fileManager);
10990 less.FileManager = FileManager;
10991 less.PluginLoader = PluginLoader;
10992 LogListener(less, options);
10993 var errors = ErrorReporting(window, less, options);
10994 var cache = less.cache = options.cache || Cache(window, options, less.logger);
10995 ImageSize(less.environment);
10996 // Setup user functions - Deprecate?
10997 if (options.functions) {
10998 less.functions.functionRegistry.addMultiple(options.functions);
10999 }
11000 var typePattern = /^text\/(x-)?less$/;
11001 function clone(obj) {
11002 var cloned = {};
11003 for (var prop in obj) {
11004 if (obj.hasOwnProperty(prop)) {
11005 cloned[prop] = obj[prop];
11006 }
11007 }
11008 return cloned;
11009 }
11010 // only really needed for phantom
11011 function bind(func, thisArg) {
11012 var curryArgs = Array.prototype.slice.call(arguments, 2);
11013 return function () {
11014 var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0));
11015 return func.apply(thisArg, args);
11016 };
11017 }
11018 function loadStyles(modifyVars) {
11019 var styles = document.getElementsByTagName('style');
11020 var style;
11021 for (var i = 0; i < styles.length; i++) {
11022 style = styles[i];
11023 if (style.type.match(typePattern)) {
11024 var instanceOptions = clone(options);
11025 instanceOptions.modifyVars = modifyVars;
11026 var lessText = style.innerHTML || '';
11027 instanceOptions.filename = document.location.href.replace(/#.*$/, '');
11028 /* jshint loopfunc:true */
11029 // use closure to store current style
11030 less.render(lessText, instanceOptions, bind(function (style, e, result) {
11031 if (e) {
11032 errors.add(e, 'inline');
11033 }
11034 else {
11035 style.type = 'text/css';
11036 if (style.styleSheet) {
11037 style.styleSheet.cssText = result.css;
11038 }
11039 else {
11040 style.innerHTML = result.css;
11041 }
11042 }
11043 }, null, style));
11044 }
11045 }
11046 }
11047 function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
11048 var instanceOptions = clone(options);
11049 addDataAttr(instanceOptions, sheet);
11050 instanceOptions.mime = sheet.type;
11051 if (modifyVars) {
11052 instanceOptions.modifyVars = modifyVars;
11053 }
11054 function loadInitialFileCallback(loadedFile) {
11055 var data = loadedFile.contents;
11056 var path = loadedFile.filename;
11057 var webInfo = loadedFile.webInfo;
11058 var newFileInfo = {
11059 currentDirectory: fileManager.getPath(path),
11060 filename: path,
11061 rootFilename: path,
11062 rewriteUrls: instanceOptions.rewriteUrls
11063 };
11064 newFileInfo.entryPath = newFileInfo.currentDirectory;
11065 newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory;
11066 if (webInfo) {
11067 webInfo.remaining = remaining;
11068 var css = cache.getCSS(path, webInfo, instanceOptions.modifyVars);
11069 if (!reload && css) {
11070 webInfo.local = true;
11071 callback(null, css, data, sheet, webInfo, path);
11072 return;
11073 }
11074 }
11075 // TODO add tests around how this behaves when reloading
11076 errors.remove(path);
11077 instanceOptions.rootFileInfo = newFileInfo;
11078 less.render(data, instanceOptions, function (e, result) {
11079 if (e) {
11080 e.href = path;
11081 callback(e);
11082 }
11083 else {
11084 cache.setCSS(sheet.href, webInfo.lastModified, instanceOptions.modifyVars, result.css);
11085 callback(null, result.css, data, sheet, webInfo, path);
11086 }
11087 });
11088 }
11089 fileManager.loadFile(sheet.href, null, instanceOptions, environment)
11090 .then(function (loadedFile) {
11091 loadInitialFileCallback(loadedFile);
11092 }).catch(function (err) {
11093 console.log(err);
11094 callback(err);
11095 });
11096 }
11097 function loadStyleSheets(callback, reload, modifyVars) {
11098 for (var i = 0; i < less.sheets.length; i++) {
11099 loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars);
11100 }
11101 }
11102 function initRunningMode() {
11103 if (less.env === 'development') {
11104 less.watchTimer = setInterval(function () {
11105 if (less.watchMode) {
11106 fileManager.clearFileCache();
11107 loadStyleSheets(function (e, css, _, sheet, webInfo) {
11108 if (e) {
11109 errors.add(e, e.href || sheet.href);
11110 }
11111 else if (css) {
11112 browser.createCSS(window.document, css, sheet);
11113 }
11114 });
11115 }
11116 }, options.poll);
11117 }
11118 }
11119 //
11120 // Watch mode
11121 //
11122 less.watch = function () {
11123 if (!less.watchMode) {
11124 less.env = 'development';
11125 initRunningMode();
11126 }
11127 this.watchMode = true;
11128 return true;
11129 };
11130 less.unwatch = function () { clearInterval(less.watchTimer); this.watchMode = false; return false; };
11131 //
11132 // Synchronously get all <link> tags with the 'rel' attribute set to
11133 // "stylesheet/less".
11134 //
11135 less.registerStylesheetsImmediately = function () {
11136 var links = document.getElementsByTagName('link');
11137 less.sheets = [];
11138 for (var i = 0; i < links.length; i++) {
11139 if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
11140 (links[i].type.match(typePattern)))) {
11141 less.sheets.push(links[i]);
11142 }
11143 }
11144 };
11145 //
11146 // Asynchronously get all <link> tags with the 'rel' attribute set to
11147 // "stylesheet/less", returning a Promise.
11148 //
11149 less.registerStylesheets = function () { return new Promise(function (resolve, reject) {
11150 less.registerStylesheetsImmediately();
11151 resolve();
11152 }); };
11153 //
11154 // With this function, it's possible to alter variables and re-render
11155 // CSS without reloading less-files
11156 //
11157 less.modifyVars = function (record) { return less.refresh(true, record, false); };
11158 less.refresh = function (reload, modifyVars, clearFileCache) {
11159 if ((reload || clearFileCache) && clearFileCache !== false) {
11160 fileManager.clearFileCache();
11161 }
11162 return new Promise(function (resolve, reject) {
11163 var startTime;
11164 var endTime;
11165 var totalMilliseconds;
11166 var remainingSheets;
11167 startTime = endTime = new Date();
11168 // Set counter for remaining unprocessed sheets
11169 remainingSheets = less.sheets.length;
11170 if (remainingSheets === 0) {
11171 endTime = new Date();
11172 totalMilliseconds = endTime - startTime;
11173 less.logger.info('Less has finished and no sheets were loaded.');
11174 resolve({
11175 startTime: startTime,
11176 endTime: endTime,
11177 totalMilliseconds: totalMilliseconds,
11178 sheets: less.sheets.length
11179 });
11180 }
11181 else {
11182 // Relies on less.sheets array, callback seems to be guaranteed to be called for every element of the array
11183 loadStyleSheets(function (e, css, _, sheet, webInfo) {
11184 if (e) {
11185 errors.add(e, e.href || sheet.href);
11186 reject(e);
11187 return;
11188 }
11189 if (webInfo.local) {
11190 less.logger.info("Loading " + sheet.href + " from cache.");
11191 }
11192 else {
11193 less.logger.info("Rendered " + sheet.href + " successfully.");
11194 }
11195 browser.createCSS(window.document, css, sheet);
11196 less.logger.info("CSS for " + sheet.href + " generated in " + (new Date() - endTime) + "ms");
11197 // Count completed sheet
11198 remainingSheets--;
11199 // Check if the last remaining sheet was processed and then call the promise
11200 if (remainingSheets === 0) {
11201 totalMilliseconds = new Date() - startTime;
11202 less.logger.info("Less has finished. CSS generated in " + totalMilliseconds + "ms");
11203 resolve({
11204 startTime: startTime,
11205 endTime: endTime,
11206 totalMilliseconds: totalMilliseconds,
11207 sheets: less.sheets.length
11208 });
11209 }
11210 endTime = new Date();
11211 }, reload, modifyVars);
11212 }
11213 loadStyles(modifyVars);
11214 });
11215 };
11216 less.refreshStyles = loadStyles;
11217 return less;
11218 });
11219
11220 /**
11221 * Kicks off less and compiles any stylesheets
11222 * used in the browser distributed version of less
11223 * to kick-start less using the browser api
11224 */
11225 var options$1 = defaultOptions();
11226 if (window.less) {
11227 for (var key in window.less) {
11228 if (window.less.hasOwnProperty(key)) {
11229 options$1[key] = window.less[key];
11230 }
11231 }
11232 }
11233 addDefaultOptions(window, options$1);
11234 options$1.plugins = options$1.plugins || [];
11235 if (window.LESS_PLUGINS) {
11236 options$1.plugins = options$1.plugins.concat(window.LESS_PLUGINS);
11237 }
11238 var less = root(window, options$1);
11239 window.less = less;
11240 var css;
11241 var head;
11242 var style;
11243 // Always restore page visibility
11244 function resolveOrReject(data) {
11245 if (data.filename) {
11246 console.warn(data);
11247 }
11248 if (!options$1.async) {
11249 head.removeChild(style);
11250 }
11251 }
11252 if (options$1.onReady) {
11253 if (/!watch/.test(window.location.hash)) {
11254 less.watch();
11255 }
11256 // Simulate synchronous stylesheet loading by hiding page rendering
11257 if (!options$1.async) {
11258 css = 'body { display: none !important }';
11259 head = document.head || document.getElementsByTagName('head')[0];
11260 style = document.createElement('style');
11261 style.type = 'text/css';
11262 if (style.styleSheet) {
11263 style.styleSheet.cssText = css;
11264 }
11265 else {
11266 style.appendChild(document.createTextNode(css));
11267 }
11268 head.appendChild(style);
11269 }
11270 less.registerStylesheetsImmediately();
11271 less.pageLoadFinished = less.refresh(less.env === 'development').then(resolveOrReject, resolveOrReject);
11272 }
11273
11274 return less;
11275
11276})));