PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/node_modules/less-middleware/node_modules/less/test/browser/less.js

https://gitlab.com/mba811/webAudio
JavaScript | 8307 lines | 5978 code | 606 blank | 1723 comment | 1022 complexity | 4193e0e46c43a5d5941ac83d2944c318 MD5 | raw file
Possible License(s): Apache-2.0, MIT, BSD-3-Clause, MPL-2.0-no-copyleft-exception, 0BSD
  1. /*!
  2. * Less - Leaner CSS v1.7.4
  3. * http://lesscss.org
  4. *
  5. * Copyright (c) 2009-2014, Alexis Sellier <self@cloudhead.net>
  6. * Licensed under the Apache v2 License.
  7. *
  8. */
  9. /** * @license Apache v2
  10. */
  11. (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  12. //
  13. // browser.js - client-side engine
  14. //
  15. /*global window, document, location */
  16. var logLevel = {
  17. debug: 3,
  18. info: 2,
  19. errors: 1,
  20. none: 0
  21. };
  22. var less;
  23. function log(str, level) {
  24. if (typeof(console) !== 'undefined' && less.logLevel >= level) {
  25. console.log('less: ' + str);
  26. }
  27. }
  28. /*
  29. TODO - options is now hidden - we should expose it on the less object, but not have it "as" the less object
  30. alternately even have it on environment
  31. e.g. less.environment.options.fileAsync = true;
  32. is it weird you do
  33. less = { fileAsync: true }
  34. then access as less.environment.options.fileAsync ?
  35. */
  36. var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol),
  37. options = window.less,
  38. environment = require("./environment/browser.js")(options, isFileProtocol, log, logLevel);
  39. window.less = less = require('./non-node-index.js')(environment);
  40. less.env = options.env || (location.hostname == '127.0.0.1' ||
  41. location.hostname == '0.0.0.0' ||
  42. location.hostname == 'localhost' ||
  43. (location.port &&
  44. location.port.length > 0) ||
  45. isFileProtocol ? 'development'
  46. : 'production');
  47. // The amount of logging in the javascript console.
  48. // 3 - Debug, information and errors
  49. // 2 - Information and errors
  50. // 1 - Errors
  51. // 0 - None
  52. // Defaults to 2
  53. less.logLevel = typeof(options.logLevel) != 'undefined' ? options.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors);
  54. // Load styles asynchronously (default: false)
  55. //
  56. // This is set to `false` by default, so that the body
  57. // doesn't start loading before the stylesheets are parsed.
  58. // Setting this to `true` can result in flickering.
  59. //
  60. options.async = options.async || false;
  61. options.fileAsync = options.fileAsync || false;
  62. // Interval between watch polls
  63. less.poll = less.poll || (isFileProtocol ? 1000 : 1500);
  64. //Setup user functions
  65. if (options.functions) {
  66. less.functions.functionRegistry.addMultiple(options.functions);
  67. }
  68. var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);
  69. if (dumpLineNumbers) {
  70. less.dumpLineNumbers = dumpLineNumbers[1];
  71. }
  72. var typePattern = /^text\/(x-)?less$/;
  73. var cache = null;
  74. function extractId(href) {
  75. return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain
  76. .replace(/^\//, '' ) // Remove root /
  77. .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
  78. .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
  79. .replace(/\./g, ':'); // Replace dots with colons(for valid id)
  80. }
  81. function errorConsole(e, rootHref) {
  82. var template = '{line} {content}';
  83. var filename = e.filename || rootHref;
  84. var errors = [];
  85. var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
  86. " in " + filename + " ";
  87. var errorline = function (e, i, classname) {
  88. if (e.extract[i] !== undefined) {
  89. errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
  90. .replace(/\{class\}/, classname)
  91. .replace(/\{content\}/, e.extract[i]));
  92. }
  93. };
  94. if (e.extract) {
  95. errorline(e, 0, '');
  96. errorline(e, 1, 'line');
  97. errorline(e, 2, '');
  98. content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' +
  99. errors.join('\n');
  100. } else if (e.stack) {
  101. content += e.stack;
  102. }
  103. log(content, logLevel.errors);
  104. }
  105. function createCSS(styles, sheet, lastModified) {
  106. // Strip the query-string
  107. var href = sheet.href || '';
  108. // If there is no title set, use the filename, minus the extension
  109. var id = 'less:' + (sheet.title || extractId(href));
  110. // If this has already been inserted into the DOM, we may need to replace it
  111. var oldCss = document.getElementById(id);
  112. var keepOldCss = false;
  113. // Create a new stylesheet node for insertion or (if necessary) replacement
  114. var css = document.createElement('style');
  115. css.setAttribute('type', 'text/css');
  116. if (sheet.media) {
  117. css.setAttribute('media', sheet.media);
  118. }
  119. css.id = id;
  120. if (!css.styleSheet) {
  121. css.appendChild(document.createTextNode(styles));
  122. // If new contents match contents of oldCss, don't replace oldCss
  123. keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 &&
  124. oldCss.firstChild.nodeValue === css.firstChild.nodeValue);
  125. }
  126. var head = document.getElementsByTagName('head')[0];
  127. // If there is no oldCss, just append; otherwise, only append if we need
  128. // to replace oldCss with an updated stylesheet
  129. if (oldCss === null || keepOldCss === false) {
  130. var nextEl = sheet && sheet.nextSibling || null;
  131. if (nextEl) {
  132. nextEl.parentNode.insertBefore(css, nextEl);
  133. } else {
  134. head.appendChild(css);
  135. }
  136. }
  137. if (oldCss && keepOldCss === false) {
  138. oldCss.parentNode.removeChild(oldCss);
  139. }
  140. // For IE.
  141. // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash.
  142. // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head
  143. if (css.styleSheet) {
  144. try {
  145. css.styleSheet.cssText = styles;
  146. } catch (e) {
  147. throw new(Error)("Couldn't reassign styleSheet.cssText.");
  148. }
  149. }
  150. // Don't update the local store if the file wasn't modified
  151. if (lastModified && cache) {
  152. log('saving ' + href + ' to cache.', logLevel.info);
  153. try {
  154. cache.setItem(href, styles);
  155. cache.setItem(href + ':timestamp', lastModified);
  156. } catch(e) {
  157. //TODO - could do with adding more robust error handling
  158. log('failed to save', logLevel.errors);
  159. }
  160. }
  161. }
  162. function postProcessCSS(styles) {
  163. if (options.postProcessor && typeof options.postProcessor === 'function') {
  164. styles = options.postProcessor.call(styles, styles) || styles;
  165. }
  166. return styles;
  167. }
  168. function errorHTML(e, rootHref) {
  169. var id = 'less-error-message:' + extractId(rootHref || "");
  170. var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
  171. var elem = document.createElement('div'), timer, content, errors = [];
  172. var filename = e.filename || rootHref;
  173. var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1];
  174. elem.id = id;
  175. elem.className = "less-error-message";
  176. content = '<h3>' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
  177. '</h3>' + '<p>in <a href="' + filename + '">' + filenameNoPath + "</a> ";
  178. var errorline = function (e, i, classname) {
  179. if (e.extract[i] !== undefined) {
  180. errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
  181. .replace(/\{class\}/, classname)
  182. .replace(/\{content\}/, e.extract[i]));
  183. }
  184. };
  185. if (e.extract) {
  186. errorline(e, 0, '');
  187. errorline(e, 1, 'line');
  188. errorline(e, 2, '');
  189. content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
  190. '<ul>' + errors.join('') + '</ul>';
  191. } else if (e.stack) {
  192. content += '<br/>' + e.stack.split('\n').slice(1).join('<br/>');
  193. }
  194. elem.innerHTML = content;
  195. // CSS for error messages
  196. createCSS([
  197. '.less-error-message ul, .less-error-message li {',
  198. 'list-style-type: none;',
  199. 'margin-right: 15px;',
  200. 'padding: 4px 0;',
  201. 'margin: 0;',
  202. '}',
  203. '.less-error-message label {',
  204. 'font-size: 12px;',
  205. 'margin-right: 15px;',
  206. 'padding: 4px 0;',
  207. 'color: #cc7777;',
  208. '}',
  209. '.less-error-message pre {',
  210. 'color: #dd6666;',
  211. 'padding: 4px 0;',
  212. 'margin: 0;',
  213. 'display: inline-block;',
  214. '}',
  215. '.less-error-message pre.line {',
  216. 'color: #ff0000;',
  217. '}',
  218. '.less-error-message h3 {',
  219. 'font-size: 20px;',
  220. 'font-weight: bold;',
  221. 'padding: 15px 0 5px 0;',
  222. 'margin: 0;',
  223. '}',
  224. '.less-error-message a {',
  225. 'color: #10a',
  226. '}',
  227. '.less-error-message .error {',
  228. 'color: red;',
  229. 'font-weight: bold;',
  230. 'padding-bottom: 2px;',
  231. 'border-bottom: 1px dashed red;',
  232. '}'
  233. ].join('\n'), { title: 'error-message' });
  234. elem.style.cssText = [
  235. "font-family: Arial, sans-serif",
  236. "border: 1px solid #e00",
  237. "background-color: #eee",
  238. "border-radius: 5px",
  239. "-webkit-border-radius: 5px",
  240. "-moz-border-radius: 5px",
  241. "color: #e00",
  242. "padding: 15px",
  243. "margin-bottom: 15px"
  244. ].join(';');
  245. if (less.env == 'development') {
  246. timer = setInterval(function () {
  247. if (document.body) {
  248. if (document.getElementById(id)) {
  249. document.body.replaceChild(elem, document.getElementById(id));
  250. } else {
  251. document.body.insertBefore(elem, document.body.firstChild);
  252. }
  253. clearInterval(timer);
  254. }
  255. }, 10);
  256. }
  257. }
  258. function error(e, rootHref) {
  259. if (!options.errorReporting || options.errorReporting === "html") {
  260. errorHTML(e, rootHref);
  261. } else if (options.errorReporting === "console") {
  262. errorConsole(e, rootHref);
  263. } else if (typeof options.errorReporting === 'function') {
  264. options.errorReporting("add", e, rootHref);
  265. }
  266. }
  267. function removeErrorHTML(path) {
  268. var node = document.getElementById('less-error-message:' + extractId(path));
  269. if (node) {
  270. node.parentNode.removeChild(node);
  271. }
  272. }
  273. function removeErrorConsole(path) {
  274. //no action
  275. }
  276. function removeError(path) {
  277. if (!options.errorReporting || options.errorReporting === "html") {
  278. removeErrorHTML(path);
  279. } else if (options.errorReporting === "console") {
  280. removeErrorConsole(path);
  281. } else if (typeof options.errorReporting === 'function') {
  282. options.errorReporting("remove", path);
  283. }
  284. }
  285. function loadStyles(modifyVars) {
  286. var styles = document.getElementsByTagName('style'),
  287. style;
  288. for (var i = 0; i < styles.length; i++) {
  289. style = styles[i];
  290. if (style.type.match(typePattern)) {
  291. var env = new less.contexts.parseEnv(options),
  292. lessText = style.innerHTML || '';
  293. env.filename = document.location.href.replace(/#.*$/, '');
  294. if (modifyVars || options.globalVars) {
  295. env.useFileCache = true;
  296. }
  297. /*jshint loopfunc:true */
  298. // use closure to store current value of i
  299. var callback = (function(style) {
  300. return function (e, cssAST) {
  301. if (e) {
  302. return error(e, "inline");
  303. }
  304. var css = cssAST.toCSS(options);
  305. style.type = 'text/css';
  306. if (style.styleSheet) {
  307. style.styleSheet.cssText = css;
  308. } else {
  309. style.innerHTML = css;
  310. }
  311. };
  312. })(style);
  313. new(less.Parser)(env).parse(lessText, callback, {globalVars: options.globalVars, modifyVars: modifyVars});
  314. }
  315. }
  316. }
  317. function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
  318. var env = new less.contexts.parseEnv(options);
  319. env.mime = sheet.type;
  320. if (modifyVars || options.globalVars) {
  321. env.useFileCache = true;
  322. }
  323. less.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) {
  324. var newFileInfo = {
  325. currentDirectory: less.environment.getPath(env, path),
  326. filename: path,
  327. rootFilename: path,
  328. relativeUrls: env.relativeUrls};
  329. newFileInfo.entryPath = newFileInfo.currentDirectory;
  330. newFileInfo.rootpath = env.rootpath || newFileInfo.currentDirectory;
  331. if (webInfo) {
  332. webInfo.remaining = remaining;
  333. var css = cache && cache.getItem(path),
  334. timestamp = cache && cache.getItem(path + ':timestamp');
  335. if (!reload && timestamp && webInfo.lastModified &&
  336. (new(Date)(webInfo.lastModified).valueOf() ===
  337. new(Date)(timestamp).valueOf())) {
  338. // Use local copy
  339. createCSS(css, sheet);
  340. webInfo.local = true;
  341. callback(null, null, data, sheet, webInfo, path);
  342. return;
  343. }
  344. }
  345. //TODO add tests around how this behaves when reloading
  346. removeError(path);
  347. if (data) {
  348. env.currentFileInfo = newFileInfo;
  349. new(less.Parser)(env).parse(data, function (e, root) {
  350. if (e) { return callback(e, null, null, sheet); }
  351. try {
  352. callback(e, root, data, sheet, webInfo, path);
  353. } catch (e) {
  354. callback(e, null, null, sheet);
  355. }
  356. }, {modifyVars: modifyVars, globalVars: options.globalVars});
  357. } else {
  358. callback(e, null, null, sheet, webInfo, path);
  359. }
  360. }, env, modifyVars);
  361. }
  362. function loadStyleSheets(callback, reload, modifyVars) {
  363. for (var i = 0; i < less.sheets.length; i++) {
  364. loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars);
  365. }
  366. }
  367. function initRunningMode(){
  368. if (less.env === 'development') {
  369. less.watchTimer = setInterval(function () {
  370. if (less.watchMode) {
  371. loadStyleSheets(function (e, root, _, sheet, env) {
  372. if (e) {
  373. error(e, sheet.href);
  374. } else if (root) {
  375. var styles = root.toCSS(less);
  376. styles = postProcessCSS(styles);
  377. createCSS(styles, sheet, env.lastModified);
  378. }
  379. });
  380. }
  381. }, options.poll);
  382. }
  383. }
  384. //
  385. // Watch mode
  386. //
  387. less.watch = function () {
  388. if (!less.watchMode ){
  389. less.env = 'development';
  390. initRunningMode();
  391. }
  392. this.watchMode = true;
  393. return true;
  394. };
  395. less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; };
  396. if (/!watch/.test(location.hash)) {
  397. less.watch();
  398. }
  399. if (less.env != 'development') {
  400. try {
  401. cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;
  402. } catch (_) {}
  403. }
  404. //
  405. // Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
  406. //
  407. var links = document.getElementsByTagName('link');
  408. less.sheets = [];
  409. for (var i = 0; i < links.length; i++) {
  410. if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
  411. (links[i].type.match(typePattern)))) {
  412. less.sheets.push(links[i]);
  413. }
  414. }
  415. //
  416. // With this function, it's possible to alter variables and re-render
  417. // CSS without reloading less-files
  418. //
  419. less.modifyVars = function(record) {
  420. less.refresh(false, record);
  421. };
  422. less.refresh = function (reload, modifyVars) {
  423. var startTime, endTime;
  424. startTime = endTime = new Date();
  425. loadStyleSheets(function (e, root, _, sheet, env) {
  426. if (e) {
  427. return error(e, sheet.href);
  428. }
  429. if (env.local) {
  430. log("loading " + sheet.href + " from cache.", logLevel.info);
  431. } else {
  432. log("parsed " + sheet.href + " successfully.", logLevel.debug);
  433. var styles = root.toCSS(options);
  434. styles = postProcessCSS(styles);
  435. createCSS(styles, sheet, env.lastModified);
  436. }
  437. log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info);
  438. if (env.remaining === 0) {
  439. log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info);
  440. }
  441. endTime = new Date();
  442. }, reload, modifyVars);
  443. loadStyles(modifyVars);
  444. };
  445. less.refreshStyles = loadStyles;
  446. less.refresh(less.env === 'development');
  447. },{"./environment/browser.js":6,"./non-node-index.js":20}],2:[function(require,module,exports){
  448. var contexts = {};
  449. module.exports = contexts;
  450. var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
  451. if (!original) { return; }
  452. for(var i = 0; i < propertiesToCopy.length; i++) {
  453. if (original.hasOwnProperty(propertiesToCopy[i])) {
  454. destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
  455. }
  456. }
  457. };
  458. var parseCopyProperties = [
  459. 'paths', // option - unmodified - paths to search for imports on
  460. 'files', // list of files that have been imported, used for import-once
  461. 'contents', // map - filename to contents of all the files
  462. 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
  463. 'relativeUrls', // option - whether to adjust URL's to be relative
  464. 'rootpath', // option - rootpath to append to URL's
  465. 'strictImports', // option -
  466. 'insecure', // option - whether to allow imports from insecure ssl hosts
  467. 'dumpLineNumbers', // option - whether to dump line numbers
  468. 'compress', // option - whether to compress
  469. 'processImports', // option - whether to process imports. if false then imports will not be imported
  470. 'syncImport', // option - whether to import synchronously
  471. 'chunkInput', // option - whether to chunk input. more performant but causes parse issues.
  472. 'mime', // browser only - mime type for sheet import
  473. 'useFileCache', // browser only - whether to use the per file session cache
  474. 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
  475. ];
  476. //currentFileInfo = {
  477. // 'relativeUrls' - option - whether to adjust URL's to be relative
  478. // 'filename' - full resolved filename of current file
  479. // 'rootpath' - path to append to normal URLs for this node
  480. // 'currentDirectory' - path to the current file, absolute
  481. // 'rootFilename' - filename of the base file
  482. // 'entryPath' - absolute path to the entry file
  483. // 'reference' - whether the file should not be output and only output parts that are referenced
  484. contexts.parseEnv = function(options) {
  485. copyFromOriginal(options, this, parseCopyProperties);
  486. if (!this.contents) { this.contents = {}; }
  487. if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
  488. if (!this.files) { this.files = {}; }
  489. if (typeof this.paths === "string") { this.paths = [this.paths]; }
  490. if (!this.currentFileInfo) {
  491. var filename = (options && options.filename) || "input";
  492. var entryPath = filename.replace(/[^\/\\]*$/, "");
  493. if (options) {
  494. options.filename = null;
  495. }
  496. this.currentFileInfo = {
  497. filename: filename,
  498. relativeUrls: this.relativeUrls,
  499. rootpath: (options && options.rootpath) || "",
  500. currentDirectory: entryPath,
  501. entryPath: entryPath,
  502. rootFilename: filename
  503. };
  504. }
  505. };
  506. var evalCopyProperties = [
  507. 'silent', // whether to swallow errors and warnings
  508. 'verbose', // whether to log more activity
  509. 'compress', // whether to compress
  510. 'yuicompress', // whether to compress with the outside tool yui compressor
  511. 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
  512. 'strictMath', // whether math has to be within parenthesis
  513. 'strictUnits', // whether units need to evaluate correctly
  514. 'cleancss', // whether to compress with clean-css
  515. 'sourceMap', // whether to output a source map
  516. 'importMultiple', // whether we are currently importing multiple copies
  517. 'urlArgs', // whether to add args into url tokens
  518. 'javascriptEnabled'// option - whether JavaScript is enabled. if undefined, defaults to true
  519. ];
  520. contexts.evalEnv = function(options, frames) {
  521. copyFromOriginal(options, this, evalCopyProperties);
  522. this.frames = frames || [];
  523. };
  524. contexts.evalEnv.prototype.inParenthesis = function () {
  525. if (!this.parensStack) {
  526. this.parensStack = [];
  527. }
  528. this.parensStack.push(true);
  529. };
  530. contexts.evalEnv.prototype.outOfParenthesis = function () {
  531. this.parensStack.pop();
  532. };
  533. contexts.evalEnv.prototype.isMathOn = function () {
  534. return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
  535. };
  536. contexts.evalEnv.prototype.isPathRelative = function (path) {
  537. return !/^(?:[a-z-]+:|\/)/.test(path);
  538. };
  539. contexts.evalEnv.prototype.normalizePath = function( path ) {
  540. var
  541. segments = path.split("/").reverse(),
  542. segment;
  543. path = [];
  544. while (segments.length !== 0 ) {
  545. segment = segments.pop();
  546. switch( segment ) {
  547. case ".":
  548. break;
  549. case "..":
  550. if ((path.length === 0) || (path[path.length - 1] === "..")) {
  551. path.push( segment );
  552. } else {
  553. path.pop();
  554. }
  555. break;
  556. default:
  557. path.push( segment );
  558. break;
  559. }
  560. }
  561. return path.join("/");
  562. };
  563. //todo - do the same for the toCSS env
  564. //tree.toCSSEnv = function (options) {
  565. //};
  566. },{}],3:[function(require,module,exports){
  567. module.exports = {
  568. 'aliceblue':'#f0f8ff',
  569. 'antiquewhite':'#faebd7',
  570. 'aqua':'#00ffff',
  571. 'aquamarine':'#7fffd4',
  572. 'azure':'#f0ffff',
  573. 'beige':'#f5f5dc',
  574. 'bisque':'#ffe4c4',
  575. 'black':'#000000',
  576. 'blanchedalmond':'#ffebcd',
  577. 'blue':'#0000ff',
  578. 'blueviolet':'#8a2be2',
  579. 'brown':'#a52a2a',
  580. 'burlywood':'#deb887',
  581. 'cadetblue':'#5f9ea0',
  582. 'chartreuse':'#7fff00',
  583. 'chocolate':'#d2691e',
  584. 'coral':'#ff7f50',
  585. 'cornflowerblue':'#6495ed',
  586. 'cornsilk':'#fff8dc',
  587. 'crimson':'#dc143c',
  588. 'cyan':'#00ffff',
  589. 'darkblue':'#00008b',
  590. 'darkcyan':'#008b8b',
  591. 'darkgoldenrod':'#b8860b',
  592. 'darkgray':'#a9a9a9',
  593. 'darkgrey':'#a9a9a9',
  594. 'darkgreen':'#006400',
  595. 'darkkhaki':'#bdb76b',
  596. 'darkmagenta':'#8b008b',
  597. 'darkolivegreen':'#556b2f',
  598. 'darkorange':'#ff8c00',
  599. 'darkorchid':'#9932cc',
  600. 'darkred':'#8b0000',
  601. 'darksalmon':'#e9967a',
  602. 'darkseagreen':'#8fbc8f',
  603. 'darkslateblue':'#483d8b',
  604. 'darkslategray':'#2f4f4f',
  605. 'darkslategrey':'#2f4f4f',
  606. 'darkturquoise':'#00ced1',
  607. 'darkviolet':'#9400d3',
  608. 'deeppink':'#ff1493',
  609. 'deepskyblue':'#00bfff',
  610. 'dimgray':'#696969',
  611. 'dimgrey':'#696969',
  612. 'dodgerblue':'#1e90ff',
  613. 'firebrick':'#b22222',
  614. 'floralwhite':'#fffaf0',
  615. 'forestgreen':'#228b22',
  616. 'fuchsia':'#ff00ff',
  617. 'gainsboro':'#dcdcdc',
  618. 'ghostwhite':'#f8f8ff',
  619. 'gold':'#ffd700',
  620. 'goldenrod':'#daa520',
  621. 'gray':'#808080',
  622. 'grey':'#808080',
  623. 'green':'#008000',
  624. 'greenyellow':'#adff2f',
  625. 'honeydew':'#f0fff0',
  626. 'hotpink':'#ff69b4',
  627. 'indianred':'#cd5c5c',
  628. 'indigo':'#4b0082',
  629. 'ivory':'#fffff0',
  630. 'khaki':'#f0e68c',
  631. 'lavender':'#e6e6fa',
  632. 'lavenderblush':'#fff0f5',
  633. 'lawngreen':'#7cfc00',
  634. 'lemonchiffon':'#fffacd',
  635. 'lightblue':'#add8e6',
  636. 'lightcoral':'#f08080',
  637. 'lightcyan':'#e0ffff',
  638. 'lightgoldenrodyellow':'#fafad2',
  639. 'lightgray':'#d3d3d3',
  640. 'lightgrey':'#d3d3d3',
  641. 'lightgreen':'#90ee90',
  642. 'lightpink':'#ffb6c1',
  643. 'lightsalmon':'#ffa07a',
  644. 'lightseagreen':'#20b2aa',
  645. 'lightskyblue':'#87cefa',
  646. 'lightslategray':'#778899',
  647. 'lightslategrey':'#778899',
  648. 'lightsteelblue':'#b0c4de',
  649. 'lightyellow':'#ffffe0',
  650. 'lime':'#00ff00',
  651. 'limegreen':'#32cd32',
  652. 'linen':'#faf0e6',
  653. 'magenta':'#ff00ff',
  654. 'maroon':'#800000',
  655. 'mediumaquamarine':'#66cdaa',
  656. 'mediumblue':'#0000cd',
  657. 'mediumorchid':'#ba55d3',
  658. 'mediumpurple':'#9370d8',
  659. 'mediumseagreen':'#3cb371',
  660. 'mediumslateblue':'#7b68ee',
  661. 'mediumspringgreen':'#00fa9a',
  662. 'mediumturquoise':'#48d1cc',
  663. 'mediumvioletred':'#c71585',
  664. 'midnightblue':'#191970',
  665. 'mintcream':'#f5fffa',
  666. 'mistyrose':'#ffe4e1',
  667. 'moccasin':'#ffe4b5',
  668. 'navajowhite':'#ffdead',
  669. 'navy':'#000080',
  670. 'oldlace':'#fdf5e6',
  671. 'olive':'#808000',
  672. 'olivedrab':'#6b8e23',
  673. 'orange':'#ffa500',
  674. 'orangered':'#ff4500',
  675. 'orchid':'#da70d6',
  676. 'palegoldenrod':'#eee8aa',
  677. 'palegreen':'#98fb98',
  678. 'paleturquoise':'#afeeee',
  679. 'palevioletred':'#d87093',
  680. 'papayawhip':'#ffefd5',
  681. 'peachpuff':'#ffdab9',
  682. 'peru':'#cd853f',
  683. 'pink':'#ffc0cb',
  684. 'plum':'#dda0dd',
  685. 'powderblue':'#b0e0e6',
  686. 'purple':'#800080',
  687. 'red':'#ff0000',
  688. 'rosybrown':'#bc8f8f',
  689. 'royalblue':'#4169e1',
  690. 'saddlebrown':'#8b4513',
  691. 'salmon':'#fa8072',
  692. 'sandybrown':'#f4a460',
  693. 'seagreen':'#2e8b57',
  694. 'seashell':'#fff5ee',
  695. 'sienna':'#a0522d',
  696. 'silver':'#c0c0c0',
  697. 'skyblue':'#87ceeb',
  698. 'slateblue':'#6a5acd',
  699. 'slategray':'#708090',
  700. 'slategrey':'#708090',
  701. 'snow':'#fffafa',
  702. 'springgreen':'#00ff7f',
  703. 'steelblue':'#4682b4',
  704. 'tan':'#d2b48c',
  705. 'teal':'#008080',
  706. 'thistle':'#d8bfd8',
  707. 'tomato':'#ff6347',
  708. 'turquoise':'#40e0d0',
  709. 'violet':'#ee82ee',
  710. 'wheat':'#f5deb3',
  711. 'white':'#ffffff',
  712. 'whitesmoke':'#f5f5f5',
  713. 'yellow':'#ffff00',
  714. 'yellowgreen':'#9acd32'
  715. };
  716. },{}],4:[function(require,module,exports){
  717. module.exports = {
  718. colors: require("./colors.js"),
  719. unitConversions: require("./unit-conversions.js")
  720. };
  721. },{"./colors.js":3,"./unit-conversions.js":5}],5:[function(require,module,exports){
  722. module.exports = {
  723. length: {
  724. 'm': 1,
  725. 'cm': 0.01,
  726. 'mm': 0.001,
  727. 'in': 0.0254,
  728. 'px': 0.0254 / 96,
  729. 'pt': 0.0254 / 72,
  730. 'pc': 0.0254 / 72 * 12
  731. },
  732. duration: {
  733. 's': 1,
  734. 'ms': 0.001
  735. },
  736. angle: {
  737. 'rad': 1/(2*Math.PI),
  738. 'deg': 1/360,
  739. 'grad': 1/400,
  740. 'turn': 1
  741. }
  742. };
  743. },{}],6:[function(require,module,exports){
  744. /*global window, XMLHttpRequest */
  745. module.exports = function(options, isFileProtocol, log, logLevel) {
  746. var fileCache = {};
  747. //TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
  748. // isFileProtocol is global
  749. function getXMLHttpRequest() {
  750. if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
  751. return new XMLHttpRequest();
  752. } else {
  753. try {
  754. /*global ActiveXObject */
  755. return new ActiveXObject("Microsoft.XMLHTTP");
  756. } catch (e) {
  757. log("browser doesn't support AJAX.", logLevel.errors);
  758. return null;
  759. }
  760. }
  761. }
  762. return {
  763. // make generic but overriddable
  764. warn: function warn(env, msg) {
  765. console.warn(msg);
  766. },
  767. // make generic but overriddable
  768. getPath: function getPath(env, filename) {
  769. var j = filename.lastIndexOf('/');
  770. if (j < 0) {
  771. j = filename.lastIndexOf('\\');
  772. }
  773. if (j < 0) {
  774. return "";
  775. }
  776. return filename.slice(0, j + 1);
  777. },
  778. // make generic but overriddable
  779. isPathAbsolute: function isPathAbsolute(env, filename) {
  780. return /^(?:[a-z-]+:|\/|\\)/i.test(filename);
  781. },
  782. alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() {
  783. return true;
  784. },
  785. getCleanCSS: function () {
  786. },
  787. supportsDataURI: function() {
  788. return false;
  789. },
  790. pathDiff: function pathDiff(url, baseUrl) {
  791. // diff between two paths to create a relative path
  792. var urlParts = this.extractUrlParts(url),
  793. baseUrlParts = this.extractUrlParts(baseUrl),
  794. i, max, urlDirectories, baseUrlDirectories, diff = "";
  795. if (urlParts.hostPart !== baseUrlParts.hostPart) {
  796. return "";
  797. }
  798. max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
  799. for(i = 0; i < max; i++) {
  800. if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
  801. }
  802. baseUrlDirectories = baseUrlParts.directories.slice(i);
  803. urlDirectories = urlParts.directories.slice(i);
  804. for(i = 0; i < baseUrlDirectories.length-1; i++) {
  805. diff += "../";
  806. }
  807. for(i = 0; i < urlDirectories.length-1; i++) {
  808. diff += urlDirectories[i] + "/";
  809. }
  810. return diff;
  811. },
  812. join: function join(basePath, laterPath) {
  813. if (!basePath) {
  814. return laterPath;
  815. }
  816. return this.extractUrlParts(laterPath, basePath).path;
  817. },
  818. // helper function, not part of API
  819. extractUrlParts: function extractUrlParts(url, baseUrl) {
  820. // urlParts[1] = protocol&hostname || /
  821. // urlParts[2] = / if path relative to host base
  822. // urlParts[3] = directories
  823. // urlParts[4] = filename
  824. // urlParts[5] = parameters
  825. var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
  826. urlParts = url.match(urlPartsRegex),
  827. returner = {}, directories = [], i, baseUrlParts;
  828. if (!urlParts) {
  829. throw new Error("Could not parse sheet href - '"+url+"'");
  830. }
  831. // Stylesheets in IE don't always return the full path
  832. if (!urlParts[1] || urlParts[2]) {
  833. baseUrlParts = baseUrl.match(urlPartsRegex);
  834. if (!baseUrlParts) {
  835. throw new Error("Could not parse page url - '"+baseUrl+"'");
  836. }
  837. urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
  838. if (!urlParts[2]) {
  839. urlParts[3] = baseUrlParts[3] + urlParts[3];
  840. }
  841. }
  842. if (urlParts[3]) {
  843. directories = urlParts[3].replace(/\\/g, "/").split("/");
  844. // extract out . before .. so .. doesn't absorb a non-directory
  845. for(i = 0; i < directories.length; i++) {
  846. if (directories[i] === ".") {
  847. directories.splice(i, 1);
  848. i -= 1;
  849. }
  850. }
  851. for(i = 0; i < directories.length; i++) {
  852. if (directories[i] === ".." && i > 0) {
  853. directories.splice(i-1, 2);
  854. i -= 2;
  855. }
  856. }
  857. }
  858. returner.hostPart = urlParts[1];
  859. returner.directories = directories;
  860. returner.path = urlParts[1] + directories.join("/");
  861. returner.fileUrl = returner.path + (urlParts[4] || "");
  862. returner.url = returner.fileUrl + (urlParts[5] || "");
  863. return returner;
  864. },
  865. doXHR: function doXHR(url, type, callback, errback) {
  866. var xhr = getXMLHttpRequest();
  867. var async = isFileProtocol ? options.fileAsync : options.async;
  868. if (typeof(xhr.overrideMimeType) === 'function') {
  869. xhr.overrideMimeType('text/css');
  870. }
  871. log("XHR: Getting '" + url + "'", logLevel.debug);
  872. xhr.open('GET', url, async);
  873. xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
  874. xhr.send(null);
  875. function handleResponse(xhr, callback, errback) {
  876. if (xhr.status >= 200 && xhr.status < 300) {
  877. callback(xhr.responseText,
  878. xhr.getResponseHeader("Last-Modified"));
  879. } else if (typeof(errback) === 'function') {
  880. errback(xhr.status, url);
  881. }
  882. }
  883. if (isFileProtocol && !options.fileAsync) {
  884. if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
  885. callback(xhr.responseText);
  886. } else {
  887. errback(xhr.status, url);
  888. }
  889. } else if (async) {
  890. xhr.onreadystatechange = function () {
  891. if (xhr.readyState == 4) {
  892. handleResponse(xhr, callback, errback);
  893. }
  894. };
  895. } else {
  896. handleResponse(xhr, callback, errback);
  897. }
  898. },
  899. loadFile: function loadFile(env, filename, currentDirectory, callback) {
  900. if (currentDirectory && !this.isPathAbsolute(env, filename)) {
  901. filename = currentDirectory + filename;
  902. }
  903. // sheet may be set to the stylesheet for the initial load or a collection of properties including
  904. // some env variables for imports
  905. var hrefParts = this.extractUrlParts(filename, window.location.href);
  906. var href = hrefParts.url;
  907. if (env.useFileCache && fileCache[href]) {
  908. try {
  909. var lessText = fileCache[href];
  910. callback(null, lessText, href, { lastModified: new Date() });
  911. } catch (e) {
  912. callback(e, null, href);
  913. }
  914. return;
  915. }
  916. this.doXHR(href, env.mime, function doXHRCallback(data, lastModified) {
  917. // per file cache
  918. fileCache[href] = data;
  919. // Use remote copy (re-parse)
  920. callback(null, data, href, { lastModified: lastModified });
  921. }, function doXHRError(status, url) {
  922. callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href);
  923. });
  924. }
  925. };
  926. };
  927. },{}],7:[function(require,module,exports){
  928. var Color = require("../tree/color.js"),
  929. functionRegistry = require("./function-registry.js");
  930. // Color Blending
  931. // ref: http://www.w3.org/TR/compositing-1
  932. function colorBlend(mode, color1, color2) {
  933. var ab = color1.alpha, cb, // backdrop
  934. as = color2.alpha, cs, // source
  935. ar, cr, r = []; // result
  936. ar = as + ab * (1 - as);
  937. for (var i = 0; i < 3; i++) {
  938. cb = color1.rgb[i] / 255;
  939. cs = color2.rgb[i] / 255;
  940. cr = mode(cb, cs);
  941. if (ar) {
  942. cr = (as * cs + ab * (cb -
  943. as * (cb + cs - cr))) / ar;
  944. }
  945. r[i] = cr * 255;
  946. }
  947. return new(Color)(r, ar);
  948. }
  949. var colorBlendModeFunctions = {
  950. multiply: function(cb, cs) {
  951. return cb * cs;
  952. },
  953. screen: function(cb, cs) {
  954. return cb + cs - cb * cs;
  955. },
  956. overlay: function(cb, cs) {
  957. cb *= 2;
  958. return (cb <= 1)
  959. ? colorBlendModeFunctions.multiply(cb, cs)
  960. : colorBlendModeFunctions.screen(cb - 1, cs);
  961. },
  962. softlight: function(cb, cs) {
  963. var d = 1, e = cb;
  964. if (cs > 0.5) {
  965. e = 1;
  966. d = (cb > 0.25) ? Math.sqrt(cb)
  967. : ((16 * cb - 12) * cb + 4) * cb;
  968. }
  969. return cb - (1 - 2 * cs) * e * (d - cb);
  970. },
  971. hardlight: function(cb, cs) {
  972. return colorBlendModeFunctions.overlay(cs, cb);
  973. },
  974. difference: function(cb, cs) {
  975. return Math.abs(cb - cs);
  976. },
  977. exclusion: function(cb, cs) {
  978. return cb + cs - 2 * cb * cs;
  979. },
  980. // non-w3c functions:
  981. average: function(cb, cs) {
  982. return (cb + cs) / 2;
  983. },
  984. negation: function(cb, cs) {
  985. return 1 - Math.abs(cb + cs - 1);
  986. }
  987. };
  988. for (var f in colorBlendModeFunctions) {
  989. if (colorBlendModeFunctions.hasOwnProperty(f)) {
  990. colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]);
  991. }
  992. }
  993. functionRegistry.addMultiple(colorBlend);
  994. },{"../tree/color.js":31,"./function-registry.js":12}],8:[function(require,module,exports){
  995. var Dimension = require("../tree/dimension.js"),
  996. Color = require("../tree/color.js"),
  997. Quoted = require("../tree/quoted.js"),
  998. Anonymous = require("../tree/anonymous.js"),
  999. functionRegistry = require("./function-registry.js"),
  1000. colorFunctions;
  1001. function clamp(val) {
  1002. return Math.min(1, Math.max(0, val));
  1003. }
  1004. function hsla(color) {
  1005. return colorFunctions.hsla(color.h, color.s, color.l, color.a);
  1006. }
  1007. function number(n) {
  1008. if (n instanceof Dimension) {
  1009. return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
  1010. } else if (typeof(n) === 'number') {
  1011. return n;
  1012. } else {
  1013. throw {
  1014. error: "RuntimeError",
  1015. message: "color functions take numbers as parameters"
  1016. };
  1017. }
  1018. }
  1019. function scaled(n, size) {
  1020. if (n instanceof Dimension && n.unit.is('%')) {
  1021. return parseFloat(n.value * size / 100);
  1022. } else {
  1023. return number(n);
  1024. }
  1025. }
  1026. colorFunctions = {
  1027. rgb: function (r, g, b) {
  1028. return colorFunctions.rgba(r, g, b, 1.0);
  1029. },
  1030. rgba: function (r, g, b, a) {
  1031. var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
  1032. a = number(a);
  1033. return new(Color)(rgb, a);
  1034. },
  1035. hsl: function (h, s, l) {
  1036. return colorFunctions.hsla(h, s, l, 1.0);
  1037. },
  1038. hsla: function (h, s, l, a) {
  1039. function hue(h) {
  1040. h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
  1041. if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }
  1042. else if (h * 2 < 1) { return m2; }
  1043. else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }
  1044. else { return m1; }
  1045. }
  1046. h = (number(h) % 360) / 360;
  1047. s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
  1048. var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
  1049. var m1 = l * 2 - m2;
  1050. return colorFunctions.rgba(hue(h + 1/3) * 255,
  1051. hue(h) * 255,
  1052. hue(h - 1/3) * 255,
  1053. a);
  1054. },
  1055. hsv: function(h, s, v) {
  1056. return colorFunctions.hsva(h, s, v, 1.0);
  1057. },
  1058. hsva: function(h, s, v, a) {
  1059. h = ((number(h) % 360) / 360) * 360;
  1060. s = number(s); v = number(v); a = number(a);
  1061. var i, f;
  1062. i = Math.floor((h / 60) % 6);
  1063. f = (h / 60) - i;
  1064. var vs = [v,
  1065. v * (1 - s),
  1066. v * (1 - f * s),
  1067. v * (1 - (1 - f) * s)];
  1068. var perm = [[0, 3, 1],
  1069. [2, 0, 1],
  1070. [1, 0, 3],
  1071. [1, 2, 0],
  1072. [3, 1, 0],
  1073. [0, 1, 2]];
  1074. return colorFunctions.rgba(vs[perm[i][0]] * 255,
  1075. vs[perm[i][1]] * 255,
  1076. vs[perm[i][2]] * 255,
  1077. a);
  1078. },
  1079. hue: function (color) {
  1080. return new(Dimension)(color.toHSL().h);
  1081. },
  1082. saturation: function (color) {
  1083. return new(Dimension)(color.toHSL().s * 100, '%');
  1084. },
  1085. lightness: function (color) {
  1086. return new(Dimension)(color.toHSL().l * 100, '%');
  1087. },
  1088. hsvhue: function(color) {
  1089. return new(Dimension)(color.toHSV().h);
  1090. },
  1091. hsvsaturation: function (color) {
  1092. return new(Dimension)(color.toHSV().s * 100, '%');
  1093. },
  1094. hsvvalue: function (color) {
  1095. return new(Dimension)(color.toHSV().v * 100, '%');
  1096. },
  1097. red: function (color) {
  1098. return new(Dimension)(color.rgb[0]);
  1099. },
  1100. green: function (color) {
  1101. return new(Dimension)(color.rgb[1]);
  1102. },
  1103. blue: function (color) {
  1104. return new(Dimension)(color.rgb[2]);
  1105. },
  1106. alpha: function (color) {
  1107. return new(Dimension)(color.toHSL().a);
  1108. },
  1109. luma: function (color) {
  1110. return new(Dimension)(color.luma() * color.alpha * 100, '%');
  1111. },
  1112. luminance: function (color) {
  1113. var luminance =
  1114. (0.2126 * color.rgb[0] / 255)
  1115. + (0.7152 * color.rgb[1] / 255)
  1116. + (0.0722 * color.rgb[2] / 255);
  1117. return new(Dimension)(luminance * color.alpha * 100, '%');
  1118. },
  1119. saturate: function (color, amount) {
  1120. // filter: saturate(3.2);
  1121. // should be kept as is, so check for color
  1122. if (!color.rgb) {
  1123. return null;
  1124. }
  1125. var hsl = color.toHSL();
  1126. hsl.s += amount.value / 100;
  1127. hsl.s = clamp(hsl.s);
  1128. return hsla(hsl);
  1129. },
  1130. desaturate: function (color, amount) {
  1131. var hsl = color.toHSL();
  1132. hsl.s -= amount.value / 100;
  1133. hsl.s = clamp(hsl.s);
  1134. return hsla(hsl);
  1135. },
  1136. lighten: function (color, amount) {
  1137. var hsl = color.toHSL();
  1138. hsl.l += amount.value / 100;
  1139. hsl.l = clamp(hsl.l);
  1140. return hsla(hsl);
  1141. },
  1142. darken: function (color, amount) {
  1143. var hsl = color.toHSL();
  1144. hsl.l -= amount.value / 100;
  1145. hsl.l = clamp(hsl.l);
  1146. return hsla(hsl);
  1147. },
  1148. fadein: function (color, amount) {
  1149. var hsl = color.toHSL();
  1150. hsl.a += amount.value / 100;
  1151. hsl.a = clamp(hsl.a);
  1152. return hsla(hsl);
  1153. },
  1154. fadeout: function (color, amount) {
  1155. var hsl = color.toHSL();
  1156. hsl.a -= amount.value / 100;
  1157. hsl.a = clamp(hsl.a);
  1158. return hsla(hsl);
  1159. },
  1160. fade: function (color, amount) {
  1161. var hsl = color.toHSL();
  1162. hsl.a = amount.value / 100;
  1163. hsl.a = clamp(hsl.a);
  1164. return hsla(hsl);
  1165. },
  1166. spin: function (color, amount) {
  1167. var hsl = color.toHSL();
  1168. var hue = (hsl.h + amount.value) % 360;
  1169. hsl.h = hue < 0 ? 360 + hue : hue;
  1170. return hsla(hsl);
  1171. },
  1172. //
  1173. // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
  1174. // http://sass-lang.com
  1175. //
  1176. mix: function (color1, color2, weight) {
  1177. if (!weight) {
  1178. weight = new(Dimension)(50);
  1179. }
  1180. var p = weight.value / 100.0;
  1181. var w = p * 2 - 1;
  1182. var a = color1.toHSL().a - color2.toHSL().a;
  1183. var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
  1184. var w2 = 1 - w1;
  1185. var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
  1186. color1.rgb[1] * w1 + color2.rgb[1] * w2,
  1187. color1.rgb[2] * w1 + color2.rgb[2] * w2];
  1188. var alpha = color1.alpha * p + color2.alpha * (1 - p);
  1189. return new(Color)(rgb, alpha);
  1190. },
  1191. greyscale: function (color) {
  1192. return colorFunctions.desaturate(color, new(Dimension)(100));
  1193. },
  1194. contrast: function (color, dark, light, threshold) {
  1195. // filter: contrast(3.2);
  1196. // should be kept as is, so check for color
  1197. if (!color.rgb) {
  1198. return null;
  1199. }
  1200. if (typeof light === 'undefined') {
  1201. light = colorFunctions.rgba(255, 255, 255, 1.0);
  1202. }
  1203. if (typeof dark === 'undefined') {
  1204. dark = colorFunctions.rgba(0, 0, 0, 1.0);
  1205. }
  1206. //Figure out which is actually light and dark!
  1207. if (dark.luma() > light.luma()) {
  1208. var t = light;
  1209. light = dark;
  1210. dark = t;
  1211. }
  1212. if (typeof threshold === 'undefined') {
  1213. threshold = 0.43;
  1214. } else {
  1215. threshold = number(threshold);
  1216. }
  1217. if (color.luma() < threshold) {
  1218. return light;
  1219. } else {
  1220. return dark;
  1221. }
  1222. },
  1223. argb: function (color) {
  1224. return new(Anonymous)(color.toARGB());
  1225. },
  1226. color: function(c) {
  1227. if ((c instanceof Quoted) &&
  1228. (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
  1229. return new(Color)(c.value.slice(1));
  1230. }
  1231. if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
  1232. c.keyword = undefined;
  1233. return c;
  1234. }
  1235. throw {
  1236. type: "Argument",
  1237. message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF"
  1238. };
  1239. },
  1240. tint: function(color, amount) {
  1241. return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount);
  1242. },
  1243. shade: function(color, amount) {
  1244. return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
  1245. }
  1246. };
  1247. functionRegistry.addMultiple(colorFunctions);
  1248. },{"../tree/anonymous.js":27,"../tree/color.js":31,"../tree/dimension.js":37,"../tree/quoted.js":54,"./function-registry.js":12}],9:[function(require,module,exports){
  1249. module.exports = function(environment) {
  1250. var Anonymous = require("../tree/anonymous.js"),
  1251. URL = require("../tree/url.js"),
  1252. functionRegistry = require("./function-registry.js");
  1253. functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) {
  1254. if (!environment.supportsDataURI(this.env)) {
  1255. return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
  1256. }
  1257. var mimetype = mimetypeNode.value;
  1258. var filePath = (filePathNode && filePathNode.value);
  1259. var useBase64 = false;
  1260. if (arguments.length < 2) {
  1261. filePath = mimetype;
  1262. }
  1263. var fragmentStart = filePath.indexOf('#');
  1264. var fragment = '';
  1265. if (fragmentStart!==-1) {
  1266. fragment = filePath.slice(fragmentStart);
  1267. filePath = filePath.slice(0, fragmentStart);
  1268. }
  1269. if (this.env.isPathRelative(filePath)) {
  1270. if (this.currentFileInfo.relativeUrls) {
  1271. filePath = environment.join(this.currentFileInfo.currentDirectory, filePath);
  1272. } else {
  1273. filePath = environment.join(this.currentFileInfo.entryPath, filePath);
  1274. }
  1275. }
  1276. // detect the mimetype if not given
  1277. if (arguments.length < 2) {
  1278. mimetype = environment.mimeLookup(this.env, filePath);
  1279. // use base 64 unless it's an ASCII or UTF-8 format
  1280. var charset = environment.charsetLookup(this.env, mimetype);
  1281. useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
  1282. if (useBase64) { mimetype += ';base64'; }
  1283. }
  1284. else {
  1285. useBase64 = /;base64$/.test(mimetype);
  1286. }
  1287. var buf = environment.readFileSync(filePath);
  1288. // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
  1289. // and the --ieCompat flag is enabled, return a normal url() instead.
  1290. var DATA_URI_MAX_KB = 32,
  1291. fileSizeInKB = parseInt((buf.length / 1024), 10);
  1292. if (fileSizeInKB >= DATA_URI_MAX_KB) {
  1293. if (this.env.ieCompat !== false) {
  1294. if (!this.env.silent) {
  1295. console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
  1296. }
  1297. return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
  1298. }
  1299. }
  1300. buf = useBase64 ? buf.toString('base64')
  1301. : encodeURIComponent(buf);
  1302. var uri = "\"data:" + mimetype + ',' + buf + fragment + "\"";
  1303. return new(URL)(new(Anonymous)(uri));
  1304. });
  1305. };
  1306. },{"../tree/anonymous.js":27,"../tree/url.js":61,"./function-registry.js":12}],10:[function(require,module,exports){
  1307. var Keyword = require("../tree/keyword.js"),
  1308. functionRegistry = require("./function-registry.js");
  1309. var defaultFunc = {
  1310. eval: function () {
  1311. var v = this.value_, e = this.error_;
  1312. if (e) {
  1313. throw e;
  1314. }
  1315. if (v != null) {
  1316. return v ? Keyword.True : Keyword.False;
  1317. }
  1318. },
  1319. value: function (v) {
  1320. this.value_ = v;
  1321. },
  1322. error: function (e) {
  1323. this.error_ = e;
  1324. },
  1325. reset: function () {
  1326. this.value_ = this.error_ = null;
  1327. }
  1328. };
  1329. functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
  1330. module.exports = defaultFunc;
  1331. },{"../tree/keyword.js":46,"./function-registry.js":12}],11:[function(require,module,exports){
  1332. var functionRegistry = require("./function-registry.js");
  1333. var functionCaller = function(name, env, currentFileInfo) {
  1334. this.name = name.toLowerCase();
  1335. this.function = functionRegistry.get(this.name);
  1336. this.env = env;
  1337. this.currentFileInfo = currentFileInfo;
  1338. };
  1339. functionCaller.prototype.isValid = function() {
  1340. return Boolean(this.function);
  1341. };
  1342. functionCaller.prototype.call = function(args) {
  1343. return this.function.apply(this, args);
  1344. };
  1345. module.exports = functionCaller;
  1346. },{"./function-registry.js":12}],12:[function(require,module,exports){
  1347. module.exports = {
  1348. _data: {},
  1349. add: function(name, func) {
  1350. if (this._data.hasOwnProperty(name)) {
  1351. //TODO warn
  1352. }
  1353. this._data[name] = func;
  1354. },
  1355. addMultiple: function(functions) {
  1356. Object.keys(functions).forEach(
  1357. function(name) {
  1358. this.add(name, functions[name]);
  1359. }.bind(this));
  1360. },
  1361. get: function(name) {
  1362. return this._data[name];
  1363. }
  1364. };
  1365. },{}],13:[function(require,module,exports){
  1366. module.exports = function(environment) {
  1367. var functions = {
  1368. functionRegistry: require("./function-registry.js"),
  1369. functionCaller: require("./function-caller.js")
  1370. };
  1371. //register functions
  1372. require("./default.js");
  1373. require("./color.js");
  1374. require("./color-blending.js");
  1375. require("./data-uri.js")(environment);
  1376. require("./math.js");
  1377. require("./number.js");
  1378. require("./string.js");
  1379. require("./svg.js")(environment);
  1380. require("./types.js");
  1381. return functions;
  1382. };
  1383. },{"./color-blending.js":7,"./color.js":8,"./data-uri.js":9,"./default.js":10,"./function-caller.js":11,"./function-registry.js":12,"./math.js":14,"./number.js":15,"./string.js":16,"./svg.js":17,"./types.js":18}],14:[function(require,module,exports){
  1384. var Dimension = require("../tree/dimension.js"),
  1385. functionRegistry = require("./function-registry.js");
  1386. var mathFunctions = {
  1387. // name, unit
  1388. ceil: null,
  1389. floor: null,
  1390. sqrt: null,
  1391. abs: null,
  1392. tan: "",
  1393. sin: "",
  1394. cos: "",
  1395. atan: "rad",
  1396. asin: "rad",
  1397. acos: "rad"
  1398. };
  1399. function _math(fn, unit, n) {
  1400. if (!(n instanceof Dimension)) {
  1401. throw { type: "Argument", message: "argument must be a number" };
  1402. }
  1403. if (unit == null) {
  1404. unit = n.unit;
  1405. } else {
  1406. n = n.unify();
  1407. }
  1408. return new(Dimension)(fn(parseFloat(n.value)), unit);
  1409. }
  1410. for (var f in mathFunctions) {
  1411. if (mathFunctions.hasOwnProperty(f)) {
  1412. mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]);
  1413. }
  1414. }
  1415. mathFunctions.round = function (n, f) {
  1416. var fraction = typeof(f) === "undefined" ? 0 : f.value;
  1417. return _math(function(num) { return num.toFixed(fraction); }, null, n);
  1418. };
  1419. functionRegistry.addMultiple(mathFunctions);
  1420. },{"../tree/dimension.js":37,"./function-registry.js":12}],15:[function(require,module,exports){
  1421. var Dimension = require("../tree/dimension.js"),
  1422. Anonymous = require("../tree/anonymous.js"),
  1423. functionRegistry = require("./function-registry.js");
  1424. var minMax = function (isMin, args) {
  1425. args = Array.prototype.slice.call(args);
  1426. switch(args.length) {
  1427. case 0: throw { type: "Argument", message: "one or more arguments required" };
  1428. }
  1429. var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
  1430. order = [], // elems only contains original argument values.
  1431. values = {}; // key is the unit.toString() for unified Dimension values,
  1432. // value is the index into the order array.
  1433. for (i = 0; i < args.length; i++) {
  1434. current = args[i];
  1435. if (!(current instanceof Dimension)) {
  1436. if(Array.isArray(args[i].value)) {
  1437. Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
  1438. }
  1439. continue;
  1440. }
  1441. currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(Dimension)(current.value, unitClone).unify() : current.unify();
  1442. unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
  1443. unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic;
  1444. unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
  1445. j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
  1446. if (j === undefined) {
  1447. if(unitStatic !== undefined && unit !== unitStatic) {
  1448. throw{ type: "Argument", message: "incompatible types" };
  1449. }
  1450. values[unit] = order.length;
  1451. order.push(current);
  1452. continue;
  1453. }
  1454. referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(Dimension)(order[j].value, unitClone).unify() : order[j].unify();
  1455. if ( isMin && currentUnified.value < referenceUnified.value ||
  1456. !isMin && currentUnified.value > referenceUnified.value) {
  1457. order[j] = current;
  1458. }
  1459. }
  1460. if (order.length == 1) {
  1461. return order[0];
  1462. }
  1463. args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", ");
  1464. return new(Anonymous)((isMin ? "min" : "max") + "(" + args + ")");
  1465. };
  1466. functionRegistry.addMultiple({
  1467. min: function () {
  1468. return minMax(true, arguments);
  1469. },
  1470. max: function () {
  1471. return minMax(false, arguments);
  1472. },
  1473. convert: function (val, unit) {
  1474. return val.convertTo(unit.value);
  1475. },
  1476. pi: function () {
  1477. return new(Dimension)(Math.PI);
  1478. },
  1479. mod: function(a, b) {
  1480. return new(Dimension)(a.value % b.value, a.unit);
  1481. },
  1482. pow: function(x, y) {
  1483. if (typeof x === "number" && typeof y === "number") {
  1484. x = new(Dimension)(x);
  1485. y = new(Dimension)(y);
  1486. } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) {
  1487. throw { type: "Argument", message: "arguments must be numbers" };
  1488. }
  1489. return new(Dimension)(Math.pow(x.value, y.value), x.unit);
  1490. },
  1491. percentage: function (n) {
  1492. return new(Dimension)(n.value * 100, '%');
  1493. }
  1494. });
  1495. },{"../tree/anonymous.js":27,"../tree/dimension.js":37,"./function-registry.js":12}],16:[function(require,module,exports){
  1496. var Quoted = require("../tree/quoted.js"),
  1497. Anonymous = require("../tree/anonymous.js"),
  1498. JavaScript = require("../tree/javascript.js"),
  1499. functionRegistry = require("./function-registry.js");
  1500. functionRegistry.addMultiple({
  1501. e: function (str) {
  1502. return new(Anonymous)(str instanceof JavaScript ? str.evaluated : str.value);
  1503. },
  1504. escape: function (str) {
  1505. return new(Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
  1506. },
  1507. replace: function (string, pattern, replacement, flags) {
  1508. var result = string.value;
  1509. result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value);
  1510. return new(Quoted)(string.quote || '', result, string.escaped);
  1511. },
  1512. '%': function (string /* arg, arg, ...*/) {
  1513. var args = Array.prototype.slice.call(arguments, 1),
  1514. result = string.value;
  1515. for (var i = 0; i < args.length; i++) {
  1516. /*jshint loopfunc:true */
  1517. result = result.replace(/%[sda]/i, function(token) {
  1518. var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
  1519. return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
  1520. });
  1521. }
  1522. result = result.replace(/%%/g, '%');
  1523. return new(Quoted)(string.quote || '', result, string.escaped);
  1524. }
  1525. });
  1526. },{"../tree/anonymous.js":27,"../tree/javascript.js":44,"../tree/quoted.js":54,"./function-registry.js":12}],17:[function(require,module,exports){
  1527. module.exports = function(environment) {
  1528. var Dimension = require("../tree/dimension.js"),
  1529. Color = require("../tree/color.js"),
  1530. Anonymous = require("../tree/anonymous.js"),
  1531. URL = require("../tree/url.js"),
  1532. functionRegistry = require("./function-registry.js");
  1533. functionRegistry.add("svg-gradient", function(direction) {
  1534. function throwArgumentDescriptor() {
  1535. throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
  1536. }
  1537. if (arguments.length < 3) {
  1538. throwArgumentDescriptor();
  1539. }
  1540. var stops = Array.prototype.slice.call(arguments, 1),
  1541. gradientDirectionSvg,
  1542. gradientType = "linear",
  1543. rectangleDimension = 'x="0" y="0" width="1" height="1"',
  1544. useBase64 = true,
  1545. renderEnv = {compress: false},
  1546. returner,
  1547. directionValue = direction.toCSS(renderEnv),
  1548. i, color, position, positionValue, alpha;
  1549. switch (directionValue) {
  1550. case "to bottom":
  1551. gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
  1552. break;
  1553. case "to right":
  1554. gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
  1555. break;
  1556. case "to bottom right":
  1557. gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
  1558. break;
  1559. case "to top right":
  1560. gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
  1561. break;
  1562. case "ellipse":
  1563. case "ellipse at center":
  1564. gradientType = "radial";
  1565. gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
  1566. rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
  1567. break;
  1568. default:
  1569. throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
  1570. }
  1571. returner = '<?xml version="1.0" ?>' +
  1572. '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
  1573. '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
  1574. for (i = 0; i < stops.length; i+= 1) {
  1575. if (stops[i].value) {
  1576. color = stops[i].value[0];
  1577. position = stops[i].value[1];
  1578. } else {
  1579. color = stops[i];
  1580. position = undefined;
  1581. }
  1582. if (!(color instanceof Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
  1583. throwArgumentDescriptor();
  1584. }
  1585. positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
  1586. alpha = color.alpha;
  1587. returner += '<stop offset="' + positionValue + '" stop-color="' + color.toRGB() + '"' + (alpha < 1 ? ' stop-opacity="' + alpha + '"' : '') + '/>';
  1588. }
  1589. returner += '</' + gradientType + 'Gradient>' +
  1590. '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
  1591. if (useBase64) {
  1592. try {
  1593. returner = environment.encodeBase64(this.env, returner);
  1594. } catch(e) {
  1595. useBase64 = false;
  1596. }
  1597. }
  1598. returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
  1599. return new(URL)(new(Anonymous)(returner));
  1600. });
  1601. };
  1602. },{"../tree/anonymous.js":27,"../tree/color.js":31,"../tree/dimension.js":37,"../tree/url.js":61,"./function-registry.js":12}],18:[function(require,module,exports){
  1603. var Keyword = require("../tree/keyword.js"),
  1604. Dimension = require("../tree/dimension.js"),
  1605. Color = require("../tree/color.js"),
  1606. Quoted = require("../tree/quoted.js"),
  1607. Anonymous = require("../tree/anonymous.js"),
  1608. URL = require("../tree/url.js"),
  1609. Operation = require("../tree/operation.js"),
  1610. functionRegistry = require("./function-registry.js");
  1611. var isa = function (n, Type) {
  1612. return (n instanceof Type) ? Keyword.True : Keyword.False;
  1613. },
  1614. isunit = function (n, unit) {
  1615. return (n instanceof Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False;
  1616. };
  1617. functionRegistry.addMultiple({
  1618. iscolor: function (n) {
  1619. return isa(n, Color);
  1620. },
  1621. isnumber: function (n) {
  1622. return isa(n, Dimension);
  1623. },
  1624. isstring: function (n) {
  1625. return isa(n, Quoted);
  1626. },
  1627. iskeyword: function (n) {
  1628. return isa(n, Keyword);
  1629. },
  1630. isurl: function (n) {
  1631. return isa(n, URL);
  1632. },
  1633. ispixel: function (n) {
  1634. return isunit(n, 'px');
  1635. },
  1636. ispercentage: function (n) {
  1637. return isunit(n, '%');
  1638. },
  1639. isem: function (n) {
  1640. return isunit(n, 'em');
  1641. },
  1642. isunit: isunit,
  1643. unit: function (val, unit) {
  1644. if(!(val instanceof Dimension)) {
  1645. throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
  1646. }
  1647. if (unit) {
  1648. if (unit instanceof Keyword) {
  1649. unit = unit.value;
  1650. } else {
  1651. unit = unit.toCSS();
  1652. }
  1653. } else {
  1654. unit = "";
  1655. }
  1656. return new(Dimension)(val.value, unit);
  1657. },
  1658. "get-unit": function (n) {
  1659. return new(Anonymous)(n.unit);
  1660. },
  1661. extract: function(values, index) {
  1662. index = index.value - 1; // (1-based index)
  1663. // handle non-array values as an array of length 1
  1664. // return 'undefined' if index is invalid
  1665. return Array.isArray(values.value) ?
  1666. values.value[index] : Array(values)[index];
  1667. },
  1668. length: function(values) {
  1669. var n = Array.isArray(values.value) ? values.value.length : 1;
  1670. return new Dimension(n);
  1671. }
  1672. });
  1673. },{"../tree/anonymous.js":27,"../tree/color.js":31,"../tree/dimension.js":37,"../tree/keyword.js":46,"../tree/operation.js":52,"../tree/quoted.js":54,"../tree/url.js":61,"./function-registry.js":12}],19:[function(require,module,exports){
  1674. var LessError = module.exports = function LessError(parser, e, env) {
  1675. var input = parser.getInput(e, env),
  1676. loc = parser.getLocation(e.index, input),
  1677. line = loc.line,
  1678. col = loc.column,
  1679. callLine = e.call && parser.getLocation(e.call, input).line,
  1680. lines = input.split('\n');
  1681. this.type = e.type || 'Syntax';
  1682. this.message = e.message;
  1683. this.filename = e.filename || env.currentFileInfo.filename;
  1684. this.index = e.index;
  1685. this.line = typeof(line) === 'number' ? line + 1 : null;
  1686. this.callLine = callLine + 1;
  1687. this.callExtract = lines[callLine];
  1688. this.stack = e.stack;
  1689. this.column = col;
  1690. this.extract = [
  1691. lines[line - 1],
  1692. lines[line],
  1693. lines[line + 1]
  1694. ];
  1695. };
  1696. LessError.prototype = new Error();
  1697. LessError.prototype.constructor = LessError;
  1698. },{}],20:[function(require,module,exports){
  1699. module.exports = function(environment) {
  1700. var less = {
  1701. version: [2, 0, 0],
  1702. data: require('./data/index.js'),
  1703. tree: require('./tree/index.js'),
  1704. visitor: require('./visitor/index.js'),
  1705. Parser: require('./parser/parser.js')(environment),
  1706. functions: require('./functions/index.js')(environment),
  1707. contexts: require("./contexts.js"),
  1708. environment: environment
  1709. };
  1710. return less;
  1711. };
  1712. },{"./contexts.js":2,"./data/index.js":4,"./functions/index.js":13,"./parser/parser.js":24,"./tree/index.js":43,"./visitor/index.js":66}],21:[function(require,module,exports){
  1713. // Split the input into chunks.
  1714. module.exports = function (input, fail) {
  1715. var len = input.length, level = 0, parenLevel = 0,
  1716. lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
  1717. chunks = [], emitFrom = 0,
  1718. chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
  1719. function emitChunk(force) {
  1720. var len = chunkerCurrentIndex - emitFrom;
  1721. if (((len < 512) && !force) || !len) {
  1722. return;
  1723. }
  1724. chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1));
  1725. emitFrom = chunkerCurrentIndex + 1;
  1726. }
  1727. for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
  1728. cc = input.charCodeAt(chunkerCurrentIndex);
  1729. if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
  1730. // a-z or whitespace
  1731. continue;
  1732. }
  1733. switch (cc) {
  1734. case 40: // (
  1735. parenLevel++;
  1736. lastOpeningParen = chunkerCurrentIndex;
  1737. continue;
  1738. case 41: // )
  1739. if (--parenLevel < 0) {
  1740. return fail("missing opening `(`", chunkerCurrentIndex);
  1741. }
  1742. continue;
  1743. case 59: // ;
  1744. if (!parenLevel) { emitChunk(); }
  1745. continue;
  1746. case 123: // {
  1747. level++;
  1748. lastOpening = chunkerCurrentIndex;
  1749. continue;
  1750. case 125: // }
  1751. if (--level < 0) {
  1752. return fail("missing opening `{`", chunkerCurrentIndex);
  1753. }
  1754. if (!level && !parenLevel) { emitChunk(); }
  1755. continue;
  1756. case 92: // \
  1757. if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; }
  1758. return fail("unescaped `\\`", chunkerCurrentIndex);
  1759. case 34:
  1760. case 39:
  1761. case 96: // ", ' and `
  1762. matched = 0;
  1763. currentChunkStartIndex = chunkerCurrentIndex;
  1764. for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
  1765. cc2 = input.charCodeAt(chunkerCurrentIndex);
  1766. if (cc2 > 96) { continue; }
  1767. if (cc2 == cc) { matched = 1; break; }
  1768. if (cc2 == 92) { // \
  1769. if (chunkerCurrentIndex == len - 1) {
  1770. return fail("unescaped `\\`", chunkerCurrentIndex);
  1771. }
  1772. chunkerCurrentIndex++;
  1773. }
  1774. }
  1775. if (matched) { continue; }
  1776. return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
  1777. case 47: // /, check for comment
  1778. if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; }
  1779. cc2 = input.charCodeAt(chunkerCurrentIndex + 1);
  1780. if (cc2 == 47) {
  1781. // //, find lnfeed
  1782. for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) {
  1783. cc2 = input.charCodeAt(chunkerCurrentIndex);
  1784. if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
  1785. }
  1786. } else if (cc2 == 42) {
  1787. // /*, find */
  1788. lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex;
  1789. for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) {
  1790. cc2 = input.charCodeAt(chunkerCurrentIndex);
  1791. if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; }
  1792. if (cc2 != 42) { continue; }
  1793. if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; }
  1794. }
  1795. if (chunkerCurrentIndex == len - 1) {
  1796. return fail("missing closing `*/`", currentChunkStartIndex);
  1797. }
  1798. chunkerCurrentIndex++;
  1799. }
  1800. continue;
  1801. case 42: // *, check for unmatched */
  1802. if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) {
  1803. return fail("unmatched `/*`", chunkerCurrentIndex);
  1804. }
  1805. continue;
  1806. }
  1807. }
  1808. if (level !== 0) {
  1809. if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
  1810. return fail("missing closing `}` or `*/`", lastOpening);
  1811. } else {
  1812. return fail("missing closing `}`", lastOpening);
  1813. }
  1814. } else if (parenLevel !== 0) {
  1815. return fail("missing closing `)`", lastOpeningParen);
  1816. }
  1817. emitChunk(true);
  1818. return chunks;
  1819. };
  1820. },{}],22:[function(require,module,exports){
  1821. var contexts = require("../contexts.js");
  1822. module.exports = function(environment, env, Parser) {
  1823. var rootFilename = env && env.filename;
  1824. return {
  1825. paths: env.paths || [], // Search paths, when importing
  1826. queue: [], // Files which haven't been imported yet
  1827. files: env.files, // Holds the imported parse trees
  1828. contents: env.contents, // Holds the imported file contents
  1829. contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less
  1830. mime: env.mime, // MIME type of .less files
  1831. error: null, // Error in parsing/evaluating an import
  1832. push: function (path, currentFileInfo, importOptions, callback) {
  1833. var parserImports = this;
  1834. this.queue.push(path);
  1835. var fileParsedFunc = function (e, root, fullPath) {
  1836. parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue
  1837. var importedPreviously = fullPath === rootFilename;
  1838. parserImports.files[fullPath] = root; // Store the root
  1839. if (e && !parserImports.error) { parserImports.error = e; }
  1840. callback(e, root, importedPreviously, fullPath);
  1841. };
  1842. var newFileInfo = {
  1843. relativeUrls: env.relativeUrls,
  1844. entryPath: currentFileInfo.entryPath,
  1845. rootpath: currentFileInfo.rootpath,
  1846. rootFilename: currentFileInfo.rootFilename
  1847. };
  1848. environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) {
  1849. if (e) {
  1850. fileParsedFunc(e);
  1851. return;
  1852. }
  1853. // Pass on an updated rootpath if path of imported file is relative and file
  1854. // is in a (sub|sup) directory
  1855. //
  1856. // Examples:
  1857. // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
  1858. // then rootpath should become 'less/module/nav/'
  1859. // - If path of imported file is '../mixins.less' and rootpath is 'less/',
  1860. // then rootpath should become 'less/../'
  1861. newFileInfo.currentDirectory = environment.getPath(env, resolvedFilename);
  1862. if(newFileInfo.relativeUrls) {
  1863. newFileInfo.rootpath = environment.join((env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
  1864. if (!environment.isPathAbsolute(env, newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) {
  1865. newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath);
  1866. }
  1867. }
  1868. newFileInfo.filename = resolvedFilename;
  1869. var newEnv = new contexts.parseEnv(env);
  1870. newEnv.currentFileInfo = newFileInfo;
  1871. newEnv.processImports = false;
  1872. newEnv.contents[resolvedFilename] = contents;
  1873. if (currentFileInfo.reference || importOptions.reference) {
  1874. newFileInfo.reference = true;
  1875. }
  1876. if (importOptions.inline) {
  1877. fileParsedFunc(null, contents, resolvedFilename);
  1878. } else {
  1879. new(Parser)(newEnv).parse(contents, function (e, root) {
  1880. fileParsedFunc(e, root, resolvedFilename);
  1881. });
  1882. }
  1883. });
  1884. }
  1885. };
  1886. };
  1887. },{"../contexts.js":2}],23:[function(require,module,exports){
  1888. var chunker = require('./chunker.js'),
  1889. LessError = require('../less-error.js');
  1890. module.exports = function() {
  1891. var input, // LeSS input string
  1892. j, // current chunk
  1893. saveStack = [], // holds state for backtracking
  1894. furthest, // furthest index the parser has gone to
  1895. furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause
  1896. chunks, // chunkified input
  1897. current, // current chunk
  1898. currentPos, // index of current chunk, in `input`
  1899. parserInput = {};
  1900. parserInput.save = function() {
  1901. currentPos = parserInput.i;
  1902. saveStack.push( { current: current, i: parserInput.i, j: j });
  1903. };
  1904. parserInput.restore = function(possibleErrorMessage) {
  1905. if (parserInput.i > furthest) {
  1906. furthest = parserInput.i;
  1907. furthestPossibleErrorMessage = possibleErrorMessage;
  1908. }
  1909. var state = saveStack.pop();
  1910. current = state.current;
  1911. currentPos = parserInput.i = state.i;
  1912. j = state.j;
  1913. };
  1914. parserInput.forget = function() {
  1915. saveStack.pop();
  1916. };
  1917. function sync() {
  1918. if (parserInput.i > currentPos) {
  1919. current = current.slice(parserInput.i - currentPos);
  1920. currentPos = parserInput.i;
  1921. }
  1922. }
  1923. parserInput.isWhitespace = function (offset) {
  1924. var pos = parserInput.i + (offset || 0),
  1925. code = input.charCodeAt(pos);
  1926. return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF);
  1927. };
  1928. //
  1929. // Parse from a token, regexp or string, and move forward if match
  1930. //
  1931. parserInput.$ = function(tok) {
  1932. var tokType = typeof tok,
  1933. match, length;
  1934. // Either match a single character in the input,
  1935. // or match a regexp in the current chunk (`current`).
  1936. //
  1937. if (tokType === "string") {
  1938. if (input.charAt(parserInput.i) !== tok) {
  1939. return null;
  1940. }
  1941. skipWhitespace(1);
  1942. return tok;
  1943. }
  1944. // regexp
  1945. sync();
  1946. if (! (match = tok.exec(current))) {
  1947. return null;
  1948. }
  1949. length = match[0].length;
  1950. // The match is confirmed, add the match length to `i`,
  1951. // and consume any extra white-space characters (' ' || '\n')
  1952. // which come after that. The reason for this is that LeSS's
  1953. // grammar is mostly white-space insensitive.
  1954. //
  1955. skipWhitespace(length);
  1956. if(typeof(match) === 'string') {
  1957. return match;
  1958. } else {
  1959. return match.length === 1 ? match[0] : match;
  1960. }
  1961. };
  1962. // Specialization of $(tok)
  1963. parserInput.$re = function(tok) {
  1964. if (parserInput.i > currentPos) {
  1965. current = current.slice(parserInput.i - currentPos);
  1966. currentPos = parserInput.i;
  1967. }
  1968. var m = tok.exec(current);
  1969. if (!m) {
  1970. return null;
  1971. }
  1972. skipWhitespace(m[0].length);
  1973. if(typeof m === "string") {
  1974. return m;
  1975. }
  1976. return m.length === 1 ? m[0] : m;
  1977. };
  1978. // Specialization of $(tok)
  1979. parserInput.$char = function(tok) {
  1980. if (input.charAt(parserInput.i) !== tok) {
  1981. return null;
  1982. }
  1983. skipWhitespace(1);
  1984. return tok;
  1985. };
  1986. var CHARCODE_SPACE = 32,
  1987. CHARCODE_TAB = 9,
  1988. CHARCODE_LF = 10,
  1989. CHARCODE_CR = 13,
  1990. CHARCODE_PLUS = 43,
  1991. CHARCODE_COMMA = 44,
  1992. CHARCODE_FORWARD_SLASH = 47,
  1993. CHARCODE_9 = 57;
  1994. parserInput.autoCommentAbsorb = true;
  1995. parserInput.commentStore = [];
  1996. parserInput.finished = false;
  1997. var skipWhitespace = function(length) {
  1998. var oldi = parserInput.i, oldj = j,
  1999. curr = parserInput.i - currentPos,
  2000. endIndex = parserInput.i + current.length - curr,
  2001. mem = (parserInput.i += length),
  2002. inp = input,
  2003. c, nextChar, comment;
  2004. for (; parserInput.i < endIndex; parserInput.i++) {
  2005. c = inp.charCodeAt(parserInput.i);
  2006. if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
  2007. nextChar = inp[parserInput.i + 1];
  2008. if (nextChar === '/') {
  2009. comment = {index: parserInput.i, isLineComment: true};
  2010. var nextNewLine = inp.indexOf("\n", parserInput.i + 1);
  2011. if (nextNewLine < 0) {
  2012. nextNewLine = endIndex;
  2013. }
  2014. parserInput.i = nextNewLine;
  2015. comment.text = inp.substr(comment.i, parserInput.i - comment.i);
  2016. parserInput.commentStore.push(comment);
  2017. continue;
  2018. } else if (nextChar === '*') {
  2019. var haystack = inp.substr(parserInput.i);
  2020. var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//);
  2021. if (comment_search_result) {
  2022. comment = {
  2023. index: parserInput.i,
  2024. text: comment_search_result[0],
  2025. isLineComment: false
  2026. };
  2027. parserInput.i += comment.text.length - 1;
  2028. parserInput.commentStore.push(comment);
  2029. continue;
  2030. }
  2031. }
  2032. break;
  2033. }
  2034. if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
  2035. break;
  2036. }
  2037. }
  2038. current = current.slice(length + parserInput.i - mem + curr);
  2039. currentPos = parserInput.i;
  2040. if (!current.length) {
  2041. if (j < chunks.length - 1)
  2042. {
  2043. current = chunks[++j];
  2044. skipWhitespace(0); // skip space at the beginning of a chunk
  2045. return true; // things changed
  2046. }
  2047. parserInput.finished = true;
  2048. }
  2049. return oldi !== parserInput.i || oldj !== j;
  2050. };
  2051. // Same as $(), but don't change the state of the parser,
  2052. // just return the match.
  2053. parserInput.peek = function(tok) {
  2054. if (typeof(tok) === 'string') {
  2055. return input.charAt(parserInput.i) === tok;
  2056. } else {
  2057. return tok.test(current);
  2058. }
  2059. };
  2060. // Specialization of peek()
  2061. // TODO remove or change some currentChar calls to peekChar
  2062. parserInput.peekChar = function(tok) {
  2063. return input.charAt(parserInput.i) === tok;
  2064. };
  2065. parserInput.currentChar = function() {
  2066. return input.charAt(parserInput.i);
  2067. };
  2068. parserInput.getInput = function() {
  2069. return input;
  2070. };
  2071. parserInput.peekNotNumeric = function() {
  2072. var c = input.charCodeAt(parserInput.i);
  2073. //Is the first char of the dimension 0-9, '.', '+' or '-'
  2074. return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
  2075. };
  2076. parserInput.getLocation = function(index, inputStream) {
  2077. inputStream = inputStream == null ? input : inputStream;
  2078. var n = index + 1,
  2079. line = null,
  2080. column = -1;
  2081. while (--n >= 0 && inputStream.charAt(n) !== '\n') {
  2082. column++;
  2083. }
  2084. if (typeof index === 'number') {
  2085. line = (inputStream.slice(0, index).match(/\n/g) || "").length;
  2086. }
  2087. return {
  2088. line: line,
  2089. column: column
  2090. };
  2091. };
  2092. parserInput.start = function(str, chunkInput, parser, env) {
  2093. input = str;
  2094. parserInput.i = j = currentPos = furthest = 0;
  2095. // chunking apparantly makes things quicker (but my tests indicate
  2096. // it might actually make things slower in node at least)
  2097. // and it is a non-perfect parse - it can't recognise
  2098. // unquoted urls, meaning it can't distinguish comments
  2099. // meaning comments with quotes or {}() in them get 'counted'
  2100. // and then lead to parse errors.
  2101. // In addition if the chunking chunks in the wrong place we might
  2102. // not be able to parse a parser statement in one go
  2103. // this is officially deprecated but can be switched on via an option
  2104. // in the case it causes too much performance issues.
  2105. if (chunkInput) {
  2106. chunks = chunker(str, function fail(msg, index) {
  2107. throw new(LessError)(parser, {
  2108. index: index,
  2109. type: 'Parse',
  2110. message: msg,
  2111. filename: env.currentFileInfo.filename
  2112. }, env);
  2113. });
  2114. } else {
  2115. chunks = [str];
  2116. }
  2117. current = chunks[0];
  2118. skipWhitespace(0);
  2119. };
  2120. parserInput.end = function() {
  2121. var message,
  2122. isFinished = parserInput.i >= input.length - 1;
  2123. if (parserInput.i < furthest) {
  2124. message = furthestPossibleErrorMessage;
  2125. parserInput.i = furthest;
  2126. }
  2127. return {
  2128. isFinished: isFinished,
  2129. furthest: parserInput.i,
  2130. furthestPossibleErrorMessage: message,
  2131. furthestReachedEnd: parserInput.i >= input.length - 1,
  2132. furthestChar: input[parserInput.i]
  2133. };
  2134. };
  2135. return parserInput;
  2136. };
  2137. },{"../less-error.js":19,"./chunker.js":21}],24:[function(require,module,exports){
  2138. var LessError = require('../less-error.js'),
  2139. tree = require("../tree/index.js"),
  2140. visitor = require("../visitor/index.js"),
  2141. contexts = require("../contexts.js"),
  2142. getImportManager = require("./imports.js"),
  2143. getParserInput = require("./parser-input.js");
  2144. module.exports = function(environment) {
  2145. var SourceMapOutput = require("../source-map-output")(environment);
  2146. //
  2147. // less.js - parser
  2148. //
  2149. // A relatively straight-forward predictive parser.
  2150. // There is no tokenization/lexing stage, the input is parsed
  2151. // in one sweep.
  2152. //
  2153. // To make the parser fast enough to run in the browser, several
  2154. // optimization had to be made:
  2155. //
  2156. // - Matching and slicing on a huge input is often cause of slowdowns.
  2157. // The solution is to chunkify the input into smaller strings.
  2158. // The chunks are stored in the `chunks` var,
  2159. // `j` holds the current chunk index, and `currentPos` holds
  2160. // the index of the current chunk in relation to `input`.
  2161. // This gives us an almost 4x speed-up.
  2162. //
  2163. // - In many cases, we don't need to match individual tokens;
  2164. // for example, if a value doesn't hold any variables, operations
  2165. // or dynamic references, the parser can effectively 'skip' it,
  2166. // treating it as a literal.
  2167. // An example would be '1px solid #000' - which evaluates to itself,
  2168. // we don't need to know what the individual components are.
  2169. // The drawback, of course is that you don't get the benefits of
  2170. // syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
  2171. // and a smaller speed-up in the code-gen.
  2172. //
  2173. //
  2174. // Token matching is done with the `$` function, which either takes
  2175. // a terminal string or regexp, or a non-terminal function to call.
  2176. // It also takes care of moving all the indices forwards.
  2177. //
  2178. //
  2179. var Parser = function Parser(env) {
  2180. var parser,
  2181. parsers,
  2182. parserInput = getParserInput();
  2183. // Top parser on an import tree must be sure there is one "env"
  2184. // which will then be passed around by reference.
  2185. if (!(env instanceof contexts.parseEnv)) {
  2186. env = new contexts.parseEnv(env);
  2187. }
  2188. this.env = env;
  2189. var imports = this.imports = getImportManager(environment, env, Parser);
  2190. function expect(arg, msg, index) {
  2191. // some older browsers return typeof 'function' for RegExp
  2192. var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg);
  2193. if (result) {
  2194. return result;
  2195. }
  2196. error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'"
  2197. : "unexpected token"));
  2198. }
  2199. // Specialization of expect()
  2200. function expectChar(arg, msg) {
  2201. if (parserInput.$char(arg)) {
  2202. return arg;
  2203. }
  2204. error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'");
  2205. }
  2206. function error(msg, type) {
  2207. var e = new Error(msg);
  2208. e.index = parserInput.i;
  2209. e.type = type || 'Syntax';
  2210. throw e;
  2211. }
  2212. function getInput(e, env) {
  2213. if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) {
  2214. return parser.imports.contents[e.filename];
  2215. } else {
  2216. return parserInput.getInput();
  2217. }
  2218. }
  2219. function getDebugInfo(index) {
  2220. var filename = env.currentFileInfo.filename;
  2221. filename = environment.getAbsolutePath(env, filename);
  2222. return {
  2223. lineNumber: parserInput.getLocation(index).line + 1,
  2224. fileName: filename
  2225. };
  2226. }
  2227. //
  2228. // The Parser
  2229. //
  2230. parser = {
  2231. imports: imports,
  2232. //
  2233. // Parse an input string into an abstract syntax tree,
  2234. // @param str A string containing 'less' markup
  2235. // @param callback call `callback` when done.
  2236. // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
  2237. //
  2238. parse: function (str, callback, additionalData) {
  2239. var root, error = null, globalVars, modifyVars, preText = "";
  2240. globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : '';
  2241. modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : '';
  2242. if (globalVars || (additionalData && additionalData.banner)) {
  2243. preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
  2244. parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length;
  2245. }
  2246. str = str.replace(/\r\n/g, '\n');
  2247. // Remove potential UTF Byte Order Mark
  2248. str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
  2249. parser.imports.contents[env.currentFileInfo.filename] = str;
  2250. // Start with the primary rule.
  2251. // The whole syntax tree is held under a Ruleset node,
  2252. // with the `root` property set to true, so no `{}` are
  2253. // output. The callback is called when the input is parsed.
  2254. try {
  2255. parserInput.start(str, env.chunkInput, parser, env);
  2256. root = new(tree.Ruleset)(null, this.parsers.primary());
  2257. root.root = true;
  2258. root.firstRoot = true;
  2259. } catch (e) {
  2260. return callback(new LessError(parser, e, env));
  2261. }
  2262. root.toCSS = (function (evaluate) {
  2263. return function (options, variables) {
  2264. options = options || {};
  2265. var evaldRoot,
  2266. css,
  2267. evalEnv = new contexts.evalEnv(options);
  2268. //
  2269. // Allows setting variables with a hash, so:
  2270. //
  2271. // `{ color: new(tree.Color)('#f01') }` will become:
  2272. //
  2273. // new(tree.Rule)('@color',
  2274. // new(tree.Value)([
  2275. // new(tree.Expression)([
  2276. // new(tree.Color)('#f01')
  2277. // ])
  2278. // ])
  2279. // )
  2280. //
  2281. if (typeof(variables) === 'object' && !Array.isArray(variables)) {
  2282. variables = Object.keys(variables).map(function (k) {
  2283. var value = variables[k];
  2284. if (! (value instanceof tree.Value)) {
  2285. if (! (value instanceof tree.Expression)) {
  2286. value = new(tree.Expression)([value]);
  2287. }
  2288. value = new(tree.Value)([value]);
  2289. }
  2290. return new(tree.Rule)('@' + k, value, false, null, 0);
  2291. });
  2292. evalEnv.frames = [new(tree.Ruleset)(null, variables)];
  2293. }
  2294. try {
  2295. var preEvalVisitors = [],
  2296. visitors = [
  2297. new(visitor.JoinSelectorVisitor)(),
  2298. new(visitor.ExtendVisitor)(),
  2299. new(visitor.ToCSSVisitor)({compress: Boolean(options.compress)})
  2300. ], i, root = this;
  2301. if (options.plugins) {
  2302. for(i =0; i < options.plugins.length; i++) {
  2303. if (options.plugins[i].isPreEvalVisitor) {
  2304. preEvalVisitors.push(options.plugins[i]);
  2305. } else {
  2306. if (options.plugins[i].isPreVisitor) {
  2307. visitors.splice(0, 0, options.plugins[i]);
  2308. } else {
  2309. visitors.push(options.plugins[i]);
  2310. }
  2311. }
  2312. }
  2313. }
  2314. for(i = 0; i < preEvalVisitors.length; i++) {
  2315. preEvalVisitors[i].run(root);
  2316. }
  2317. evaldRoot = evaluate.call(root, evalEnv);
  2318. for(i = 0; i < visitors.length; i++) {
  2319. visitors[i].run(evaldRoot);
  2320. }
  2321. if (options.sourceMap) {
  2322. evaldRoot = new SourceMapOutput(
  2323. {
  2324. contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars,
  2325. writeSourceMap: options.writeSourceMap,
  2326. rootNode: evaldRoot,
  2327. contentsMap: parser.imports.contents,
  2328. sourceMapFilename: options.sourceMapFilename,
  2329. sourceMapURL: options.sourceMapURL,
  2330. outputFilename: options.sourceMapOutputFilename,
  2331. sourceMapBasepath: options.sourceMapBasepath,
  2332. sourceMapRootpath: options.sourceMapRootpath,
  2333. outputSourceFiles: options.outputSourceFiles,
  2334. sourceMapGenerator: options.sourceMapGenerator
  2335. });
  2336. }
  2337. css = evaldRoot.toCSS({
  2338. compress: Boolean(options.compress),
  2339. dumpLineNumbers: env.dumpLineNumbers,
  2340. strictUnits: Boolean(options.strictUnits),
  2341. numPrecision: 8});
  2342. } catch (e) {
  2343. throw new LessError(parser, e, env);
  2344. }
  2345. var CleanCSS = environment.getCleanCSS();
  2346. if (options.cleancss && CleanCSS) {
  2347. var cleancssOptions = options.cleancssOptions || {};
  2348. if (cleancssOptions.keepSpecialComments === undefined) {
  2349. cleancssOptions.keepSpecialComments = "*";
  2350. }
  2351. cleancssOptions.processImport = false;
  2352. cleancssOptions.noRebase = true;
  2353. if (cleancssOptions.noAdvanced === undefined) {
  2354. cleancssOptions.noAdvanced = true;
  2355. }
  2356. return new CleanCSS(cleancssOptions).minify(css);
  2357. } else if (options.compress) {
  2358. return css.replace(/(^(\s)+)|((\s)+$)/g, "");
  2359. } else {
  2360. return css;
  2361. }
  2362. };
  2363. })(root.eval);
  2364. // If `i` is smaller than the `input.length - 1`,
  2365. // it means the parser wasn't able to parse the whole
  2366. // string, so we've got a parsing error.
  2367. //
  2368. // We try to extract a \n delimited string,
  2369. // showing the line where the parse error occurred.
  2370. // We split it up into two parts (the part which parsed,
  2371. // and the part which didn't), so we can color them differently.
  2372. var endInfo = parserInput.end();
  2373. if (!endInfo.isFinished) {
  2374. var message = endInfo.furthestPossibleErrorMessage;
  2375. if (!message) {
  2376. message = "Unrecognised input";
  2377. if (endInfo.furthestChar === '}') {
  2378. message += ". Possibly missing opening '{'";
  2379. } else if (endInfo.furthestChar === ')') {
  2380. message += ". Possibly missing opening '('";
  2381. } else if (endInfo.furthestReachedEnd) {
  2382. message += ". Possibly missing something";
  2383. }
  2384. }
  2385. error = new LessError(parser, {
  2386. type: "Parse",
  2387. message: message,
  2388. index: endInfo.furthest,
  2389. filename: env.currentFileInfo.filename
  2390. }, env);
  2391. }
  2392. var finish = function (e) {
  2393. e = error || e || parser.imports.error;
  2394. if (e) {
  2395. if (!(e instanceof LessError)) {
  2396. e = new LessError(parser, e, env);
  2397. }
  2398. return callback(e);
  2399. }
  2400. else {
  2401. return callback(null, root);
  2402. }
  2403. };
  2404. if (env.processImports !== false) {
  2405. new visitor.ImportVisitor(this.imports, finish)
  2406. .run(root);
  2407. } else {
  2408. return finish();
  2409. }
  2410. },
  2411. //
  2412. // Here in, the parsing rules/functions
  2413. //
  2414. // The basic structure of the syntax tree generated is as follows:
  2415. //
  2416. // Ruleset -> Rule -> Value -> Expression -> Entity
  2417. //
  2418. // Here's some Less code:
  2419. //
  2420. // .class {
  2421. // color: #fff;
  2422. // border: 1px solid #000;
  2423. // width: @w + 4px;
  2424. // > .child {...}
  2425. // }
  2426. //
  2427. // And here's what the parse tree might look like:
  2428. //
  2429. // Ruleset (Selector '.class', [
  2430. // Rule ("color", Value ([Expression [Color #fff]]))
  2431. // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
  2432. // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
  2433. // Ruleset (Selector [Element '>', '.child'], [...])
  2434. // ])
  2435. //
  2436. // In general, most rules will try to parse a token with the `$()` function, and if the return
  2437. // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
  2438. // first, before parsing, that's when we use `peek()`.
  2439. //
  2440. parsers: parsers = {
  2441. //
  2442. // The `primary` rule is the *entry* and *exit* point of the parser.
  2443. // The rules here can appear at any level of the parse tree.
  2444. //
  2445. // The recursive nature of the grammar is an interplay between the `block`
  2446. // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
  2447. // as represented by this simplified grammar:
  2448. //
  2449. // primary → (ruleset | rule)+
  2450. // ruleset → selector+ block
  2451. // block → '{' primary '}'
  2452. //
  2453. // Only at one point is the primary rule not called from the
  2454. // block rule: at the root level.
  2455. //
  2456. primary: function () {
  2457. var mixin = this.mixin, root = [], node;
  2458. while (!parserInput.finished)
  2459. {
  2460. while(true) {
  2461. node = this.comment();
  2462. if (!node) { break; }
  2463. root.push(node);
  2464. }
  2465. if (parserInput.peek('}')) {
  2466. break;
  2467. }
  2468. node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||
  2469. mixin.call() || this.rulesetCall() || this.directive();
  2470. if (node) {
  2471. root.push(node);
  2472. } else {
  2473. if (!(parserInput.$re(/^[\s\n]+/) || parserInput.$re(/^;+/))) {
  2474. break;
  2475. }
  2476. }
  2477. }
  2478. return root;
  2479. },
  2480. // comments are collected by the main parsing mechanism and then assigned to nodes
  2481. // where the current structure allows it
  2482. comment: function () {
  2483. if (parserInput.commentStore.length) {
  2484. var comment = parserInput.commentStore.shift();
  2485. return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, env.currentFileInfo);
  2486. }
  2487. },
  2488. //
  2489. // Entities are tokens which can be found inside an Expression
  2490. //
  2491. entities: {
  2492. //
  2493. // A string, which supports escaping " and '
  2494. //
  2495. // "milky way" 'he\'s the one!'
  2496. //
  2497. quoted: function () {
  2498. var str, index = parserInput.i;
  2499. str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/);
  2500. if (str) {
  2501. return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, env.currentFileInfo);
  2502. }
  2503. },
  2504. //
  2505. // A catch-all word, such as:
  2506. //
  2507. // black border-collapse
  2508. //
  2509. keyword: function () {
  2510. var k = parserInput.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);
  2511. if (k) {
  2512. return tree.Color.fromKeyword(k) || new(tree.Keyword)(k);
  2513. }
  2514. },
  2515. //
  2516. // A function call
  2517. //
  2518. // rgb(255, 0, 255)
  2519. //
  2520. // We also try to catch IE's `alpha()`, but let the `alpha` parser
  2521. // deal with the details.
  2522. //
  2523. // The arguments are parsed with the `entities.arguments` parser.
  2524. //
  2525. call: function () {
  2526. var name, nameLC, args, alpha, index = parserInput.i;
  2527. if (parserInput.peek(/^url\(/i)) {
  2528. return;
  2529. }
  2530. parserInput.save();
  2531. name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/);
  2532. if (!name) { parserInput.forget(); return; }
  2533. name = name[1];
  2534. nameLC = name.toLowerCase();
  2535. if (nameLC === 'alpha') {
  2536. alpha = parsers.alpha();
  2537. if(alpha) {
  2538. return alpha;
  2539. }
  2540. }
  2541. args = this.arguments();
  2542. if (! parserInput.$char(')')) {
  2543. parserInput.restore("Could not parse call arguments or missing ')'");
  2544. return;
  2545. }
  2546. parserInput.forget();
  2547. return new(tree.Call)(name, args, index, env.currentFileInfo);
  2548. },
  2549. arguments: function () {
  2550. var args = [], arg;
  2551. while (true) {
  2552. arg = this.assignment() || parsers.expression();
  2553. if (!arg) {
  2554. break;
  2555. }
  2556. args.push(arg);
  2557. if (! parserInput.$char(',')) {
  2558. break;
  2559. }
  2560. }
  2561. return args;
  2562. },
  2563. literal: function () {
  2564. return this.dimension() ||
  2565. this.color() ||
  2566. this.quoted() ||
  2567. this.unicodeDescriptor();
  2568. },
  2569. // Assignments are argument entities for calls.
  2570. // They are present in ie filter properties as shown below.
  2571. //
  2572. // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
  2573. //
  2574. assignment: function () {
  2575. var key, value;
  2576. key = parserInput.$re(/^\w+(?=\s?=)/i);
  2577. if (!key) {
  2578. return;
  2579. }
  2580. if (!parserInput.$char('=')) {
  2581. return;
  2582. }
  2583. value = parsers.entity();
  2584. if (value) {
  2585. return new(tree.Assignment)(key, value);
  2586. }
  2587. },
  2588. //
  2589. // Parse url() tokens
  2590. //
  2591. // We use a specific rule for urls, because they don't really behave like
  2592. // standard function calls. The difference is that the argument doesn't have
  2593. // to be enclosed within a string, so it can't be parsed as an Expression.
  2594. //
  2595. url: function () {
  2596. var value;
  2597. if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) {
  2598. return;
  2599. }
  2600. parserInput.autoCommentAbsorb = false;
  2601. value = this.quoted() || this.variable() ||
  2602. parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
  2603. parserInput.autoCommentAbsorb = true;
  2604. expectChar(')');
  2605. return new(tree.URL)((value.value != null || value instanceof tree.Variable)
  2606. ? value : new(tree.Anonymous)(value), env.currentFileInfo);
  2607. },
  2608. //
  2609. // A Variable entity, such as `@fink`, in
  2610. //
  2611. // width: @fink + 2px
  2612. //
  2613. // We use a different parser for variable definitions,
  2614. // see `parsers.variable`.
  2615. //
  2616. variable: function () {
  2617. var name, index = parserInput.i;
  2618. if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
  2619. return new(tree.Variable)(name, index, env.currentFileInfo);
  2620. }
  2621. },
  2622. // A variable entity useing the protective {} e.g. @{var}
  2623. variableCurly: function () {
  2624. var curly, index = parserInput.i;
  2625. if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
  2626. return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo);
  2627. }
  2628. },
  2629. //
  2630. // A Hexadecimal color
  2631. //
  2632. // #4F3C2F
  2633. //
  2634. // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
  2635. //
  2636. color: function () {
  2637. var rgb;
  2638. if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
  2639. var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string
  2640. colorCandidateString = colorCandidateString[1];
  2641. if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
  2642. error("Invalid HEX color code");
  2643. }
  2644. return new(tree.Color)(rgb[1]);
  2645. }
  2646. },
  2647. //
  2648. // A Dimension, that is, a number and a unit
  2649. //
  2650. // 0.5em 95%
  2651. //
  2652. dimension: function () {
  2653. if (parserInput.peekNotNumeric()) {
  2654. return;
  2655. }
  2656. var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/);
  2657. if (value) {
  2658. return new(tree.Dimension)(value[1], value[2]);
  2659. }
  2660. },
  2661. //
  2662. // A unicode descriptor, as is used in unicode-range
  2663. //
  2664. // U+0?? or U+00A1-00A9
  2665. //
  2666. unicodeDescriptor: function () {
  2667. var ud;
  2668. ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
  2669. if (ud) {
  2670. return new(tree.UnicodeDescriptor)(ud[0]);
  2671. }
  2672. },
  2673. //
  2674. // JavaScript code to be evaluated
  2675. //
  2676. // `window.location.href`
  2677. //
  2678. javascript: function () {
  2679. var js, index = parserInput.i;
  2680. js = parserInput.$re(/^(~)?`([^`]*)`/);
  2681. if (js) {
  2682. return new(tree.JavaScript)(js[2], index, Boolean(js[1]));
  2683. }
  2684. }
  2685. },
  2686. //
  2687. // The variable part of a variable definition. Used in the `rule` parser
  2688. //
  2689. // @fink:
  2690. //
  2691. variable: function () {
  2692. var name;
  2693. if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
  2694. },
  2695. //
  2696. // The variable part of a variable definition. Used in the `rule` parser
  2697. //
  2698. // @fink();
  2699. //
  2700. rulesetCall: function () {
  2701. var name;
  2702. if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
  2703. return new tree.RulesetCall(name[1]);
  2704. }
  2705. },
  2706. //
  2707. // extend syntax - used to extend selectors
  2708. //
  2709. extend: function(isRule) {
  2710. var elements, e, index = parserInput.i, option, extendList, extend;
  2711. if (!(isRule ? parserInput.$re(/^&:extend\(/) : parserInput.$re(/^:extend\(/))) { return; }
  2712. do {
  2713. option = null;
  2714. elements = null;
  2715. while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
  2716. e = this.element();
  2717. if (!e) { break; }
  2718. if (elements) { elements.push(e); } else { elements = [ e ]; }
  2719. }
  2720. option = option && option[1];
  2721. if (!elements)
  2722. error("Missing target selector for :extend().");
  2723. extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
  2724. if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
  2725. } while(parserInput.$char(","));
  2726. expect(/^\)/);
  2727. if (isRule) {
  2728. expect(/^;/);
  2729. }
  2730. return extendList;
  2731. },
  2732. //
  2733. // extendRule - used in a rule to extend all the parent selectors
  2734. //
  2735. extendRule: function() {
  2736. return this.extend(true);
  2737. },
  2738. //
  2739. // Mixins
  2740. //
  2741. mixin: {
  2742. //
  2743. // A Mixin call, with an optional argument list
  2744. //
  2745. // #mixins > .square(#fff);
  2746. // .rounded(4px, black);
  2747. // .button;
  2748. //
  2749. // The `while` loop is there because mixins can be
  2750. // namespaced, but we only support the child and descendant
  2751. // selector for now.
  2752. //
  2753. call: function () {
  2754. var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex,
  2755. elements, elem, e, c, args;
  2756. if (s !== '.' && s !== '#') { return; }
  2757. parserInput.save(); // stop us absorbing part of an invalid selector
  2758. while (true) {
  2759. elemIndex = parserInput.i;
  2760. e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
  2761. if (!e) {
  2762. break;
  2763. }
  2764. elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo);
  2765. if (elements) { elements.push(elem); } else { elements = [ elem ]; }
  2766. c = parserInput.$char('>');
  2767. }
  2768. if (elements) {
  2769. if (parserInput.$char('(')) {
  2770. args = this.args(true).args;
  2771. expectChar(')');
  2772. }
  2773. if (parsers.important()) {
  2774. important = true;
  2775. }
  2776. if (parsers.end()) {
  2777. parserInput.forget();
  2778. return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important);
  2779. }
  2780. }
  2781. parserInput.restore();
  2782. },
  2783. args: function (isCall) {
  2784. var parsers = parser.parsers, entities = parsers.entities,
  2785. returner = { args:null, variadic: false },
  2786. expressions = [], argsSemiColon = [], argsComma = [],
  2787. isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg;
  2788. parserInput.save();
  2789. while (true) {
  2790. if (isCall) {
  2791. arg = parsers.detachedRuleset() || parsers.expression();
  2792. } else {
  2793. parserInput.commentStore.length = 0;
  2794. if (parserInput.currentChar() === '.' && parserInput.$re(/^\.{3}/)) {
  2795. returner.variadic = true;
  2796. if (parserInput.$char(";") && !isSemiColonSeperated) {
  2797. isSemiColonSeperated = true;
  2798. }
  2799. (isSemiColonSeperated ? argsSemiColon : argsComma)
  2800. .push({ variadic: true });
  2801. break;
  2802. }
  2803. arg = entities.variable() || entities.literal() || entities.keyword();
  2804. }
  2805. if (!arg) {
  2806. break;
  2807. }
  2808. nameLoop = null;
  2809. if (arg.throwAwayComments) {
  2810. arg.throwAwayComments();
  2811. }
  2812. value = arg;
  2813. var val = null;
  2814. if (isCall) {
  2815. // Variable
  2816. if (arg.value && arg.value.length == 1) {
  2817. val = arg.value[0];
  2818. }
  2819. } else {
  2820. val = arg;
  2821. }
  2822. if (val && val instanceof tree.Variable) {
  2823. if (parserInput.$char(':')) {
  2824. if (expressions.length > 0) {
  2825. if (isSemiColonSeperated) {
  2826. error("Cannot mix ; and , as delimiter types");
  2827. }
  2828. expressionContainsNamed = true;
  2829. }
  2830. // we do not support setting a ruleset as a default variable - it doesn't make sense
  2831. // However if we do want to add it, there is nothing blocking it, just don't error
  2832. // and remove isCall dependency below
  2833. value = (isCall && parsers.detachedRuleset()) || parsers.expression();
  2834. if (!value) {
  2835. if (isCall) {
  2836. error("could not understand value for named argument");
  2837. } else {
  2838. parserInput.restore();
  2839. returner.args = [];
  2840. return returner;
  2841. }
  2842. }
  2843. nameLoop = (name = val.name);
  2844. } else if (!isCall && parserInput.$re(/^\.{3}/)) {
  2845. returner.variadic = true;
  2846. if (parserInput.$char(";") && !isSemiColonSeperated) {
  2847. isSemiColonSeperated = true;
  2848. }
  2849. (isSemiColonSeperated ? argsSemiColon : argsComma)
  2850. .push({ name: arg.name, variadic: true });
  2851. break;
  2852. } else if (!isCall) {
  2853. name = nameLoop = val.name;
  2854. value = null;
  2855. }
  2856. }
  2857. if (value) {
  2858. expressions.push(value);
  2859. }
  2860. argsComma.push({ name:nameLoop, value:value });
  2861. if (parserInput.$char(',')) {
  2862. continue;
  2863. }
  2864. if (parserInput.$char(';') || isSemiColonSeperated) {
  2865. if (expressionContainsNamed) {
  2866. error("Cannot mix ; and , as delimiter types");
  2867. }
  2868. isSemiColonSeperated = true;
  2869. if (expressions.length > 1) {
  2870. value = new(tree.Value)(expressions);
  2871. }
  2872. argsSemiColon.push({ name:name, value:value });
  2873. name = null;
  2874. expressions = [];
  2875. expressionContainsNamed = false;
  2876. }
  2877. }
  2878. parserInput.forget();
  2879. returner.args = isSemiColonSeperated ? argsSemiColon : argsComma;
  2880. return returner;
  2881. },
  2882. //
  2883. // A Mixin definition, with a list of parameters
  2884. //
  2885. // .rounded (@radius: 2px, @color) {
  2886. // ...
  2887. // }
  2888. //
  2889. // Until we have a finer grained state-machine, we have to
  2890. // do a look-ahead, to make sure we don't have a mixin call.
  2891. // See the `rule` function for more information.
  2892. //
  2893. // We start by matching `.rounded (`, and then proceed on to
  2894. // the argument list, which has optional default values.
  2895. // We store the parameters in `params`, with a `value` key,
  2896. // if there is a value, such as in the case of `@radius`.
  2897. //
  2898. // Once we've got our params list, and a closing `)`, we parse
  2899. // the `{...}` block.
  2900. //
  2901. definition: function () {
  2902. var name, params = [], match, ruleset, cond, variadic = false;
  2903. if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
  2904. parserInput.peek(/^[^{]*\}/)) {
  2905. return;
  2906. }
  2907. parserInput.save();
  2908. match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
  2909. if (match) {
  2910. name = match[1];
  2911. var argInfo = this.args(false);
  2912. params = argInfo.args;
  2913. variadic = argInfo.variadic;
  2914. // .mixincall("@{a}");
  2915. // looks a bit like a mixin definition..
  2916. // also
  2917. // .mixincall(@a: {rule: set;});
  2918. // so we have to be nice and restore
  2919. if (!parserInput.$char(')')) {
  2920. parserInput.restore("Missing closing ')'");
  2921. return;
  2922. }
  2923. parserInput.commentStore.length = 0;
  2924. if (parserInput.$re(/^when/)) { // Guard
  2925. cond = expect(parsers.conditions, 'expected condition');
  2926. }
  2927. ruleset = parsers.block();
  2928. if (ruleset) {
  2929. parserInput.forget();
  2930. return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
  2931. } else {
  2932. parserInput.restore();
  2933. }
  2934. } else {
  2935. parserInput.forget();
  2936. }
  2937. }
  2938. },
  2939. //
  2940. // Entities are the smallest recognized token,
  2941. // and can be found inside a rule's value.
  2942. //
  2943. entity: function () {
  2944. var entities = this.entities;
  2945. return this.comment() || entities.literal() || entities.variable() || entities.url() ||
  2946. entities.call() || entities.keyword() || entities.javascript();
  2947. },
  2948. //
  2949. // A Rule terminator. Note that we use `peek()` to check for '}',
  2950. // because the `block` rule will be expecting it, but we still need to make sure
  2951. // it's there, if ';' was ommitted.
  2952. //
  2953. end: function () {
  2954. return parserInput.$char(';') || parserInput.peek('}');
  2955. },
  2956. //
  2957. // IE's alpha function
  2958. //
  2959. // alpha(opacity=88)
  2960. //
  2961. alpha: function () {
  2962. var value;
  2963. if (! parserInput.$re(/^opacity=/i)) { return; }
  2964. value = parserInput.$re(/^\d+/);
  2965. if (!value) {
  2966. value = expect(this.entities.variable, "Could not parse alpha");
  2967. }
  2968. expectChar(')');
  2969. return new(tree.Alpha)(value);
  2970. },
  2971. //
  2972. // A Selector Element
  2973. //
  2974. // div
  2975. // + h1
  2976. // #socks
  2977. // input[type="text"]
  2978. //
  2979. // Elements are the building blocks for Selectors,
  2980. // they are made out of a `Combinator` (see combinator rule),
  2981. // and an element name, such as a tag a class, or `*`.
  2982. //
  2983. element: function () {
  2984. var e, c, v, index = parserInput.i;
  2985. c = this.combinator();
  2986. e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
  2987. parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^()@]+\)/) || parserInput.$re(/^[\.#](?=@)/) ||
  2988. this.entities.variableCurly();
  2989. if (! e) {
  2990. parserInput.save();
  2991. if (parserInput.$char('(')) {
  2992. if ((v = this.selector()) && parserInput.$char(')')) {
  2993. e = new(tree.Paren)(v);
  2994. parserInput.forget();
  2995. } else {
  2996. parserInput.restore("Missing closing ')'");
  2997. }
  2998. } else {
  2999. parserInput.forget();
  3000. }
  3001. }
  3002. if (e) { return new(tree.Element)(c, e, index, env.currentFileInfo); }
  3003. },
  3004. //
  3005. // Combinators combine elements together, in a Selector.
  3006. //
  3007. // Because our parser isn't white-space sensitive, special care
  3008. // has to be taken, when parsing the descendant combinator, ` `,
  3009. // as it's an empty space. We have to check the previous character
  3010. // in the input, to see if it's a ` ` character. More info on how
  3011. // we deal with this in *combinator.js*.
  3012. //
  3013. combinator: function () {
  3014. var c = parserInput.currentChar();
  3015. if (c === '/') {
  3016. parserInput.save();
  3017. var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i);
  3018. if (slashedCombinator) {
  3019. parserInput.forget();
  3020. return new(tree.Combinator)(slashedCombinator);
  3021. }
  3022. parserInput.restore();
  3023. }
  3024. if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
  3025. parserInput.i++;
  3026. if (c === '^' && parserInput.currentChar() === '^') {
  3027. c = '^^';
  3028. parserInput.i++;
  3029. }
  3030. while (parserInput.isWhitespace()) { parserInput.i++; }
  3031. return new(tree.Combinator)(c);
  3032. } else if (parserInput.isWhitespace(-1)) {
  3033. return new(tree.Combinator)(" ");
  3034. } else {
  3035. return new(tree.Combinator)(null);
  3036. }
  3037. },
  3038. //
  3039. // A CSS selector (see selector below)
  3040. // with less extensions e.g. the ability to extend and guard
  3041. //
  3042. lessSelector: function () {
  3043. return this.selector(true);
  3044. },
  3045. //
  3046. // A CSS Selector
  3047. //
  3048. // .class > div + h1
  3049. // li a:hover
  3050. //
  3051. // Selectors are made out of one or more Elements, see above.
  3052. //
  3053. selector: function (isLess) {
  3054. var index = parserInput.i, elements, extendList, c, e, extend, when, condition;
  3055. while ((isLess && (extend = this.extend())) || (isLess && (when = parserInput.$re(/^when/))) || (e = this.element())) {
  3056. if (when) {
  3057. condition = expect(this.conditions, 'expected condition');
  3058. } else if (condition) {
  3059. error("CSS guard can only be used at the end of selector");
  3060. } else if (extend) {
  3061. if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
  3062. } else {
  3063. if (extendList) { error("Extend can only be used at the end of selector"); }
  3064. c = parserInput.currentChar();
  3065. if (elements) { elements.push(e); } else { elements = [ e ]; }
  3066. e = null;
  3067. }
  3068. if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
  3069. break;
  3070. }
  3071. }
  3072. if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); }
  3073. if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
  3074. },
  3075. attribute: function () {
  3076. if (! parserInput.$char('[')) { return; }
  3077. var entities = this.entities,
  3078. key, val, op;
  3079. if (!(key = entities.variableCurly())) {
  3080. key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
  3081. }
  3082. op = parserInput.$re(/^[|~*$^]?=/);
  3083. if (op) {
  3084. val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly();
  3085. }
  3086. expectChar(']');
  3087. return new(tree.Attribute)(key, op, val);
  3088. },
  3089. //
  3090. // The `block` rule is used by `ruleset` and `mixin.definition`.
  3091. // It's a wrapper around the `primary` rule, with added `{}`.
  3092. //
  3093. block: function () {
  3094. var content;
  3095. if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) {
  3096. return content;
  3097. }
  3098. },
  3099. blockRuleset: function() {
  3100. var block = this.block();
  3101. if (block) {
  3102. block = new tree.Ruleset(null, block);
  3103. }
  3104. return block;
  3105. },
  3106. detachedRuleset: function() {
  3107. var blockRuleset = this.blockRuleset();
  3108. if (blockRuleset) {
  3109. return new tree.DetachedRuleset(blockRuleset);
  3110. }
  3111. },
  3112. //
  3113. // div, .class, body > p {...}
  3114. //
  3115. ruleset: function () {
  3116. var selectors, s, rules, debugInfo;
  3117. parserInput.save();
  3118. if (env.dumpLineNumbers) {
  3119. debugInfo = getDebugInfo(parserInput.i);
  3120. }
  3121. while (true) {
  3122. s = this.lessSelector();
  3123. if (!s) {
  3124. break;
  3125. }
  3126. if (selectors) { selectors.push(s); } else { selectors = [ s ]; }
  3127. parserInput.commentStore.length = 0;
  3128. if (s.condition && selectors.length > 1) {
  3129. error("Guards are only currently allowed on a single selector.");
  3130. }
  3131. if (! parserInput.$char(',')) { break; }
  3132. if (s.condition) {
  3133. error("Guards are only currently allowed on a single selector.");
  3134. }
  3135. parserInput.commentStore.length = 0;
  3136. }
  3137. if (selectors && (rules = this.block())) {
  3138. parserInput.forget();
  3139. var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);
  3140. if (env.dumpLineNumbers) {
  3141. ruleset.debugInfo = debugInfo;
  3142. }
  3143. return ruleset;
  3144. } else {
  3145. parserInput.restore();
  3146. }
  3147. },
  3148. rule: function (tryAnonymous) {
  3149. var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable;
  3150. if (c === '.' || c === '#' || c === '&') { return; }
  3151. parserInput.save();
  3152. name = this.variable() || this.ruleProperty();
  3153. if (name) {
  3154. isVariable = typeof name === "string";
  3155. if (isVariable) {
  3156. value = this.detachedRuleset();
  3157. }
  3158. parserInput.commentStore.length = 0;
  3159. if (!value) {
  3160. // a name returned by this.ruleProperty() is always an array of the form:
  3161. // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
  3162. // where each item is a tree.Keyword or tree.Variable
  3163. merge = !isVariable && name.pop().value;
  3164. // prefer to try to parse first if its a variable or we are compressing
  3165. // but always fallback on the other one
  3166. var tryValueFirst = !tryAnonymous && (env.compress || isVariable);
  3167. if (tryValueFirst) {
  3168. value = this.value();
  3169. }
  3170. if (!value) {
  3171. value = this.anonymousValue();
  3172. if (value) {
  3173. parserInput.forget();
  3174. // anonymous values absorb the end ';' which is reequired for them to work
  3175. return new (tree.Rule)(name, value, false, merge, startOfRule, env.currentFileInfo);
  3176. }
  3177. }
  3178. if (!tryValueFirst && !value) {
  3179. value = this.value();
  3180. }
  3181. important = this.important();
  3182. }
  3183. if (value && this.end()) {
  3184. parserInput.forget();
  3185. return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo);
  3186. } else {
  3187. parserInput.restore();
  3188. if (value && !tryAnonymous) {
  3189. return this.rule(true);
  3190. }
  3191. }
  3192. } else {
  3193. parserInput.forget();
  3194. }
  3195. },
  3196. anonymousValue: function () {
  3197. var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/);
  3198. if (match) {
  3199. return new(tree.Anonymous)(match[1]);
  3200. }
  3201. },
  3202. //
  3203. // An @import directive
  3204. //
  3205. // @import "lib";
  3206. //
  3207. // Depending on our environment, importing is done differently:
  3208. // In the browser, it's an XHR request, in Node, it would be a
  3209. // file-system operation. The function used for importing is
  3210. // stored in `import`, which we pass to the Import constructor.
  3211. //
  3212. "import": function () {
  3213. var path, features, index = parserInput.i;
  3214. var dir = parserInput.$re(/^@import?\s+/);
  3215. if (dir) {
  3216. var options = (dir ? this.importOptions() : null) || {};
  3217. if ((path = this.entities.quoted() || this.entities.url())) {
  3218. features = this.mediaFeatures();
  3219. if (!parserInput.$(';')) {
  3220. parserInput.i = index;
  3221. error("missing semi-colon or unrecognised media features on import");
  3222. }
  3223. features = features && new(tree.Value)(features);
  3224. return new(tree.Import)(path, features, options, index, env.currentFileInfo);
  3225. }
  3226. else
  3227. {
  3228. parserInput.i = index;
  3229. error("malformed import statement");
  3230. }
  3231. }
  3232. },
  3233. importOptions: function() {
  3234. var o, options = {}, optionName, value;
  3235. // list of options, surrounded by parens
  3236. if (! parserInput.$char('(')) { return null; }
  3237. do {
  3238. o = this.importOption();
  3239. if (o) {
  3240. optionName = o;
  3241. value = true;
  3242. switch(optionName) {
  3243. case "css":
  3244. optionName = "less";
  3245. value = false;
  3246. break;
  3247. case "once":
  3248. optionName = "multiple";
  3249. value = false;
  3250. break;
  3251. }
  3252. options[optionName] = value;
  3253. if (! parserInput.$char(',')) { break; }
  3254. }
  3255. } while (o);
  3256. expectChar(')');
  3257. return options;
  3258. },
  3259. importOption: function() {
  3260. var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference)/);
  3261. if (opt) {
  3262. return opt[1];
  3263. }
  3264. },
  3265. mediaFeature: function () {
  3266. var entities = this.entities, nodes = [], e, p;
  3267. parserInput.save();
  3268. do {
  3269. e = entities.keyword() || entities.variable();
  3270. if (e) {
  3271. nodes.push(e);
  3272. } else if (parserInput.$char('(')) {
  3273. p = this.property();
  3274. e = this.value();
  3275. if (parserInput.$char(')')) {
  3276. if (p && e) {
  3277. nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, env.currentFileInfo, true)));
  3278. } else if (e) {
  3279. nodes.push(new(tree.Paren)(e));
  3280. } else {
  3281. parserInput.restore("badly formed media feature definition");
  3282. return null;
  3283. }
  3284. } else {
  3285. parserInput.restore("Missing closing ')'");
  3286. return null;
  3287. }
  3288. }
  3289. } while (e);
  3290. parserInput.forget();
  3291. if (nodes.length > 0) {
  3292. return new(tree.Expression)(nodes);
  3293. }
  3294. },
  3295. mediaFeatures: function () {
  3296. var entities = this.entities, features = [], e;
  3297. do {
  3298. e = this.mediaFeature();
  3299. if (e) {
  3300. features.push(e);
  3301. if (! parserInput.$char(',')) { break; }
  3302. } else {
  3303. e = entities.variable();
  3304. if (e) {
  3305. features.push(e);
  3306. if (! parserInput.$char(',')) { break; }
  3307. }
  3308. }
  3309. } while (e);
  3310. return features.length > 0 ? features : null;
  3311. },
  3312. media: function () {
  3313. var features, rules, media, debugInfo;
  3314. if (env.dumpLineNumbers) {
  3315. debugInfo = getDebugInfo(parserInput.i);
  3316. }
  3317. if (parserInput.$re(/^@media/)) {
  3318. features = this.mediaFeatures();
  3319. rules = this.block();
  3320. if (rules) {
  3321. media = new(tree.Media)(rules, features, parserInput.i, env.currentFileInfo);
  3322. if (env.dumpLineNumbers) {
  3323. media.debugInfo = debugInfo;
  3324. }
  3325. return media;
  3326. }
  3327. }
  3328. },
  3329. //
  3330. // A CSS Directive
  3331. //
  3332. // @charset "utf-8";
  3333. //
  3334. directive: function () {
  3335. var index = parserInput.i, name, value, rules, nonVendorSpecificName,
  3336. hasIdentifier, hasExpression, hasUnknown, hasBlock = true;
  3337. if (parserInput.currentChar() !== '@') { return; }
  3338. value = this['import']() || this.media();
  3339. if (value) {
  3340. return value;
  3341. }
  3342. parserInput.save();
  3343. name = parserInput.$re(/^@[a-z-]+/);
  3344. if (!name) { return; }
  3345. nonVendorSpecificName = name;
  3346. if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
  3347. nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
  3348. }
  3349. switch(nonVendorSpecificName) {
  3350. /*
  3351. case "@font-face":
  3352. case "@viewport":
  3353. case "@top-left":
  3354. case "@top-left-corner":
  3355. case "@top-center":
  3356. case "@top-right":
  3357. case "@top-right-corner":
  3358. case "@bottom-left":
  3359. case "@bottom-left-corner":
  3360. case "@bottom-center":
  3361. case "@bottom-right":
  3362. case "@bottom-right-corner":
  3363. case "@left-top":
  3364. case "@left-middle":
  3365. case "@left-bottom":
  3366. case "@right-top":
  3367. case "@right-middle":
  3368. case "@right-bottom":
  3369. hasBlock = true;
  3370. break;
  3371. */
  3372. case "@charset":
  3373. hasIdentifier = true;
  3374. hasBlock = false;
  3375. break;
  3376. case "@namespace":
  3377. hasExpression = true;
  3378. hasBlock = false;
  3379. break;
  3380. case "@keyframes":
  3381. hasIdentifier = true;
  3382. break;
  3383. case "@host":
  3384. case "@page":
  3385. case "@document":
  3386. case "@supports":
  3387. hasUnknown = true;
  3388. break;
  3389. }
  3390. parserInput.commentStore.length = 0;
  3391. if (hasIdentifier) {
  3392. value = this.entity();
  3393. if (!value) {
  3394. error("expected " + name + " identifier");
  3395. }
  3396. } else if (hasExpression) {
  3397. value = this.expression();
  3398. if (!value) {
  3399. error("expected " + name + " expression");
  3400. }
  3401. } else if (hasUnknown) {
  3402. value = (parserInput.$re(/^[^{;]+/) || '').trim();
  3403. if (value) {
  3404. value = new(tree.Anonymous)(value);
  3405. }
  3406. }
  3407. if (hasBlock) {
  3408. rules = this.blockRuleset();
  3409. }
  3410. if (rules || (!hasBlock && value && parserInput.$char(';'))) {
  3411. parserInput.forget();
  3412. return new(tree.Directive)(name, value, rules, index, env.currentFileInfo,
  3413. env.dumpLineNumbers ? getDebugInfo(index) : null);
  3414. }
  3415. parserInput.restore("directive options not recognised");
  3416. },
  3417. //
  3418. // A Value is a comma-delimited list of Expressions
  3419. //
  3420. // font-family: Baskerville, Georgia, serif;
  3421. //
  3422. // In a Rule, a Value represents everything after the `:`,
  3423. // and before the `;`.
  3424. //
  3425. value: function () {
  3426. var e, expressions = [];
  3427. do {
  3428. e = this.expression();
  3429. if (e) {
  3430. expressions.push(e);
  3431. if (! parserInput.$char(',')) { break; }
  3432. }
  3433. } while(e);
  3434. if (expressions.length > 0) {
  3435. return new(tree.Value)(expressions);
  3436. }
  3437. },
  3438. important: function () {
  3439. if (parserInput.currentChar() === '!') {
  3440. return parserInput.$re(/^! *important/);
  3441. }
  3442. },
  3443. sub: function () {
  3444. var a, e;
  3445. if (parserInput.$char('(')) {
  3446. a = this.addition();
  3447. if (a) {
  3448. e = new(tree.Expression)([a]);
  3449. expectChar(')');
  3450. e.parens = true;
  3451. return e;
  3452. }
  3453. }
  3454. },
  3455. multiplication: function () {
  3456. var m, a, op, operation, isSpaced;
  3457. m = this.operand();
  3458. if (m) {
  3459. isSpaced = parserInput.isWhitespace(-1);
  3460. while (true) {
  3461. if (parserInput.peek(/^\/[*\/]/)) {
  3462. break;
  3463. }
  3464. parserInput.save();
  3465. op = parserInput.$char('/') || parserInput.$char('*');
  3466. if (!op) { parserInput.forget(); break; }
  3467. a = this.operand();
  3468. if (!a) { parserInput.restore(); break; }
  3469. parserInput.forget();
  3470. m.parensInOp = true;
  3471. a.parensInOp = true;
  3472. operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
  3473. isSpaced = parserInput.isWhitespace(-1);
  3474. }
  3475. return operation || m;
  3476. }
  3477. },
  3478. addition: function () {
  3479. var m, a, op, operation, isSpaced;
  3480. m = this.multiplication();
  3481. if (m) {
  3482. isSpaced = parserInput.isWhitespace(-1);
  3483. while (true) {
  3484. op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-')));
  3485. if (!op) {
  3486. break;
  3487. }
  3488. a = this.multiplication();
  3489. if (!a) {
  3490. break;
  3491. }
  3492. m.parensInOp = true;
  3493. a.parensInOp = true;
  3494. operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
  3495. isSpaced = parserInput.isWhitespace(-1);
  3496. }
  3497. return operation || m;
  3498. }
  3499. },
  3500. conditions: function () {
  3501. var a, b, index = parserInput.i, condition;
  3502. a = this.condition();
  3503. if (a) {
  3504. while (true) {
  3505. if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) {
  3506. break;
  3507. }
  3508. b = this.condition();
  3509. if (!b) {
  3510. break;
  3511. }
  3512. condition = new(tree.Condition)('or', condition || a, b, index);
  3513. }
  3514. return condition || a;
  3515. }
  3516. },
  3517. condition: function () {
  3518. var entities = this.entities, index = parserInput.i, negate = false,
  3519. a, b, c, op;
  3520. if (parserInput.$re(/^not/)) { negate = true; }
  3521. expectChar('(');
  3522. a = this.addition() || entities.keyword() || entities.quoted();
  3523. if (a) {
  3524. op = parserInput.$re(/^(?:>=|<=|=<|[<=>])/);
  3525. if (op) {
  3526. b = this.addition() || entities.keyword() || entities.quoted();
  3527. if (b) {
  3528. c = new(tree.Condition)(op, a, b, index, negate);
  3529. } else {
  3530. error('expected expression');
  3531. }
  3532. } else {
  3533. c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
  3534. }
  3535. expectChar(')');
  3536. return parserInput.$re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;
  3537. }
  3538. },
  3539. //
  3540. // An operand is anything that can be part of an operation,
  3541. // such as a Color, or a Variable
  3542. //
  3543. operand: function () {
  3544. var entities = this.entities, negate;
  3545. if (parserInput.peek(/^-[@\(]/)) {
  3546. negate = parserInput.$char('-');
  3547. }
  3548. var o = this.sub() || entities.dimension() ||
  3549. entities.color() || entities.variable() ||
  3550. entities.call();
  3551. if (negate) {
  3552. o.parensInOp = true;
  3553. o = new(tree.Negative)(o);
  3554. }
  3555. return o;
  3556. },
  3557. //
  3558. // Expressions either represent mathematical operations,
  3559. // or white-space delimited Entities.
  3560. //
  3561. // 1px solid black
  3562. // @var * 2
  3563. //
  3564. expression: function () {
  3565. var entities = [], e, delim;
  3566. do {
  3567. e = this.comment();
  3568. if (e) {
  3569. entities.push(e);
  3570. continue;
  3571. }
  3572. e = this.addition() || this.entity();
  3573. if (e) {
  3574. entities.push(e);
  3575. // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
  3576. if (!parserInput.peek(/^\/[\/*]/)) {
  3577. delim = parserInput.$char('/');
  3578. if (delim) {
  3579. entities.push(new(tree.Anonymous)(delim));
  3580. }
  3581. }
  3582. }
  3583. } while (e);
  3584. if (entities.length > 0) {
  3585. return new(tree.Expression)(entities);
  3586. }
  3587. },
  3588. property: function () {
  3589. var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
  3590. if (name) {
  3591. return name[1];
  3592. }
  3593. },
  3594. ruleProperty: function () {
  3595. var name = [], index = [], s, k;
  3596. parserInput.save();
  3597. function match(re) {
  3598. var i = parserInput.i,
  3599. chunk = parserInput.$re(re);
  3600. if (chunk) {
  3601. index.push(i);
  3602. return name.push(chunk[1]);
  3603. }
  3604. }
  3605. match(/^(\*?)/);
  3606. while (true) {
  3607. if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) {
  3608. break;
  3609. }
  3610. }
  3611. if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) {
  3612. parserInput.forget();
  3613. // at last, we have the complete match now. move forward,
  3614. // convert name particles to tree objects and return:
  3615. if (name[0] === '') {
  3616. name.shift();
  3617. index.shift();
  3618. }
  3619. for (k = 0; k < name.length; k++) {
  3620. s = name[k];
  3621. name[k] = (s.charAt(0) !== '@') ?
  3622. new(tree.Keyword)(s) :
  3623. new(tree.Variable)('@' + s.slice(2, -1),
  3624. index[k], env.currentFileInfo);
  3625. }
  3626. return name;
  3627. }
  3628. parserInput.restore();
  3629. }
  3630. }
  3631. };
  3632. parser.getInput = getInput;
  3633. parser.getLocation = parserInput.getLocation;
  3634. return parser;
  3635. };
  3636. Parser.serializeVars = function(vars) {
  3637. var s = '';
  3638. for (var name in vars) {
  3639. if (Object.hasOwnProperty.call(vars, name)) {
  3640. var value = vars[name];
  3641. s += ((name[0] === '@') ? '' : '@') + name +': '+ value +
  3642. ((('' + value).slice(-1) === ';') ? '' : ';');
  3643. }
  3644. }
  3645. return s;
  3646. };
  3647. return Parser;
  3648. };
  3649. },{"../contexts.js":2,"../less-error.js":19,"../source-map-output":25,"../tree/index.js":43,"../visitor/index.js":66,"./imports.js":22,"./parser-input.js":23}],25:[function(require,module,exports){
  3650. module.exports = function (environment) {
  3651. var SourceMapOutput = function (options) {
  3652. this._css = [];
  3653. this._rootNode = options.rootNode;
  3654. this._writeSourceMap = options.writeSourceMap;
  3655. this._contentsMap = options.contentsMap;
  3656. this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap;
  3657. this._sourceMapFilename = options.sourceMapFilename;
  3658. this._outputFilename = options.outputFilename;
  3659. this._sourceMapURL = options.sourceMapURL;
  3660. if (options.sourceMapBasepath) {
  3661. this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
  3662. }
  3663. this._sourceMapRootpath = options.sourceMapRootpath;
  3664. this._outputSourceFiles = options.outputSourceFiles;
  3665. this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator();
  3666. if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') {
  3667. this._sourceMapRootpath += '/';
  3668. }
  3669. this._lineNumber = 0;
  3670. this._column = 0;
  3671. };
  3672. SourceMapOutput.prototype.normalizeFilename = function(filename) {
  3673. filename = filename.replace(/\\/g, '/');
  3674. if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) {
  3675. filename = filename.substring(this._sourceMapBasepath.length);
  3676. if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') {
  3677. filename = filename.substring(1);
  3678. }
  3679. }
  3680. return (this._sourceMapRootpath || "") + filename;
  3681. };
  3682. SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
  3683. //ignore adding empty strings
  3684. if (!chunk) {
  3685. return;
  3686. }
  3687. var lines,
  3688. sourceLines,
  3689. columns,
  3690. sourceColumns,
  3691. i;
  3692. if (fileInfo) {
  3693. var inputSource = this._contentsMap[fileInfo.filename];
  3694. // remove vars/banner added to the top of the file
  3695. if (this._contentsIgnoredCharsMap[fileInfo.filename]) {
  3696. // adjust the index
  3697. index -= this._contentsIgnoredCharsMap[fileInfo.filename];
  3698. if (index < 0) { index = 0; }
  3699. // adjust the source
  3700. inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]);
  3701. }
  3702. inputSource = inputSource.substring(0, index);
  3703. sourceLines = inputSource.split("\n");
  3704. sourceColumns = sourceLines[sourceLines.length-1];
  3705. }
  3706. lines = chunk.split("\n");
  3707. columns = lines[lines.length-1];
  3708. if (fileInfo) {
  3709. if (!mapLines) {
  3710. this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column},
  3711. original: { line: sourceLines.length, column: sourceColumns.length},
  3712. source: this.normalizeFilename(fileInfo.filename)});
  3713. } else {
  3714. for(i = 0; i < lines.length; i++) {
  3715. this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0},
  3716. original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0},
  3717. source: this.normalizeFilename(fileInfo.filename)});
  3718. }
  3719. }
  3720. }
  3721. if (lines.length === 1) {
  3722. this._column += columns.length;
  3723. } else {
  3724. this._lineNumber += lines.length - 1;
  3725. this._column = columns.length;
  3726. }
  3727. this._css.push(chunk);
  3728. };
  3729. SourceMapOutput.prototype.isEmpty = function() {
  3730. return this._css.length === 0;
  3731. };
  3732. SourceMapOutput.prototype.toCSS = function(env) {
  3733. this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
  3734. if (this._outputSourceFiles) {
  3735. for(var filename in this._contentsMap) {
  3736. if (this._contentsMap.hasOwnProperty(filename))
  3737. {
  3738. var source = this._contentsMap[filename];
  3739. if (this._contentsIgnoredCharsMap[filename]) {
  3740. source = source.slice(this._contentsIgnoredCharsMap[filename]);
  3741. }
  3742. this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
  3743. }
  3744. }
  3745. }
  3746. this._rootNode.genCSS(env, this);
  3747. if (this._css.length > 0) {
  3748. var sourceMapURL,
  3749. sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
  3750. if (this._sourceMapURL) {
  3751. sourceMapURL = this._sourceMapURL;
  3752. } else if (this._sourceMapFilename) {
  3753. sourceMapURL = this.normalizeFilename(this._sourceMapFilename);
  3754. }
  3755. if (this._writeSourceMap) {
  3756. this._writeSourceMap(sourceMapContent);
  3757. } else {
  3758. sourceMapURL = "data:application/json;base64," + environment.encodeBase64(sourceMapContent);
  3759. }
  3760. if (sourceMapURL) {
  3761. this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */");
  3762. }
  3763. }
  3764. return this._css.join('');
  3765. };
  3766. return SourceMapOutput;
  3767. };
  3768. },{}],26:[function(require,module,exports){
  3769. var Node = require("./node.js");
  3770. var Alpha = function (val) {
  3771. this.value = val;
  3772. };
  3773. Alpha.prototype = new Node();
  3774. Alpha.prototype.type = "Alpha";
  3775. Alpha.prototype.accept = function (visitor) {
  3776. this.value = visitor.visit(this.value);
  3777. };
  3778. Alpha.prototype.eval = function (env) {
  3779. if (this.value.eval) { return new Alpha(this.value.eval(env)); }
  3780. return this;
  3781. };
  3782. Alpha.prototype.genCSS = function (env, output) {
  3783. output.add("alpha(opacity=");
  3784. if (this.value.genCSS) {
  3785. this.value.genCSS(env, output);
  3786. } else {
  3787. output.add(this.value);
  3788. }
  3789. output.add(")");
  3790. };
  3791. module.exports = Alpha;
  3792. },{"./node.js":51}],27:[function(require,module,exports){
  3793. var Node = require("./node.js");
  3794. var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) {
  3795. this.value = value;
  3796. this.index = index;
  3797. this.mapLines = mapLines;
  3798. this.currentFileInfo = currentFileInfo;
  3799. this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike;
  3800. };
  3801. Anonymous.prototype = new Node();
  3802. Anonymous.prototype.type = "Anonymous";
  3803. Anonymous.prototype.eval = function () {
  3804. return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
  3805. };
  3806. Anonymous.prototype.compare = function (x) {
  3807. if (!x.toCSS) {
  3808. return -1;
  3809. }
  3810. var left = this.toCSS(),
  3811. right = x.toCSS();
  3812. if (left === right) {
  3813. return 0;
  3814. }
  3815. return left < right ? -1 : 1;
  3816. };
  3817. Anonymous.prototype.isRulesetLike = function() {
  3818. return this.rulesetLike;
  3819. };
  3820. Anonymous.prototype.genCSS = function (env, output) {
  3821. output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
  3822. };
  3823. module.exports = Anonymous;
  3824. },{"./node.js":51}],28:[function(require,module,exports){
  3825. var Node = require("./node.js");
  3826. var Assignment = function (key, val) {
  3827. this.key = key;
  3828. this.value = val;
  3829. };
  3830. Assignment.prototype = new Node();
  3831. Assignment.prototype.type = "Assignment";
  3832. Assignment.prototype.accept = function (visitor) {
  3833. this.value = visitor.visit(this.value);
  3834. };
  3835. Assignment.prototype.eval = function (env) {
  3836. if (this.value.eval) {
  3837. return new(Assignment)(this.key, this.value.eval(env));
  3838. }
  3839. return this;
  3840. };
  3841. Assignment.prototype.genCSS = function (env, output) {
  3842. output.add(this.key + '=');
  3843. if (this.value.genCSS) {
  3844. this.value.genCSS(env, output);
  3845. } else {
  3846. output.add(this.value);
  3847. }
  3848. };
  3849. module.exports = Assignment;
  3850. },{"./node.js":51}],29:[function(require,module,exports){
  3851. var Node = require("./node.js");
  3852. var Attribute = function (key, op, value) {
  3853. this.key = key;
  3854. this.op = op;
  3855. this.value = value;
  3856. };
  3857. Attribute.prototype = new Node();
  3858. Attribute.prototype.type = "Attribute";
  3859. Attribute.prototype.eval = function (env) {
  3860. return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key,
  3861. this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value);
  3862. };
  3863. Attribute.prototype.genCSS = function (env, output) {
  3864. output.add(this.toCSS(env));
  3865. };
  3866. Attribute.prototype.toCSS = function (env) {
  3867. var value = this.key.toCSS ? this.key.toCSS(env) : this.key;
  3868. if (this.op) {
  3869. value += this.op;
  3870. value += (this.value.toCSS ? this.value.toCSS(env) : this.value);
  3871. }
  3872. return '[' + value + ']';
  3873. };
  3874. module.exports = Attribute;
  3875. },{"./node.js":51}],30:[function(require,module,exports){
  3876. var Node = require("./node.js"),
  3877. FunctionCaller = require("../functions/function-caller.js");
  3878. //
  3879. // A function call node.
  3880. //
  3881. var Call = function (name, args, index, currentFileInfo) {
  3882. this.name = name;
  3883. this.args = args;
  3884. this.index = index;
  3885. this.currentFileInfo = currentFileInfo;
  3886. };
  3887. Call.prototype = new Node();
  3888. Call.prototype.type = "Call";
  3889. Call.prototype.accept = function (visitor) {
  3890. if (this.args) {
  3891. this.args = visitor.visitArray(this.args);
  3892. }
  3893. };
  3894. //
  3895. // When evaluating a function call,
  3896. // we either find the function in `less.functions` [1],
  3897. // in which case we call it, passing the evaluated arguments,
  3898. // if this returns null or we cannot find the function, we
  3899. // simply print it out as it appeared originally [2].
  3900. //
  3901. // The *functions.js* file contains the built-in functions.
  3902. //
  3903. // The reason why we evaluate the arguments, is in the case where
  3904. // we try to pass a variable to a function, like: `saturate(@color)`.
  3905. // The function should receive the value, not the variable.
  3906. //
  3907. Call.prototype.eval = function (env) {
  3908. var args = this.args.map(function (a) { return a.eval(env); }),
  3909. result, funcCaller = new FunctionCaller(this.name, env, this.currentFileInfo);
  3910. if (funcCaller.isValid()) { // 1.
  3911. try {
  3912. result = funcCaller.call(args);
  3913. if (result != null) {
  3914. return result;
  3915. }
  3916. } catch (e) {
  3917. throw { type: e.type || "Runtime",
  3918. message: "error evaluating function `" + this.name + "`" +
  3919. (e.message ? ': ' + e.message : ''),
  3920. index: this.index, filename: this.currentFileInfo.filename };
  3921. }
  3922. }
  3923. return new Call(this.name, args, this.index, this.currentFileInfo);
  3924. };
  3925. Call.prototype.genCSS = function (env, output) {
  3926. output.add(this.name + "(", this.currentFileInfo, this.index);
  3927. for(var i = 0; i < this.args.length; i++) {
  3928. this.args[i].genCSS(env, output);
  3929. if (i + 1 < this.args.length) {
  3930. output.add(", ");
  3931. }
  3932. }
  3933. output.add(")");
  3934. };
  3935. module.exports = Call;
  3936. },{"../functions/function-caller.js":11,"./node.js":51}],31:[function(require,module,exports){
  3937. var Node = require("./node.js"),
  3938. colors = require("../data/colors.js");
  3939. //
  3940. // RGB Colors - #ff0014, #eee
  3941. //
  3942. var Color = function (rgb, a) {
  3943. //
  3944. // The end goal here, is to parse the arguments
  3945. // into an integer triplet, such as `128, 255, 0`
  3946. //
  3947. // This facilitates operations and conversions.
  3948. //
  3949. if (Array.isArray(rgb)) {
  3950. this.rgb = rgb;
  3951. } else if (rgb.length == 6) {
  3952. this.rgb = rgb.match(/.{2}/g).map(function (c) {
  3953. return parseInt(c, 16);
  3954. });
  3955. } else {
  3956. this.rgb = rgb.split('').map(function (c) {
  3957. return parseInt(c + c, 16);
  3958. });
  3959. }
  3960. this.alpha = typeof(a) === 'number' ? a : 1;
  3961. };
  3962. Color.prototype = new Node();
  3963. Color.prototype.type = "Color";
  3964. function clamp(v, max) {
  3965. return Math.min(Math.max(v, 0), max);
  3966. }
  3967. function toHex(v) {
  3968. return '#' + v.map(function (c) {
  3969. c = clamp(Math.round(c), 255);
  3970. return (c < 16 ? '0' : '') + c.toString(16);
  3971. }).join('');
  3972. }
  3973. Color.prototype.luma = function () {
  3974. var r = this.rgb[0] / 255,
  3975. g = this.rgb[1] / 255,
  3976. b = this.rgb[2] / 255;
  3977. r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
  3978. g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
  3979. b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
  3980. return 0.2126 * r + 0.7152 * g + 0.0722 * b;
  3981. };
  3982. Color.prototype.genCSS = function (env, output) {
  3983. output.add(this.toCSS(env));
  3984. };
  3985. Color.prototype.toCSS = function (env, doNotCompress) {
  3986. var compress = env && env.compress && !doNotCompress, color, alpha;
  3987. // `keyword` is set if this color was originally
  3988. // converted from a named color string so we need
  3989. // to respect this and try to output named color too.
  3990. if (this.keyword) {
  3991. return this.keyword;
  3992. }
  3993. // If we have some transparency, the only way to represent it
  3994. // is via `rgba`. Otherwise, we use the hex representation,
  3995. // which has better compatibility with older browsers.
  3996. // Values are capped between `0` and `255`, rounded and zero-padded.
  3997. alpha = this.fround(env, this.alpha);
  3998. if (alpha < 1) {
  3999. return "rgba(" + this.rgb.map(function (c) {
  4000. return clamp(Math.round(c), 255);
  4001. }).concat(clamp(alpha, 1))
  4002. .join(',' + (compress ? '' : ' ')) + ")";
  4003. }
  4004. color = this.toRGB();
  4005. if (compress) {
  4006. var splitcolor = color.split('');
  4007. // Convert color to short format
  4008. if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
  4009. color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
  4010. }
  4011. }
  4012. return color;
  4013. };
  4014. //
  4015. // Operations have to be done per-channel, if not,
  4016. // channels will spill onto each other. Once we have
  4017. // our result, in the form of an integer triplet,
  4018. // we create a new Color node to hold the result.
  4019. //
  4020. Color.prototype.operate = function (env, op, other) {
  4021. var rgb = [];
  4022. var alpha = this.alpha * (1 - other.alpha) + other.alpha;
  4023. for (var c = 0; c < 3; c++) {
  4024. rgb[c] = this._operate(env, op, this.rgb[c], other.rgb[c]);
  4025. }
  4026. return new(Color)(rgb, alpha);
  4027. };
  4028. Color.prototype.toRGB = function () {
  4029. return toHex(this.rgb);
  4030. };
  4031. Color.prototype.toHSL = function () {
  4032. var r = this.rgb[0] / 255,
  4033. g = this.rgb[1] / 255,
  4034. b = this.rgb[2] / 255,
  4035. a = this.alpha;
  4036. var max = Math.max(r, g, b), min = Math.min(r, g, b);
  4037. var h, s, l = (max + min) / 2, d = max - min;
  4038. if (max === min) {
  4039. h = s = 0;
  4040. } else {
  4041. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  4042. switch (max) {
  4043. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  4044. case g: h = (b - r) / d + 2; break;
  4045. case b: h = (r - g) / d + 4; break;
  4046. }
  4047. h /= 6;
  4048. }
  4049. return { h: h * 360, s: s, l: l, a: a };
  4050. };
  4051. //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
  4052. Color.prototype.toHSV = function () {
  4053. var r = this.rgb[0] / 255,
  4054. g = this.rgb[1] / 255,
  4055. b = this.rgb[2] / 255,
  4056. a = this.alpha;
  4057. var max = Math.max(r, g, b), min = Math.min(r, g, b);
  4058. var h, s, v = max;
  4059. var d = max - min;
  4060. if (max === 0) {
  4061. s = 0;
  4062. } else {
  4063. s = d / max;
  4064. }
  4065. if (max === min) {
  4066. h = 0;
  4067. } else {
  4068. switch(max){
  4069. case r: h = (g - b) / d + (g < b ? 6 : 0); break;
  4070. case g: h = (b - r) / d + 2; break;
  4071. case b: h = (r - g) / d + 4; break;
  4072. }
  4073. h /= 6;
  4074. }
  4075. return { h: h * 360, s: s, v: v, a: a };
  4076. };
  4077. Color.prototype.toARGB = function () {
  4078. return toHex([this.alpha * 255].concat(this.rgb));
  4079. };
  4080. Color.prototype.compare = function (x) {
  4081. if (!x.rgb) {
  4082. return -1;
  4083. }
  4084. return (x.rgb[0] === this.rgb[0] &&
  4085. x.rgb[1] === this.rgb[1] &&
  4086. x.rgb[2] === this.rgb[2] &&
  4087. x.alpha === this.alpha) ? 0 : -1;
  4088. };
  4089. Color.fromKeyword = function(keyword) {
  4090. var c, key = keyword.toLowerCase();
  4091. if (colors.hasOwnProperty(key)) {
  4092. c = new(Color)(colors[key].slice(1));
  4093. }
  4094. else if (key === "transparent") {
  4095. c = new(Color)([0, 0, 0], 0);
  4096. }
  4097. if (c) {
  4098. c.keyword = keyword;
  4099. return c;
  4100. }
  4101. };
  4102. module.exports = Color;
  4103. },{"../data/colors.js":3,"./node.js":51}],32:[function(require,module,exports){
  4104. var Node = require("./node.js");
  4105. var Combinator = function (value) {
  4106. if (value === ' ') {
  4107. this.value = ' ';
  4108. } else {
  4109. this.value = value ? value.trim() : "";
  4110. }
  4111. };
  4112. Combinator.prototype = new Node();
  4113. Combinator.prototype.type = "Combinator";
  4114. var _noSpaceCombinators = {
  4115. '': true,
  4116. ' ': true,
  4117. '|': true
  4118. };
  4119. Combinator.prototype.genCSS = function (env, output) {
  4120. var spaceOrEmpty = (env.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
  4121. output.add(spaceOrEmpty + this.value + spaceOrEmpty);
  4122. };
  4123. module.exports = Combinator;
  4124. },{"./node.js":51}],33:[function(require,module,exports){
  4125. var Node = require("./node.js"),
  4126. getDebugInfo = require("./debug-info.js");
  4127. var Comment = function (value, isLineComment, index, currentFileInfo) {
  4128. this.value = value;
  4129. this.isLineComment = isLineComment;
  4130. this.currentFileInfo = currentFileInfo;
  4131. };
  4132. Comment.prototype = new Node();
  4133. Comment.prototype.type = "Comment";
  4134. Comment.prototype.genCSS = function (env, output) {
  4135. if (this.debugInfo) {
  4136. output.add(getDebugInfo(env, this), this.currentFileInfo, this.index);
  4137. }
  4138. output.add(this.value);
  4139. };
  4140. Comment.prototype.isSilent = function(env) {
  4141. var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
  4142. isCompressed = env.compress && this.value[2] !== "!";
  4143. return this.isLineComment || isReference || isCompressed;
  4144. };
  4145. Comment.prototype.markReferenced = function () {
  4146. this.isReferenced = true;
  4147. };
  4148. Comment.prototype.isRulesetLike = function(root) {
  4149. return Boolean(root);
  4150. };
  4151. module.exports = Comment;
  4152. },{"./debug-info.js":35,"./node.js":51}],34:[function(require,module,exports){
  4153. var Node = require("./node.js");
  4154. var Condition = function (op, l, r, i, negate) {
  4155. this.op = op.trim();
  4156. this.lvalue = l;
  4157. this.rvalue = r;
  4158. this.index = i;
  4159. this.negate = negate;
  4160. };
  4161. Condition.prototype = new Node();
  4162. Condition.prototype.type = "Condition";
  4163. Condition.prototype.accept = function (visitor) {
  4164. this.lvalue = visitor.visit(this.lvalue);
  4165. this.rvalue = visitor.visit(this.rvalue);
  4166. };
  4167. Condition.prototype.eval = function (env) {
  4168. var a = this.lvalue.eval(env),
  4169. b = this.rvalue.eval(env);
  4170. var i = this.index, result;
  4171. result = (function (op) {
  4172. switch (op) {
  4173. case 'and':
  4174. return a && b;
  4175. case 'or':
  4176. return a || b;
  4177. default:
  4178. if (a.compare) {
  4179. result = a.compare(b);
  4180. } else if (b.compare) {
  4181. result = b.compare(a);
  4182. } else {
  4183. throw { type: "Type",
  4184. message: "Unable to perform comparison",
  4185. index: i };
  4186. }
  4187. switch (result) {
  4188. case -1: return op === '<' || op === '=<' || op === '<=';
  4189. case 0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
  4190. case 1: return op === '>' || op === '>=';
  4191. }
  4192. }
  4193. })(this.op);
  4194. return this.negate ? !result : result;
  4195. };
  4196. module.exports = Condition;
  4197. },{"./node.js":51}],35:[function(require,module,exports){
  4198. var debugInfo = function(env, ctx, lineSeperator) {
  4199. var result="";
  4200. if (env.dumpLineNumbers && !env.compress) {
  4201. switch(env.dumpLineNumbers) {
  4202. case 'comments':
  4203. result = debugInfo.asComment(ctx);
  4204. break;
  4205. case 'mediaquery':
  4206. result = debugInfo.asMediaQuery(ctx);
  4207. break;
  4208. case 'all':
  4209. result = debugInfo.asComment(ctx) + (lineSeperator || "") + debugInfo.asMediaQuery(ctx);
  4210. break;
  4211. }
  4212. }
  4213. return result;
  4214. };
  4215. debugInfo.asComment = function(ctx) {
  4216. return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
  4217. };
  4218. debugInfo.asMediaQuery = function(ctx) {
  4219. return '@media -sass-debug-info{filename{font-family:' +
  4220. ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) {
  4221. if (a == '\\') {
  4222. a = '\/';
  4223. }
  4224. return '\\' + a;
  4225. }) +
  4226. '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
  4227. };
  4228. module.exports = debugInfo;
  4229. },{}],36:[function(require,module,exports){
  4230. var Node = require("./node.js"),
  4231. contexts = require("../contexts.js");
  4232. var DetachedRuleset = function (ruleset, frames) {
  4233. this.ruleset = ruleset;
  4234. this.frames = frames;
  4235. };
  4236. DetachedRuleset.prototype = new Node();
  4237. DetachedRuleset.prototype.type = "DetachedRuleset";
  4238. DetachedRuleset.prototype.evalFirst = true;
  4239. DetachedRuleset.prototype.accept = function (visitor) {
  4240. this.ruleset = visitor.visit(this.ruleset);
  4241. };
  4242. DetachedRuleset.prototype.eval = function (env) {
  4243. var frames = this.frames || env.frames.slice(0);
  4244. return new DetachedRuleset(this.ruleset, frames);
  4245. };
  4246. DetachedRuleset.prototype.callEval = function (env) {
  4247. return this.ruleset.eval(this.frames ? new(contexts.evalEnv)(env, this.frames.concat(env.frames)) : env);
  4248. };
  4249. module.exports = DetachedRuleset;
  4250. },{"../contexts.js":2,"./node.js":51}],37:[function(require,module,exports){
  4251. var Node = require("./node.js"),
  4252. unitConversions = require("../data/unit-conversions.js"),
  4253. Unit = require("./unit.js"),
  4254. Color = require("./color.js");
  4255. //
  4256. // A number with a unit
  4257. //
  4258. var Dimension = function (value, unit) {
  4259. this.value = parseFloat(value);
  4260. this.unit = (unit && unit instanceof Unit) ? unit :
  4261. new(Unit)(unit ? [unit] : undefined);
  4262. };
  4263. Dimension.prototype = new Node();
  4264. Dimension.prototype.type = "Dimension";
  4265. Dimension.prototype.accept = function (visitor) {
  4266. this.unit = visitor.visit(this.unit);
  4267. };
  4268. Dimension.prototype.eval = function (env) {
  4269. return this;
  4270. };
  4271. Dimension.prototype.toColor = function () {
  4272. return new(Color)([this.value, this.value, this.value]);
  4273. };
  4274. Dimension.prototype.genCSS = function (env, output) {
  4275. if ((env && env.strictUnits) && !this.unit.isSingular()) {
  4276. throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
  4277. }
  4278. var value = this.fround(env, this.value),
  4279. strValue = String(value);
  4280. if (value !== 0 && value < 0.000001 && value > -0.000001) {
  4281. // would be output 1e-6 etc.
  4282. strValue = value.toFixed(20).replace(/0+$/, "");
  4283. }
  4284. if (env && env.compress) {
  4285. // Zero values doesn't need a unit
  4286. if (value === 0 && this.unit.isLength()) {
  4287. output.add(strValue);
  4288. return;
  4289. }
  4290. // Float values doesn't need a leading zero
  4291. if (value > 0 && value < 1) {
  4292. strValue = (strValue).substr(1);
  4293. }
  4294. }
  4295. output.add(strValue);
  4296. this.unit.genCSS(env, output);
  4297. };
  4298. // In an operation between two Dimensions,
  4299. // we default to the first Dimension's unit,
  4300. // so `1px + 2` will yield `3px`.
  4301. Dimension.prototype.operate = function (env, op, other) {
  4302. /*jshint noempty:false */
  4303. var value = this._operate(env, op, this.value, other.value),
  4304. unit = this.unit.clone();
  4305. if (op === '+' || op === '-') {
  4306. if (unit.numerator.length === 0 && unit.denominator.length === 0) {
  4307. unit.numerator = other.unit.numerator.slice(0);
  4308. unit.denominator = other.unit.denominator.slice(0);
  4309. } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
  4310. // do nothing
  4311. } else {
  4312. other = other.convertTo(this.unit.usedUnits());
  4313. if(env.strictUnits && other.unit.toString() !== unit.toString()) {
  4314. throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
  4315. "' and '" + other.unit.toString() + "'.");
  4316. }
  4317. value = this._operate(env, op, this.value, other.value);
  4318. }
  4319. } else if (op === '*') {
  4320. unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
  4321. unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
  4322. unit.cancel();
  4323. } else if (op === '/') {
  4324. unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
  4325. unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
  4326. unit.cancel();
  4327. }
  4328. return new(Dimension)(value, unit);
  4329. };
  4330. Dimension.prototype.compare = function (other) {
  4331. if (other instanceof Dimension) {
  4332. var a, b,
  4333. aValue, bValue;
  4334. if (this.unit.isEmpty() || other.unit.isEmpty()) {
  4335. a = this;
  4336. b = other;
  4337. } else {
  4338. a = this.unify();
  4339. b = other.unify();
  4340. if (a.unit.compare(b.unit) !== 0) {
  4341. return -1;
  4342. }
  4343. }
  4344. aValue = a.value;
  4345. bValue = b.value;
  4346. if (bValue > aValue) {
  4347. return -1;
  4348. } else if (bValue < aValue) {
  4349. return 1;
  4350. } else {
  4351. return 0;
  4352. }
  4353. } else {
  4354. return -1;
  4355. }
  4356. };
  4357. Dimension.prototype.unify = function () {
  4358. return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
  4359. };
  4360. Dimension.prototype.convertTo = function (conversions) {
  4361. var value = this.value, unit = this.unit.clone(),
  4362. i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
  4363. if (typeof conversions === 'string') {
  4364. for(i in unitConversions) {
  4365. if (unitConversions[i].hasOwnProperty(conversions)) {
  4366. derivedConversions = {};
  4367. derivedConversions[i] = conversions;
  4368. }
  4369. }
  4370. conversions = derivedConversions;
  4371. }
  4372. applyUnit = function (atomicUnit, denominator) {
  4373. /*jshint loopfunc:true */
  4374. if (group.hasOwnProperty(atomicUnit)) {
  4375. if (denominator) {
  4376. value = value / (group[atomicUnit] / group[targetUnit]);
  4377. } else {
  4378. value = value * (group[atomicUnit] / group[targetUnit]);
  4379. }
  4380. return targetUnit;
  4381. }
  4382. return atomicUnit;
  4383. };
  4384. for (groupName in conversions) {
  4385. if (conversions.hasOwnProperty(groupName)) {
  4386. targetUnit = conversions[groupName];
  4387. group = unitConversions[groupName];
  4388. unit.map(applyUnit);
  4389. }
  4390. }
  4391. unit.cancel();
  4392. return new(Dimension)(value, unit);
  4393. };
  4394. module.exports = Dimension;
  4395. },{"../data/unit-conversions.js":5,"./color.js":31,"./node.js":51,"./unit.js":60}],38:[function(require,module,exports){
  4396. var Node = require("./node.js"),
  4397. Ruleset = require("./ruleset.js");
  4398. var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) {
  4399. this.name = name;
  4400. this.value = value;
  4401. if (rules) {
  4402. this.rules = rules;
  4403. this.rules.allowImports = true;
  4404. }
  4405. this.index = index;
  4406. this.currentFileInfo = currentFileInfo;
  4407. this.debugInfo = debugInfo;
  4408. };
  4409. Directive.prototype = new Node();
  4410. Directive.prototype.type = "Directive";
  4411. Directive.prototype.accept = function (visitor) {
  4412. var value = this.value, rules = this.rules;
  4413. if (rules) {
  4414. rules = visitor.visit(rules);
  4415. }
  4416. if (value) {
  4417. value = visitor.visit(value);
  4418. }
  4419. };
  4420. Directive.prototype.isRulesetLike = function() {
  4421. return this.rules || !this.isCharset();
  4422. };
  4423. Directive.prototype.isCharset = function() {
  4424. return "@charset" === this.name;
  4425. };
  4426. Directive.prototype.genCSS = function (env, output) {
  4427. var value = this.value, rules = this.rules;
  4428. output.add(this.name, this.currentFileInfo, this.index);
  4429. if (value) {
  4430. output.add(' ');
  4431. value.genCSS(env, output);
  4432. }
  4433. if (rules) {
  4434. this.outputRuleset(env, output, [rules]);
  4435. } else {
  4436. output.add(';');
  4437. }
  4438. };
  4439. Directive.prototype.eval = function (env) {
  4440. var value = this.value, rules = this.rules;
  4441. if (value) {
  4442. value = value.eval(env);
  4443. }
  4444. if (rules) {
  4445. rules = rules.eval(env);
  4446. rules.root = true;
  4447. }
  4448. return new(Directive)(this.name, value, rules,
  4449. this.index, this.currentFileInfo, this.debugInfo);
  4450. };
  4451. Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); };
  4452. Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); };
  4453. Directive.prototype.rulesets = function () { if (this.rules) return Ruleset.prototype.rulesets.apply(this.rules); };
  4454. Directive.prototype.markReferenced = function () {
  4455. var i, rules;
  4456. this.isReferenced = true;
  4457. if (this.rules) {
  4458. rules = this.rules.rules;
  4459. for (i = 0; i < rules.length; i++) {
  4460. if (rules[i].markReferenced) {
  4461. rules[i].markReferenced();
  4462. }
  4463. }
  4464. }
  4465. };
  4466. Directive.prototype.outputRuleset = function (env, output, rules) {
  4467. var ruleCnt = rules.length, i;
  4468. env.tabLevel = (env.tabLevel | 0) + 1;
  4469. // Compressed
  4470. if (env.compress) {
  4471. output.add('{');
  4472. for (i = 0; i < ruleCnt; i++) {
  4473. rules[i].genCSS(env, output);
  4474. }
  4475. output.add('}');
  4476. env.tabLevel--;
  4477. return;
  4478. }
  4479. // Non-compressed
  4480. var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " ";
  4481. if (!ruleCnt) {
  4482. output.add(" {" + tabSetStr + '}');
  4483. } else {
  4484. output.add(" {" + tabRuleStr);
  4485. rules[0].genCSS(env, output);
  4486. for (i = 1; i < ruleCnt; i++) {
  4487. output.add(tabRuleStr);
  4488. rules[i].genCSS(env, output);
  4489. }
  4490. output.add(tabSetStr + '}');
  4491. }
  4492. env.tabLevel--;
  4493. };
  4494. module.exports = Directive;
  4495. },{"./node.js":51,"./ruleset.js":57}],39:[function(require,module,exports){
  4496. var Node = require("./node.js"),
  4497. Combinator = require("./combinator.js");
  4498. var Element = function (combinator, value, index, currentFileInfo) {
  4499. this.combinator = combinator instanceof Combinator ?
  4500. combinator : new(Combinator)(combinator);
  4501. if (typeof(value) === 'string') {
  4502. this.value = value.trim();
  4503. } else if (value) {
  4504. this.value = value;
  4505. } else {
  4506. this.value = "";
  4507. }
  4508. this.index = index;
  4509. this.currentFileInfo = currentFileInfo;
  4510. };
  4511. Element.prototype = new Node();
  4512. Element.prototype.type = "Element";
  4513. Element.prototype.accept = function (visitor) {
  4514. var value = this.value;
  4515. this.combinator = visitor.visit(this.combinator);
  4516. if (typeof value === "object") {
  4517. this.value = visitor.visit(value);
  4518. }
  4519. };
  4520. Element.prototype.eval = function (env) {
  4521. return new(Element)(this.combinator,
  4522. this.value.eval ? this.value.eval(env) : this.value,
  4523. this.index,
  4524. this.currentFileInfo);
  4525. };
  4526. Element.prototype.genCSS = function (env, output) {
  4527. output.add(this.toCSS(env), this.currentFileInfo, this.index);
  4528. };
  4529. Element.prototype.toCSS = function (env) {
  4530. var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
  4531. if (value === '' && this.combinator.value.charAt(0) === '&') {
  4532. return '';
  4533. } else {
  4534. return this.combinator.toCSS(env || {}) + value;
  4535. }
  4536. };
  4537. module.exports = Element;
  4538. },{"./combinator.js":32,"./node.js":51}],40:[function(require,module,exports){
  4539. var Node = require("./node.js"),
  4540. Paren = require("./paren.js"),
  4541. Comment = require("./comment.js");
  4542. var Expression = function (value) { this.value = value; };
  4543. Expression.prototype = new Node();
  4544. Expression.prototype.type = "Expression";
  4545. Expression.prototype.accept = function (visitor) {
  4546. if (this.value) {
  4547. this.value = visitor.visitArray(this.value);
  4548. }
  4549. };
  4550. Expression.prototype.eval = function (env) {
  4551. var returnValue,
  4552. inParenthesis = this.parens && !this.parensInOp,
  4553. doubleParen = false;
  4554. if (inParenthesis) {
  4555. env.inParenthesis();
  4556. }
  4557. if (this.value.length > 1) {
  4558. returnValue = new(Expression)(this.value.map(function (e) {
  4559. return e.eval(env);
  4560. }));
  4561. } else if (this.value.length === 1) {
  4562. if (this.value[0].parens && !this.value[0].parensInOp) {
  4563. doubleParen = true;
  4564. }
  4565. returnValue = this.value[0].eval(env);
  4566. } else {
  4567. returnValue = this;
  4568. }
  4569. if (inParenthesis) {
  4570. env.outOfParenthesis();
  4571. }
  4572. if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) {
  4573. returnValue = new(Paren)(returnValue);
  4574. }
  4575. return returnValue;
  4576. };
  4577. Expression.prototype.genCSS = function (env, output) {
  4578. for(var i = 0; i < this.value.length; i++) {
  4579. this.value[i].genCSS(env, output);
  4580. if (i + 1 < this.value.length) {
  4581. output.add(" ");
  4582. }
  4583. }
  4584. };
  4585. Expression.prototype.throwAwayComments = function () {
  4586. this.value = this.value.filter(function(v) {
  4587. return !(v instanceof Comment);
  4588. });
  4589. };
  4590. module.exports = Expression;
  4591. },{"./comment.js":33,"./node.js":51,"./paren.js":53}],41:[function(require,module,exports){
  4592. var Node = require("./node.js");
  4593. var Extend = function Extend(selector, option, index) {
  4594. this.selector = selector;
  4595. this.option = option;
  4596. this.index = index;
  4597. this.object_id = Extend.next_id++;
  4598. this.parent_ids = [this.object_id];
  4599. switch(option) {
  4600. case "all":
  4601. this.allowBefore = true;
  4602. this.allowAfter = true;
  4603. break;
  4604. default:
  4605. this.allowBefore = false;
  4606. this.allowAfter = false;
  4607. break;
  4608. }
  4609. };
  4610. Extend.next_id = 0;
  4611. Extend.prototype = new Node();
  4612. Extend.prototype.type = "Extend";
  4613. Extend.prototype.accept = function (visitor) {
  4614. this.selector = visitor.visit(this.selector);
  4615. };
  4616. Extend.prototype.eval = function (env) {
  4617. return new(Extend)(this.selector.eval(env), this.option, this.index);
  4618. };
  4619. Extend.prototype.clone = function (env) {
  4620. return new(Extend)(this.selector, this.option, this.index);
  4621. };
  4622. Extend.prototype.findSelfSelectors = function (selectors) {
  4623. var selfElements = [],
  4624. i,
  4625. selectorElements;
  4626. for(i = 0; i < selectors.length; i++) {
  4627. selectorElements = selectors[i].elements;
  4628. // duplicate the logic in genCSS function inside the selector node.
  4629. // future TODO - move both logics into the selector joiner visitor
  4630. if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
  4631. selectorElements[0].combinator.value = ' ';
  4632. }
  4633. selfElements = selfElements.concat(selectors[i].elements);
  4634. }
  4635. this.selfSelectors = [{ elements: selfElements }];
  4636. };
  4637. module.exports = Extend;
  4638. },{"./node.js":51}],42:[function(require,module,exports){
  4639. var Node = require("./node.js"),
  4640. Media = require("./media.js"),
  4641. URL = require("./url.js"),
  4642. Quoted = require("./quoted.js"),
  4643. Ruleset = require("./ruleset.js"),
  4644. Anonymous = require("./anonymous.js");
  4645. //
  4646. // CSS @import node
  4647. //
  4648. // The general strategy here is that we don't want to wait
  4649. // for the parsing to be completed, before we start importing
  4650. // the file. That's because in the context of a browser,
  4651. // most of the time will be spent waiting for the server to respond.
  4652. //
  4653. // On creation, we push the import path to our import queue, though
  4654. // `import,push`, we also pass it a callback, which it'll call once
  4655. // the file has been fetched, and parsed.
  4656. //
  4657. var Import = function (path, features, options, index, currentFileInfo) {
  4658. this.options = options;
  4659. this.index = index;
  4660. this.path = path;
  4661. this.features = features;
  4662. this.currentFileInfo = currentFileInfo;
  4663. if (this.options.less !== undefined || this.options.inline) {
  4664. this.css = !this.options.less || this.options.inline;
  4665. } else {
  4666. var pathValue = this.getPath();
  4667. if (pathValue && /css([\?;].*)?$/.test(pathValue)) {
  4668. this.css = true;
  4669. }
  4670. }
  4671. };
  4672. //
  4673. // The actual import node doesn't return anything, when converted to CSS.
  4674. // The reason is that it's used at the evaluation stage, so that the rules
  4675. // it imports can be treated like any other rules.
  4676. //
  4677. // In `eval`, we make sure all Import nodes get evaluated, recursively, so
  4678. // we end up with a flat structure, which can easily be imported in the parent
  4679. // ruleset.
  4680. //
  4681. Import.prototype = new Node();
  4682. Import.prototype.type = "Import";
  4683. Import.prototype.accept = function (visitor) {
  4684. if (this.features) {
  4685. this.features = visitor.visit(this.features);
  4686. }
  4687. this.path = visitor.visit(this.path);
  4688. if (!this.options.inline && this.root) {
  4689. this.root = visitor.visit(this.root);
  4690. }
  4691. };
  4692. Import.prototype.genCSS = function (env, output) {
  4693. if (this.css) {
  4694. output.add("@import ", this.currentFileInfo, this.index);
  4695. this.path.genCSS(env, output);
  4696. if (this.features) {
  4697. output.add(" ");
  4698. this.features.genCSS(env, output);
  4699. }
  4700. output.add(';');
  4701. }
  4702. };
  4703. Import.prototype.getPath = function () {
  4704. if (this.path instanceof Quoted) {
  4705. var path = this.path.value;
  4706. return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less';
  4707. } else if (this.path instanceof URL) {
  4708. return this.path.value.value;
  4709. }
  4710. return null;
  4711. };
  4712. Import.prototype.evalForImport = function (env) {
  4713. return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo);
  4714. };
  4715. Import.prototype.evalPath = function (env) {
  4716. var path = this.path.eval(env);
  4717. var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
  4718. if (!(path instanceof URL)) {
  4719. if (rootpath) {
  4720. var pathValue = path.value;
  4721. // Add the base path if the import is relative
  4722. if (pathValue && env.isPathRelative(pathValue)) {
  4723. path.value = rootpath +pathValue;
  4724. }
  4725. }
  4726. path.value = env.normalizePath(path.value);
  4727. }
  4728. return path;
  4729. };
  4730. Import.prototype.eval = function (env) {
  4731. var ruleset, features = this.features && this.features.eval(env);
  4732. if (this.skip) {
  4733. if (typeof this.skip === "function") {
  4734. this.skip = this.skip();
  4735. }
  4736. if (this.skip) {
  4737. return [];
  4738. }
  4739. }
  4740. if (this.options.inline) {
  4741. //todo needs to reference css file not import
  4742. var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
  4743. return this.features ? new(Media)([contents], this.features.value) : [contents];
  4744. } else if (this.css) {
  4745. var newImport = new(Import)(this.evalPath(env), features, this.options, this.index);
  4746. if (!newImport.css && this.error) {
  4747. throw this.error;
  4748. }
  4749. return newImport;
  4750. } else {
  4751. ruleset = new(Ruleset)(null, this.root.rules.slice(0));
  4752. ruleset.evalImports(env);
  4753. return this.features ? new(Media)(ruleset.rules, this.features.value) : ruleset.rules;
  4754. }
  4755. };
  4756. module.exports = Import;
  4757. },{"./anonymous.js":27,"./media.js":47,"./node.js":51,"./quoted.js":54,"./ruleset.js":57,"./url.js":61}],43:[function(require,module,exports){
  4758. var tree = {};
  4759. tree.Alpha = require('./alpha');
  4760. tree.Color = require('./color');
  4761. tree.Directive = require('./directive');
  4762. tree.DetachedRuleset = require('./detached-ruleset');
  4763. tree.Operation = require('./operation');
  4764. tree.Dimension = require('./dimension');
  4765. tree.Unit = require('./unit');
  4766. tree.Keyword = require('./keyword');
  4767. tree.Variable = require('./variable');
  4768. tree.Ruleset = require('./ruleset');
  4769. tree.Element = require('./element');
  4770. tree.Attribute = require('./attribute');
  4771. tree.Combinator = require('./combinator');
  4772. tree.Selector = require('./selector');
  4773. tree.Quoted = require('./quoted');
  4774. tree.Expression = require('./expression');
  4775. tree.Rule = require('./rule');
  4776. tree.Call = require('./call');
  4777. tree.URL = require('./url');
  4778. tree.Import = require('./import');
  4779. tree.mixin = {
  4780. Call: require('./mixin-call'),
  4781. Definition: require('./mixin-definition')
  4782. };
  4783. tree.Comment = require('./comment');
  4784. tree.Anonymous = require('./anonymous');
  4785. tree.Value = require('./value');
  4786. tree.JavaScript = require('./javascript');
  4787. tree.Assignment = require('./assignment');
  4788. tree.Condition = require('./condition');
  4789. tree.Paren = require('./paren');
  4790. tree.Media = require('./media');
  4791. tree.UnicodeDescriptor = require('./unicode-descriptor');
  4792. tree.Negative = require('./negative');
  4793. tree.Extend = require('./extend');
  4794. tree.RulesetCall = require('./ruleset-call');
  4795. module.exports = tree;
  4796. },{"./alpha":26,"./anonymous":27,"./assignment":28,"./attribute":29,"./call":30,"./color":31,"./combinator":32,"./comment":33,"./condition":34,"./detached-ruleset":36,"./dimension":37,"./directive":38,"./element":39,"./expression":40,"./extend":41,"./import":42,"./javascript":44,"./keyword":46,"./media":47,"./mixin-call":48,"./mixin-definition":49,"./negative":50,"./operation":52,"./paren":53,"./quoted":54,"./rule":55,"./ruleset":57,"./ruleset-call":56,"./selector":58,"./unicode-descriptor":59,"./unit":60,"./url":61,"./value":62,"./variable":63}],44:[function(require,module,exports){
  4797. var JsEvalNode = require("./js-eval-node.js"),
  4798. Dimension = require("./dimension.js"),
  4799. Quoted = require("./quoted.js"),
  4800. Anonymous = require("./anonymous.js");
  4801. var JavaScript = function (string, index, escaped) {
  4802. this.escaped = escaped;
  4803. this.expression = string;
  4804. this.index = index;
  4805. };
  4806. JavaScript.prototype = new JsEvalNode();
  4807. JavaScript.prototype.type = "JavaScript";
  4808. JavaScript.prototype.eval = function(env) {
  4809. var result = this.evaluateJavaScript(this.expression, env);
  4810. if (typeof(result) === 'number') {
  4811. return new(Dimension)(result);
  4812. } else if (typeof(result) === 'string') {
  4813. return new(Quoted)('"' + result + '"', result, this.escaped, this.index);
  4814. } else if (Array.isArray(result)) {
  4815. return new(Anonymous)(result.join(', '));
  4816. } else {
  4817. return new(Anonymous)(result);
  4818. }
  4819. };
  4820. module.exports = JavaScript;
  4821. },{"./anonymous.js":27,"./dimension.js":37,"./js-eval-node.js":45,"./quoted.js":54}],45:[function(require,module,exports){
  4822. var Node = require("./node.js"),
  4823. Variable = require("./variable.js");
  4824. var jsEvalNode = function() {
  4825. };
  4826. jsEvalNode.prototype = new Node();
  4827. jsEvalNode.prototype.evaluateJavaScript = function (expression, env) {
  4828. var result,
  4829. that = this,
  4830. context = {};
  4831. if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) {
  4832. throw { message: "You are using JavaScript, which has been disabled." ,
  4833. index: this.index };
  4834. }
  4835. expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
  4836. return that.jsify(new(Variable)('@' + name, that.index).eval(env));
  4837. });
  4838. try {
  4839. expression = new(Function)('return (' + expression + ')');
  4840. } catch (e) {
  4841. throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
  4842. index: this.index };
  4843. }
  4844. var variables = env.frames[0].variables();
  4845. for (var k in variables) {
  4846. if (variables.hasOwnProperty(k)) {
  4847. /*jshint loopfunc:true */
  4848. context[k.slice(1)] = {
  4849. value: variables[k].value,
  4850. toJS: function () {
  4851. return this.value.eval(env).toCSS();
  4852. }
  4853. };
  4854. }
  4855. }
  4856. try {
  4857. result = expression.call(context);
  4858. } catch (e) {
  4859. throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
  4860. index: this.index };
  4861. }
  4862. return result;
  4863. };
  4864. jsEvalNode.prototype.jsify = function (obj) {
  4865. if (Array.isArray(obj.value) && (obj.value.length > 1)) {
  4866. return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
  4867. } else {
  4868. return obj.toCSS();
  4869. }
  4870. };
  4871. module.exports = jsEvalNode;
  4872. },{"./node.js":51,"./variable.js":63}],46:[function(require,module,exports){
  4873. var Node = require("./node.js");
  4874. var Keyword = function (value) { this.value = value; };
  4875. Keyword.prototype = new Node();
  4876. Keyword.prototype.type = "Keyword";
  4877. Keyword.prototype.genCSS = function (env, output) {
  4878. if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
  4879. output.add(this.value);
  4880. };
  4881. Keyword.prototype.compare = function (other) {
  4882. if (other instanceof Keyword) {
  4883. return other.value === this.value ? 0 : 1;
  4884. } else {
  4885. return -1;
  4886. }
  4887. };
  4888. Keyword.True = new(Keyword)('true');
  4889. Keyword.False = new(Keyword)('false');
  4890. module.exports = Keyword;
  4891. },{"./node.js":51}],47:[function(require,module,exports){
  4892. var Ruleset = require("./ruleset.js"),
  4893. Value = require("./value.js"),
  4894. Element = require("./element.js"),
  4895. Selector = require("./selector.js"),
  4896. Anonymous = require("./anonymous.js"),
  4897. Expression = require("./expression.js"),
  4898. Directive = require("./directive.js");
  4899. var Media = function (value, features, index, currentFileInfo) {
  4900. this.index = index;
  4901. this.currentFileInfo = currentFileInfo;
  4902. var selectors = this.emptySelectors();
  4903. this.features = new(Value)(features);
  4904. this.rules = [new(Ruleset)(selectors, value)];
  4905. this.rules[0].allowImports = true;
  4906. };
  4907. Media.prototype = new Directive();
  4908. Media.prototype.type = "Media";
  4909. Media.prototype.isRulesetLike = true;
  4910. Media.prototype.accept = function (visitor) {
  4911. if (this.features) {
  4912. this.features = visitor.visit(this.features);
  4913. }
  4914. if (this.rules) {
  4915. this.rules = visitor.visitArray(this.rules);
  4916. }
  4917. };
  4918. Media.prototype.genCSS = function (env, output) {
  4919. output.add('@media ', this.currentFileInfo, this.index);
  4920. this.features.genCSS(env, output);
  4921. this.outputRuleset(env, output, this.rules);
  4922. };
  4923. Media.prototype.eval = function (env) {
  4924. if (!env.mediaBlocks) {
  4925. env.mediaBlocks = [];
  4926. env.mediaPath = [];
  4927. }
  4928. var media = new(Media)(null, [], this.index, this.currentFileInfo);
  4929. if(this.debugInfo) {
  4930. this.rules[0].debugInfo = this.debugInfo;
  4931. media.debugInfo = this.debugInfo;
  4932. }
  4933. var strictMathBypass = false;
  4934. if (!env.strictMath) {
  4935. strictMathBypass = true;
  4936. env.strictMath = true;
  4937. }
  4938. try {
  4939. media.features = this.features.eval(env);
  4940. }
  4941. finally {
  4942. if (strictMathBypass) {
  4943. env.strictMath = false;
  4944. }
  4945. }
  4946. env.mediaPath.push(media);
  4947. env.mediaBlocks.push(media);
  4948. env.frames.unshift(this.rules[0]);
  4949. media.rules = [this.rules[0].eval(env)];
  4950. env.frames.shift();
  4951. env.mediaPath.pop();
  4952. return env.mediaPath.length === 0 ? media.evalTop(env) :
  4953. media.evalNested(env);
  4954. };
  4955. //TODO merge with directive
  4956. Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); };
  4957. Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); };
  4958. Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); };
  4959. Media.prototype.emptySelectors = function() {
  4960. var el = new(Element)('', '&', this.index, this.currentFileInfo),
  4961. sels = [new(Selector)([el], null, null, this.index, this.currentFileInfo)];
  4962. sels[0].mediaEmpty = true;
  4963. return sels;
  4964. };
  4965. Media.prototype.markReferenced = function () {
  4966. var i, rules = this.rules[0].rules;
  4967. this.rules[0].markReferenced();
  4968. this.isReferenced = true;
  4969. for (i = 0; i < rules.length; i++) {
  4970. if (rules[i].markReferenced) {
  4971. rules[i].markReferenced();
  4972. }
  4973. }
  4974. };
  4975. Media.prototype.evalTop = function (env) {
  4976. var result = this;
  4977. // Render all dependent Media blocks.
  4978. if (env.mediaBlocks.length > 1) {
  4979. var selectors = this.emptySelectors();
  4980. result = new(Ruleset)(selectors, env.mediaBlocks);
  4981. result.multiMedia = true;
  4982. }
  4983. delete env.mediaBlocks;
  4984. delete env.mediaPath;
  4985. return result;
  4986. };
  4987. Media.prototype.evalNested = function (env) {
  4988. var i, value,
  4989. path = env.mediaPath.concat([this]);
  4990. // Extract the media-query conditions separated with `,` (OR).
  4991. for (i = 0; i < path.length; i++) {
  4992. value = path[i].features instanceof Value ?
  4993. path[i].features.value : path[i].features;
  4994. path[i] = Array.isArray(value) ? value : [value];
  4995. }
  4996. // Trace all permutations to generate the resulting media-query.
  4997. //
  4998. // (a, b and c) with nested (d, e) ->
  4999. // a and d
  5000. // a and e
  5001. // b and c and d
  5002. // b and c and e
  5003. this.features = new(Value)(this.permute(path).map(function (path) {
  5004. path = path.map(function (fragment) {
  5005. return fragment.toCSS ? fragment : new(Anonymous)(fragment);
  5006. });
  5007. for(i = path.length - 1; i > 0; i--) {
  5008. path.splice(i, 0, new(Anonymous)("and"));
  5009. }
  5010. return new(Expression)(path);
  5011. }));
  5012. // Fake a tree-node that doesn't output anything.
  5013. return new(Ruleset)([], []);
  5014. };
  5015. Media.prototype.permute = function (arr) {
  5016. if (arr.length === 0) {
  5017. return [];
  5018. } else if (arr.length === 1) {
  5019. return arr[0];
  5020. } else {
  5021. var result = [];
  5022. var rest = this.permute(arr.slice(1));
  5023. for (var i = 0; i < rest.length; i++) {
  5024. for (var j = 0; j < arr[0].length; j++) {
  5025. result.push([arr[0][j]].concat(rest[i]));
  5026. }
  5027. }
  5028. return result;
  5029. }
  5030. };
  5031. Media.prototype.bubbleSelectors = function (selectors) {
  5032. if (!selectors)
  5033. return;
  5034. this.rules = [new(Ruleset)(selectors.slice(0), [this.rules[0]])];
  5035. };
  5036. module.exports = Media;
  5037. },{"./anonymous.js":27,"./directive.js":38,"./element.js":39,"./expression.js":40,"./ruleset.js":57,"./selector.js":58,"./value.js":62}],48:[function(require,module,exports){
  5038. var Node = require("./node.js"),
  5039. Selector = require("./selector.js"),
  5040. MixinDefinition = require("./mixin-definition.js"),
  5041. defaultFunc = require("../functions/default.js");
  5042. var MixinCall = function (elements, args, index, currentFileInfo, important) {
  5043. this.selector = new(Selector)(elements);
  5044. this.arguments = (args && args.length) ? args : null;
  5045. this.index = index;
  5046. this.currentFileInfo = currentFileInfo;
  5047. this.important = important;
  5048. };
  5049. MixinCall.prototype = new Node();
  5050. MixinCall.prototype.type = "MixinCall";
  5051. MixinCall.prototype.accept = function (visitor) {
  5052. if (this.selector) {
  5053. this.selector = visitor.visit(this.selector);
  5054. }
  5055. if (this.arguments) {
  5056. this.arguments = visitor.visitArray(this.arguments);
  5057. }
  5058. };
  5059. MixinCall.prototype.eval = function (env) {
  5060. var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
  5061. candidates = [], candidate, conditionResult = [],
  5062. defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset;
  5063. args = this.arguments && this.arguments.map(function (a) {
  5064. return { name: a.name, value: a.value.eval(env) };
  5065. });
  5066. for (i = 0; i < env.frames.length; i++) {
  5067. if ((mixins = env.frames[i].find(this.selector)).length > 0) {
  5068. isOneFound = true;
  5069. // To make `default()` function independent of definition order we have two "subpasses" here.
  5070. // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
  5071. // and build candidate list with corresponding flags. Then, when we know all possible matches,
  5072. // we make a final decision.
  5073. for (m = 0; m < mixins.length; m++) {
  5074. mixin = mixins[m];
  5075. isRecursive = false;
  5076. for(f = 0; f < env.frames.length; f++) {
  5077. if ((!(mixin instanceof MixinDefinition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
  5078. isRecursive = true;
  5079. break;
  5080. }
  5081. }
  5082. if (isRecursive) {
  5083. continue;
  5084. }
  5085. if (mixin.matchArgs(args, env)) {
  5086. candidate = {mixin: mixin, group: defNone};
  5087. if (mixin.matchCondition) {
  5088. for (f = 0; f < 2; f++) {
  5089. defaultFunc.value(f);
  5090. conditionResult[f] = mixin.matchCondition(args, env);
  5091. }
  5092. if (conditionResult[0] || conditionResult[1]) {
  5093. if (conditionResult[0] != conditionResult[1]) {
  5094. candidate.group = conditionResult[1] ?
  5095. defTrue : defFalse;
  5096. }
  5097. candidates.push(candidate);
  5098. }
  5099. }
  5100. else {
  5101. candidates.push(candidate);
  5102. }
  5103. match = true;
  5104. }
  5105. }
  5106. defaultFunc.reset();
  5107. count = [0, 0, 0];
  5108. for (m = 0; m < candidates.length; m++) {
  5109. count[candidates[m].group]++;
  5110. }
  5111. if (count[defNone] > 0) {
  5112. defaultResult = defFalse;
  5113. } else {
  5114. defaultResult = defTrue;
  5115. if ((count[defTrue] + count[defFalse]) > 1) {
  5116. throw { type: 'Runtime',
  5117. message: 'Ambiguous use of `default()` found when matching for `'
  5118. + this.format(args) + '`',
  5119. index: this.index, filename: this.currentFileInfo.filename };
  5120. }
  5121. }
  5122. for (m = 0; m < candidates.length; m++) {
  5123. candidate = candidates[m].group;
  5124. if ((candidate === defNone) || (candidate === defaultResult)) {
  5125. try {
  5126. mixin = candidates[m].mixin;
  5127. if (!(mixin instanceof MixinDefinition)) {
  5128. originalRuleset = mixin.originalRuleset || mixin;
  5129. mixin = new MixinDefinition("", [], mixin.rules, null, false);
  5130. mixin.originalRuleset = originalRuleset;
  5131. }
  5132. Array.prototype.push.apply(
  5133. rules, mixin.evalCall(env, args, this.important).rules);
  5134. } catch (e) {
  5135. throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
  5136. }
  5137. }
  5138. }
  5139. if (match) {
  5140. if (!this.currentFileInfo || !this.currentFileInfo.reference) {
  5141. for (i = 0; i < rules.length; i++) {
  5142. rule = rules[i];
  5143. if (rule.markReferenced) {
  5144. rule.markReferenced();
  5145. }
  5146. }
  5147. }
  5148. return rules;
  5149. }
  5150. }
  5151. }
  5152. if (isOneFound) {
  5153. throw { type: 'Runtime',
  5154. message: 'No matching definition was found for `' + this.format(args) + '`',
  5155. index: this.index, filename: this.currentFileInfo.filename };
  5156. } else {
  5157. throw { type: 'Name',
  5158. message: this.selector.toCSS().trim() + " is undefined",
  5159. index: this.index, filename: this.currentFileInfo.filename };
  5160. }
  5161. };
  5162. MixinCall.prototype.format = function (args) {
  5163. return this.selector.toCSS().trim() + '(' +
  5164. (args ? args.map(function (a) {
  5165. var argValue = "";
  5166. if (a.name) {
  5167. argValue += a.name + ":";
  5168. }
  5169. if (a.value.toCSS) {
  5170. argValue += a.value.toCSS();
  5171. } else {
  5172. argValue += "???";
  5173. }
  5174. return argValue;
  5175. }).join(', ') : "") + ")";
  5176. };
  5177. module.exports = MixinCall;
  5178. },{"../functions/default.js":10,"./mixin-definition.js":49,"./node.js":51,"./selector.js":58}],49:[function(require,module,exports){
  5179. var Selector = require("./selector.js"),
  5180. Element = require("./element.js"),
  5181. Ruleset = require("./ruleset.js"),
  5182. Rule = require("./rule.js"),
  5183. Expression = require("./expression.js"),
  5184. contexts = require("../contexts.js");
  5185. var Definition = function (name, params, rules, condition, variadic, frames) {
  5186. this.name = name;
  5187. this.selectors = [new(Selector)([new(Element)(null, name, this.index, this.currentFileInfo)])];
  5188. this.params = params;
  5189. this.condition = condition;
  5190. this.variadic = variadic;
  5191. this.arity = params.length;
  5192. this.rules = rules;
  5193. this._lookups = {};
  5194. this.required = params.reduce(function (count, p) {
  5195. if (!p.name || (p.name && !p.value)) { return count + 1; }
  5196. else { return count; }
  5197. }, 0);
  5198. this.frames = frames;
  5199. };
  5200. Definition.prototype = new Ruleset();
  5201. Definition.prototype.type = "MixinDefinition";
  5202. Definition.prototype.evalFirst = true;
  5203. Definition.prototype.accept = function (visitor) {
  5204. if (this.params && this.params.length) {
  5205. this.params = visitor.visitArray(this.params);
  5206. }
  5207. this.rules = visitor.visitArray(this.rules);
  5208. if (this.condition) {
  5209. this.condition = visitor.visit(this.condition);
  5210. }
  5211. };
  5212. Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) {
  5213. /*jshint boss:true */
  5214. var frame = new(Ruleset)(null, null),
  5215. varargs, arg,
  5216. params = this.params.slice(0),
  5217. i, j, val, name, isNamedFound, argIndex, argsLength = 0;
  5218. mixinEnv = new contexts.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
  5219. if (args) {
  5220. args = args.slice(0);
  5221. argsLength = args.length;
  5222. for(i = 0; i < argsLength; i++) {
  5223. arg = args[i];
  5224. if (name = (arg && arg.name)) {
  5225. isNamedFound = false;
  5226. for(j = 0; j < params.length; j++) {
  5227. if (!evaldArguments[j] && name === params[j].name) {
  5228. evaldArguments[j] = arg.value.eval(env);
  5229. frame.prependRule(new(Rule)(name, arg.value.eval(env)));
  5230. isNamedFound = true;
  5231. break;
  5232. }
  5233. }
  5234. if (isNamedFound) {
  5235. args.splice(i, 1);
  5236. i--;
  5237. continue;
  5238. } else {
  5239. throw { type: 'Runtime', message: "Named argument for " + this.name +
  5240. ' ' + args[i].name + ' not found' };
  5241. }
  5242. }
  5243. }
  5244. }
  5245. argIndex = 0;
  5246. for (i = 0; i < params.length; i++) {
  5247. if (evaldArguments[i]) { continue; }
  5248. arg = args && args[argIndex];
  5249. if (name = params[i].name) {
  5250. if (params[i].variadic) {
  5251. varargs = [];
  5252. for (j = argIndex; j < argsLength; j++) {
  5253. varargs.push(args[j].value.eval(env));
  5254. }
  5255. frame.prependRule(new(Rule)(name, new(Expression)(varargs).eval(env)));
  5256. } else {
  5257. val = arg && arg.value;
  5258. if (val) {
  5259. val = val.eval(env);
  5260. } else if (params[i].value) {
  5261. val = params[i].value.eval(mixinEnv);
  5262. frame.resetCache();
  5263. } else {
  5264. throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
  5265. ' (' + argsLength + ' for ' + this.arity + ')' };
  5266. }
  5267. frame.prependRule(new(Rule)(name, val));
  5268. evaldArguments[i] = val;
  5269. }
  5270. }
  5271. if (params[i].variadic && args) {
  5272. for (j = argIndex; j < argsLength; j++) {
  5273. evaldArguments[j] = args[j].value.eval(env);
  5274. }
  5275. }
  5276. argIndex++;
  5277. }
  5278. return frame;
  5279. };
  5280. Definition.prototype.eval = function (env) {
  5281. return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0));
  5282. };
  5283. Definition.prototype.evalCall = function (env, args, important) {
  5284. var _arguments = [],
  5285. mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames,
  5286. frame = this.evalParams(env, new(contexts.evalEnv)(env, mixinFrames), args, _arguments),
  5287. rules, ruleset;
  5288. frame.prependRule(new(Rule)('@arguments', new(Expression)(_arguments).eval(env)));
  5289. rules = this.rules.slice(0);
  5290. ruleset = new(Ruleset)(null, rules);
  5291. ruleset.originalRuleset = this;
  5292. ruleset = ruleset.eval(new(contexts.evalEnv)(env, [this, frame].concat(mixinFrames)));
  5293. if (important) {
  5294. ruleset = this.makeImportant.apply(ruleset);
  5295. }
  5296. return ruleset;
  5297. };
  5298. Definition.prototype.matchCondition = function (args, env) {
  5299. if (this.condition && !this.condition.eval(
  5300. new(contexts.evalEnv)(env,
  5301. [this.evalParams(env, new(contexts.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables
  5302. .concat(this.frames) // the parent namespace/mixin frames
  5303. .concat(env.frames)))) { // the current environment frames
  5304. return false;
  5305. }
  5306. return true;
  5307. };
  5308. Definition.prototype.matchArgs = function (args, env) {
  5309. var argsLength = (args && args.length) || 0, len;
  5310. if (! this.variadic) {
  5311. if (argsLength < this.required) { return false; }
  5312. if (argsLength > this.params.length) { return false; }
  5313. } else {
  5314. if (argsLength < (this.required - 1)) { return false; }
  5315. }
  5316. len = Math.min(argsLength, this.arity);
  5317. for (var i = 0; i < len; i++) {
  5318. if (!this.params[i].name && !this.params[i].variadic) {
  5319. if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
  5320. return false;
  5321. }
  5322. }
  5323. }
  5324. return true;
  5325. };
  5326. module.exports = Definition;
  5327. },{"../contexts.js":2,"./element.js":39,"./expression.js":40,"./rule.js":55,"./ruleset.js":57,"./selector.js":58}],50:[function(require,module,exports){
  5328. var Node = require("./node.js"),
  5329. Operation = require("./operation.js"),
  5330. Dimension = require("./dimension.js");
  5331. var Negative = function (node) {
  5332. this.value = node;
  5333. };
  5334. Negative.prototype = new Node();
  5335. Negative.prototype.type = "Negative";
  5336. Negative.prototype.genCSS = function (env, output) {
  5337. output.add('-');
  5338. this.value.genCSS(env, output);
  5339. };
  5340. Negative.prototype.eval = function (env) {
  5341. if (env.isMathOn()) {
  5342. return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(env);
  5343. }
  5344. return new(Negative)(this.value.eval(env));
  5345. };
  5346. module.exports = Negative;
  5347. },{"./dimension.js":37,"./node.js":51,"./operation.js":52}],51:[function(require,module,exports){
  5348. var Node = function() {
  5349. };
  5350. Node.prototype.toCSS = function (env) {
  5351. var strs = [];
  5352. this.genCSS(env, {
  5353. add: function(chunk, fileInfo, index) {
  5354. strs.push(chunk);
  5355. },
  5356. isEmpty: function () {
  5357. return strs.length === 0;
  5358. }
  5359. });
  5360. return strs.join('');
  5361. };
  5362. Node.prototype.genCSS = function (env, output) {
  5363. output.add(this.value);
  5364. };
  5365. Node.prototype.accept = function (visitor) {
  5366. this.value = visitor.visit(this.value);
  5367. };
  5368. Node.prototype.eval = function () { return this; };
  5369. Node.prototype._operate = function (env, op, a, b) {
  5370. switch (op) {
  5371. case '+': return a + b;
  5372. case '-': return a - b;
  5373. case '*': return a * b;
  5374. case '/': return a / b;
  5375. }
  5376. };
  5377. Node.prototype.fround = function(env, value) {
  5378. var precision = env && env.numPrecision;
  5379. //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
  5380. return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
  5381. };
  5382. module.exports = Node;
  5383. },{}],52:[function(require,module,exports){
  5384. var Node = require("./node.js"),
  5385. Color = require("./color.js"),
  5386. Dimension = require("./dimension.js");
  5387. var Operation = function (op, operands, isSpaced) {
  5388. this.op = op.trim();
  5389. this.operands = operands;
  5390. this.isSpaced = isSpaced;
  5391. };
  5392. Operation.prototype = new Node();
  5393. Operation.prototype.type = "Operation";
  5394. Operation.prototype.accept = function (visitor) {
  5395. this.operands = visitor.visit(this.operands);
  5396. };
  5397. Operation.prototype.eval = function (env) {
  5398. var a = this.operands[0].eval(env),
  5399. b = this.operands[1].eval(env);
  5400. if (env.isMathOn()) {
  5401. if (a instanceof Dimension && b instanceof Color) {
  5402. a = a.toColor();
  5403. }
  5404. if (b instanceof Dimension && a instanceof Color) {
  5405. b = b.toColor();
  5406. }
  5407. if (!a.operate) {
  5408. throw { type: "Operation",
  5409. message: "Operation on an invalid type" };
  5410. }
  5411. return a.operate(env, this.op, b);
  5412. } else {
  5413. return new(Operation)(this.op, [a, b], this.isSpaced);
  5414. }
  5415. };
  5416. Operation.prototype.genCSS = function (env, output) {
  5417. this.operands[0].genCSS(env, output);
  5418. if (this.isSpaced) {
  5419. output.add(" ");
  5420. }
  5421. output.add(this.op);
  5422. if (this.isSpaced) {
  5423. output.add(" ");
  5424. }
  5425. this.operands[1].genCSS(env, output);
  5426. };
  5427. module.exports = Operation;
  5428. },{"./color.js":31,"./dimension.js":37,"./node.js":51}],53:[function(require,module,exports){
  5429. var Node = require("./node.js");
  5430. var Paren = function (node) {
  5431. this.value = node;
  5432. };
  5433. Paren.prototype = new Node();
  5434. Paren.prototype.type = "Paren";
  5435. Paren.prototype.genCSS = function (env, output) {
  5436. output.add('(');
  5437. this.value.genCSS(env, output);
  5438. output.add(')');
  5439. };
  5440. Paren.prototype.eval = function (env) {
  5441. return new(Paren)(this.value.eval(env));
  5442. };
  5443. module.exports = Paren;
  5444. },{"./node.js":51}],54:[function(require,module,exports){
  5445. var JsEvalNode = require("./js-eval-node.js"),
  5446. Variable = require("./variable.js");
  5447. var Quoted = function (str, content, escaped, index, currentFileInfo) {
  5448. this.escaped = escaped;
  5449. this.value = content || '';
  5450. this.quote = str.charAt(0);
  5451. this.index = index;
  5452. this.currentFileInfo = currentFileInfo;
  5453. };
  5454. Quoted.prototype = new JsEvalNode();
  5455. Quoted.prototype.type = "Quoted";
  5456. Quoted.prototype.genCSS = function (env, output) {
  5457. if (!this.escaped) {
  5458. output.add(this.quote, this.currentFileInfo, this.index);
  5459. }
  5460. output.add(this.value);
  5461. if (!this.escaped) {
  5462. output.add(this.quote);
  5463. }
  5464. };
  5465. Quoted.prototype.eval = function (env) {
  5466. var that = this;
  5467. var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
  5468. return String(that.evaluateJavaScript(exp, env));
  5469. }).replace(/@\{([\w-]+)\}/g, function (_, name) {
  5470. var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
  5471. return (v instanceof Quoted) ? v.value : v.toCSS();
  5472. });
  5473. return new(Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
  5474. };
  5475. Quoted.prototype.compare = function (x) {
  5476. if (!x.toCSS) {
  5477. return -1;
  5478. }
  5479. var left, right;
  5480. // when comparing quoted strings allow the quote to differ
  5481. if (x.type === "Quoted" && !this.escaped && !x.escaped) {
  5482. left = x.value;
  5483. right = this.value;
  5484. } else {
  5485. left = this.toCSS();
  5486. right = x.toCSS();
  5487. }
  5488. if (left === right) {
  5489. return 0;
  5490. }
  5491. return left < right ? -1 : 1;
  5492. };
  5493. module.exports = Quoted;
  5494. },{"./js-eval-node.js":45,"./variable.js":63}],55:[function(require,module,exports){
  5495. var Node = require("./node.js"),
  5496. Value = require("./value.js"),
  5497. Keyword = require("./keyword.js");
  5498. var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
  5499. this.name = name;
  5500. this.value = (value instanceof Node) ? value : new(Value)([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
  5501. this.important = important ? ' ' + important.trim() : '';
  5502. this.merge = merge;
  5503. this.index = index;
  5504. this.currentFileInfo = currentFileInfo;
  5505. this.inline = inline || false;
  5506. this.variable = (variable !== undefined) ? variable
  5507. : (name.charAt && (name.charAt(0) === '@'));
  5508. };
  5509. function evalName(env, name) {
  5510. var value = "", i, n = name.length,
  5511. output = {add: function (s) {value += s;}};
  5512. for (i = 0; i < n; i++) {
  5513. name[i].eval(env).genCSS(env, output);
  5514. }
  5515. return value;
  5516. }
  5517. Rule.prototype = new Node();
  5518. Rule.prototype.type = "Rule";
  5519. Rule.prototype.genCSS = function (env, output) {
  5520. output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index);
  5521. try {
  5522. this.value.genCSS(env, output);
  5523. }
  5524. catch(e) {
  5525. e.index = this.index;
  5526. e.filename = this.currentFileInfo.filename;
  5527. throw e;
  5528. }
  5529. output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index);
  5530. };
  5531. Rule.prototype.eval = function (env) {
  5532. var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable;
  5533. if (typeof name !== "string") {
  5534. // expand 'primitive' name directly to get
  5535. // things faster (~10% for benchmark.less):
  5536. name = (name.length === 1)
  5537. && (name[0] instanceof Keyword)
  5538. ? name[0].value : evalName(env, name);
  5539. variable = false; // never treat expanded interpolation as new variable name
  5540. }
  5541. if (name === "font" && !env.strictMath) {
  5542. strictMathBypass = true;
  5543. env.strictMath = true;
  5544. }
  5545. try {
  5546. evaldValue = this.value.eval(env);
  5547. if (!this.variable && evaldValue.type === "DetachedRuleset") {
  5548. throw { message: "Rulesets cannot be evaluated on a property.",
  5549. index: this.index, filename: this.currentFileInfo.filename };
  5550. }
  5551. return new(Rule)(name,
  5552. evaldValue,
  5553. this.important,
  5554. this.merge,
  5555. this.index, this.currentFileInfo, this.inline,
  5556. variable);
  5557. }
  5558. catch(e) {
  5559. if (typeof e.index !== 'number') {
  5560. e.index = this.index;
  5561. e.filename = this.currentFileInfo.filename;
  5562. }
  5563. throw e;
  5564. }
  5565. finally {
  5566. if (strictMathBypass) {
  5567. env.strictMath = false;
  5568. }
  5569. }
  5570. };
  5571. Rule.prototype.makeImportant = function () {
  5572. return new(Rule)(this.name,
  5573. this.value,
  5574. "!important",
  5575. this.merge,
  5576. this.index, this.currentFileInfo, this.inline);
  5577. };
  5578. module.exports = Rule;
  5579. },{"./keyword.js":46,"./node.js":51,"./value.js":62}],56:[function(require,module,exports){
  5580. var Node = require("./node.js"),
  5581. Variable = require("./variable.js");
  5582. var RulesetCall = function (variable) {
  5583. this.variable = variable;
  5584. };
  5585. RulesetCall.prototype = new Node();
  5586. RulesetCall.prototype.type = "RulesetCall";
  5587. RulesetCall.prototype.eval = function (env) {
  5588. var detachedRuleset = new(Variable)(this.variable).eval(env);
  5589. return detachedRuleset.callEval(env);
  5590. };
  5591. module.exports = RulesetCall;
  5592. },{"./node.js":51,"./variable.js":63}],57:[function(require,module,exports){
  5593. var Node = require("./node.js"),
  5594. Rule = require("./rule.js"),
  5595. Selector = require("./selector.js"),
  5596. Element = require("./element.js"),
  5597. contexts = require("../contexts.js"),
  5598. defaultFunc = require("../functions/default.js"),
  5599. getDebugInfo = require("./debug-info.js");
  5600. var Ruleset = function (selectors, rules, strictImports) {
  5601. this.selectors = selectors;
  5602. this.rules = rules;
  5603. this._lookups = {};
  5604. this.strictImports = strictImports;
  5605. };
  5606. Ruleset.prototype = new Node();
  5607. Ruleset.prototype.type = "Ruleset";
  5608. Ruleset.prototype.isRuleset = true;
  5609. Ruleset.prototype.isRulesetLike = true;
  5610. Ruleset.prototype.accept = function (visitor) {
  5611. if (this.paths) {
  5612. visitor.visitArray(this.paths, true);
  5613. } else if (this.selectors) {
  5614. this.selectors = visitor.visitArray(this.selectors);
  5615. }
  5616. if (this.rules && this.rules.length) {
  5617. this.rules = visitor.visitArray(this.rules);
  5618. }
  5619. };
  5620. Ruleset.prototype.eval = function (env) {
  5621. var thisSelectors = this.selectors, selectors,
  5622. selCnt, selector, i, hasOnePassingSelector = false;
  5623. if (thisSelectors && (selCnt = thisSelectors.length)) {
  5624. selectors = [];
  5625. defaultFunc.error({
  5626. type: "Syntax",
  5627. message: "it is currently only allowed in parametric mixin guards,"
  5628. });
  5629. for (i = 0; i < selCnt; i++) {
  5630. selector = thisSelectors[i].eval(env);
  5631. selectors.push(selector);
  5632. if (selector.evaldCondition) {
  5633. hasOnePassingSelector = true;
  5634. }
  5635. }
  5636. defaultFunc.reset();
  5637. } else {
  5638. hasOnePassingSelector = true;
  5639. }
  5640. var rules = this.rules ? this.rules.slice(0) : null,
  5641. ruleset = new(Ruleset)(selectors, rules, this.strictImports),
  5642. rule, subRule;
  5643. ruleset.originalRuleset = this;
  5644. ruleset.root = this.root;
  5645. ruleset.firstRoot = this.firstRoot;
  5646. ruleset.allowImports = this.allowImports;
  5647. if(this.debugInfo) {
  5648. ruleset.debugInfo = this.debugInfo;
  5649. }
  5650. if (!hasOnePassingSelector) {
  5651. rules.length = 0;
  5652. }
  5653. // push the current ruleset to the frames stack
  5654. var envFrames = env.frames;
  5655. envFrames.unshift(ruleset);
  5656. // currrent selectors
  5657. var envSelectors = env.selectors;
  5658. if (!envSelectors) {
  5659. env.selectors = envSelectors = [];
  5660. }
  5661. envSelectors.unshift(this.selectors);
  5662. // Evaluate imports
  5663. if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
  5664. ruleset.evalImports(env);
  5665. }
  5666. // Store the frames around mixin definitions,
  5667. // so they can be evaluated like closures when the time comes.
  5668. var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
  5669. for (i = 0; i < rsRuleCnt; i++) {
  5670. if (rsRules[i].evalFirst) {
  5671. rsRules[i] = rsRules[i].eval(env);
  5672. }
  5673. }
  5674. var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
  5675. // Evaluate mixin calls.
  5676. for (i = 0; i < rsRuleCnt; i++) {
  5677. if (rsRules[i].type === "MixinCall") {
  5678. /*jshint loopfunc:true */
  5679. rules = rsRules[i].eval(env).filter(function(r) {
  5680. if ((r instanceof Rule) && r.variable) {
  5681. // do not pollute the scope if the variable is
  5682. // already there. consider returning false here
  5683. // but we need a way to "return" variable from mixins
  5684. return !(ruleset.variable(r.name));
  5685. }
  5686. return true;
  5687. });
  5688. rsRules.splice.apply(rsRules, [i, 1].concat(rules));
  5689. rsRuleCnt += rules.length - 1;
  5690. i += rules.length-1;
  5691. ruleset.resetCache();
  5692. } else if (rsRules[i].type === "RulesetCall") {
  5693. /*jshint loopfunc:true */
  5694. rules = rsRules[i].eval(env).rules.filter(function(r) {
  5695. if ((r instanceof Rule) && r.variable) {
  5696. // do not pollute the scope at all
  5697. return false;
  5698. }
  5699. return true;
  5700. });
  5701. rsRules.splice.apply(rsRules, [i, 1].concat(rules));
  5702. rsRuleCnt += rules.length - 1;
  5703. i += rules.length-1;
  5704. ruleset.resetCache();
  5705. }
  5706. }
  5707. // Evaluate everything else
  5708. for (i = 0; i < rsRules.length; i++) {
  5709. rule = rsRules[i];
  5710. if (!rule.evalFirst) {
  5711. rsRules[i] = rule = rule.eval ? rule.eval(env) : rule;
  5712. }
  5713. }
  5714. // Evaluate everything else
  5715. for (i = 0; i < rsRules.length; i++) {
  5716. rule = rsRules[i];
  5717. // for rulesets, check if it is a css guard and can be removed
  5718. if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
  5719. // check if it can be folded in (e.g. & where)
  5720. if (rule.selectors[0].isJustParentSelector()) {
  5721. rsRules.splice(i--, 1);
  5722. for(var j = 0; j < rule.rules.length; j++) {
  5723. subRule = rule.rules[j];
  5724. if (!(subRule instanceof Rule) || !subRule.variable) {
  5725. rsRules.splice(++i, 0, subRule);
  5726. }
  5727. }
  5728. }
  5729. }
  5730. }
  5731. // Pop the stack
  5732. envFrames.shift();
  5733. envSelectors.shift();
  5734. if (env.mediaBlocks) {
  5735. for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
  5736. env.mediaBlocks[i].bubbleSelectors(selectors);
  5737. }
  5738. }
  5739. return ruleset;
  5740. };
  5741. Ruleset.prototype.evalImports = function(env) {
  5742. var rules = this.rules, i, importRules;
  5743. if (!rules) { return; }
  5744. for (i = 0; i < rules.length; i++) {
  5745. if (rules[i].type === "Import") {
  5746. importRules = rules[i].eval(env);
  5747. if (importRules && importRules.length) {
  5748. rules.splice.apply(rules, [i, 1].concat(importRules));
  5749. i+= importRules.length-1;
  5750. } else {
  5751. rules.splice(i, 1, importRules);
  5752. }
  5753. this.resetCache();
  5754. }
  5755. }
  5756. };
  5757. Ruleset.prototype.makeImportant = function() {
  5758. return new Ruleset(this.selectors, this.rules.map(function (r) {
  5759. if (r.makeImportant) {
  5760. return r.makeImportant();
  5761. } else {
  5762. return r;
  5763. }
  5764. }), this.strictImports);
  5765. };
  5766. Ruleset.prototype.matchArgs = function (args) {
  5767. return !args || args.length === 0;
  5768. };
  5769. // lets you call a css selector with a guard
  5770. Ruleset.prototype.matchCondition = function (args, env) {
  5771. var lastSelector = this.selectors[this.selectors.length-1];
  5772. if (!lastSelector.evaldCondition) {
  5773. return false;
  5774. }
  5775. if (lastSelector.condition &&
  5776. !lastSelector.condition.eval(
  5777. new(contexts.evalEnv)(env,
  5778. env.frames))) {
  5779. return false;
  5780. }
  5781. return true;
  5782. };
  5783. Ruleset.prototype.resetCache = function () {
  5784. this._rulesets = null;
  5785. this._variables = null;
  5786. this._lookups = {};
  5787. };
  5788. Ruleset.prototype.variables = function () {
  5789. if (!this._variables) {
  5790. this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
  5791. if (r instanceof Rule && r.variable === true) {
  5792. hash[r.name] = r;
  5793. }
  5794. return hash;
  5795. }, {});
  5796. }
  5797. return this._variables;
  5798. };
  5799. Ruleset.prototype.variable = function (name) {
  5800. return this.variables()[name];
  5801. };
  5802. Ruleset.prototype.rulesets = function () {
  5803. if (!this.rules) { return null; }
  5804. var filtRules = [], rules = this.rules, cnt = rules.length,
  5805. i, rule;
  5806. for (i = 0; i < cnt; i++) {
  5807. rule = rules[i];
  5808. if (rule.isRuleset) {
  5809. filtRules.push(rule);
  5810. }
  5811. }
  5812. return filtRules;
  5813. };
  5814. Ruleset.prototype.prependRule = function (rule) {
  5815. var rules = this.rules;
  5816. if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; }
  5817. };
  5818. Ruleset.prototype.find = function (selector, self) {
  5819. self = self || this;
  5820. var rules = [], match,
  5821. key = selector.toCSS();
  5822. if (key in this._lookups) { return this._lookups[key]; }
  5823. this.rulesets().forEach(function (rule) {
  5824. if (rule !== self) {
  5825. for (var j = 0; j < rule.selectors.length; j++) {
  5826. match = selector.match(rule.selectors[j]);
  5827. if (match) {
  5828. if (selector.elements.length > match) {
  5829. Array.prototype.push.apply(rules, rule.find(
  5830. new(Selector)(selector.elements.slice(match)), self));
  5831. } else {
  5832. rules.push(rule);
  5833. }
  5834. break;
  5835. }
  5836. }
  5837. }
  5838. });
  5839. this._lookups[key] = rules;
  5840. return rules;
  5841. };
  5842. Ruleset.prototype.genCSS = function (env, output) {
  5843. var i, j,
  5844. charsetRuleNodes = [],
  5845. ruleNodes = [],
  5846. rulesetNodes = [],
  5847. rulesetNodeCnt,
  5848. debugInfo, // Line number debugging
  5849. rule,
  5850. path;
  5851. env.tabLevel = (env.tabLevel || 0);
  5852. if (!this.root) {
  5853. env.tabLevel++;
  5854. }
  5855. var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "),
  5856. tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "),
  5857. sep;
  5858. function isRulesetLikeNode(rule, root) {
  5859. // if it has nested rules, then it should be treated like a ruleset
  5860. // medias and comments do not have nested rules, but should be treated like rulesets anyway
  5861. // some directives and anonymous nodes are ruleset like, others are not
  5862. if (typeof rule.isRulesetLike === "boolean")
  5863. {
  5864. return rule.isRulesetLike;
  5865. } else if (typeof rule.isRulesetLike === "function")
  5866. {
  5867. return rule.isRulesetLike(root);
  5868. }
  5869. //anything else is assumed to be a rule
  5870. return false;
  5871. }
  5872. for (i = 0; i < this.rules.length; i++) {
  5873. rule = this.rules[i];
  5874. if (isRulesetLikeNode(rule, this.root)) {
  5875. rulesetNodes.push(rule);
  5876. } else {
  5877. //charsets should float on top of everything
  5878. if (rule.isCharset && rule.isCharset()) {
  5879. charsetRuleNodes.push(rule);
  5880. } else {
  5881. ruleNodes.push(rule);
  5882. }
  5883. }
  5884. }
  5885. ruleNodes = charsetRuleNodes.concat(ruleNodes);
  5886. // If this is the root node, we don't render
  5887. // a selector, or {}.
  5888. if (!this.root) {
  5889. debugInfo = getDebugInfo(env, this, tabSetStr);
  5890. if (debugInfo) {
  5891. output.add(debugInfo);
  5892. output.add(tabSetStr);
  5893. }
  5894. var paths = this.paths, pathCnt = paths.length,
  5895. pathSubCnt;
  5896. sep = env.compress ? ',' : (',\n' + tabSetStr);
  5897. for (i = 0; i < pathCnt; i++) {
  5898. path = paths[i];
  5899. if (!(pathSubCnt = path.length)) { continue; }
  5900. if (i > 0) { output.add(sep); }
  5901. env.firstSelector = true;
  5902. path[0].genCSS(env, output);
  5903. env.firstSelector = false;
  5904. for (j = 1; j < pathSubCnt; j++) {
  5905. path[j].genCSS(env, output);
  5906. }
  5907. }
  5908. output.add((env.compress ? '{' : ' {\n') + tabRuleStr);
  5909. }
  5910. // Compile rules and rulesets
  5911. for (i = 0; i < ruleNodes.length; i++) {
  5912. rule = ruleNodes[i];
  5913. // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
  5914. // In this instance we do not know whether it is the last property
  5915. if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) {
  5916. env.lastRule = true;
  5917. }
  5918. if (rule.genCSS) {
  5919. rule.genCSS(env, output);
  5920. } else if (rule.value) {
  5921. output.add(rule.value.toString());
  5922. }
  5923. if (!env.lastRule) {
  5924. output.add(env.compress ? '' : ('\n' + tabRuleStr));
  5925. } else {
  5926. env.lastRule = false;
  5927. }
  5928. }
  5929. if (!this.root) {
  5930. output.add((env.compress ? '}' : '\n' + tabSetStr + '}'));
  5931. env.tabLevel--;
  5932. }
  5933. sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr);
  5934. rulesetNodeCnt = rulesetNodes.length;
  5935. if (rulesetNodeCnt) {
  5936. if (ruleNodes.length && sep) { output.add(sep); }
  5937. rulesetNodes[0].genCSS(env, output);
  5938. for (i = 1; i < rulesetNodeCnt; i++) {
  5939. if (sep) { output.add(sep); }
  5940. rulesetNodes[i].genCSS(env, output);
  5941. }
  5942. }
  5943. if (!output.isEmpty() && !env.compress && this.firstRoot) {
  5944. output.add('\n');
  5945. }
  5946. };
  5947. Ruleset.prototype.markReferenced = function () {
  5948. if (!this.selectors) {
  5949. return;
  5950. }
  5951. for (var s = 0; s < this.selectors.length; s++) {
  5952. this.selectors[s].markReferenced();
  5953. }
  5954. };
  5955. Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
  5956. for (var s = 0; s < selectors.length; s++) {
  5957. this.joinSelector(paths, context, selectors[s]);
  5958. }
  5959. };
  5960. Ruleset.prototype.joinSelector = function (paths, context, selector) {
  5961. var i, j, k,
  5962. hasParentSelector, newSelectors, el, sel, parentSel,
  5963. newSelectorPath, afterParentJoin, newJoinedSelector,
  5964. newJoinedSelectorEmpty, lastSelector, currentElements,
  5965. selectorsMultiplied;
  5966. for (i = 0; i < selector.elements.length; i++) {
  5967. el = selector.elements[i];
  5968. if (el.value === '&') {
  5969. hasParentSelector = true;
  5970. }
  5971. }
  5972. if (!hasParentSelector) {
  5973. if (context.length > 0) {
  5974. for (i = 0; i < context.length; i++) {
  5975. paths.push(context[i].concat(selector));
  5976. }
  5977. }
  5978. else {
  5979. paths.push([selector]);
  5980. }
  5981. return;
  5982. }
  5983. // The paths are [[Selector]]
  5984. // The first list is a list of comma seperated selectors
  5985. // The inner list is a list of inheritance seperated selectors
  5986. // e.g.
  5987. // .a, .b {
  5988. // .c {
  5989. // }
  5990. // }
  5991. // == [[.a] [.c]] [[.b] [.c]]
  5992. //
  5993. // the elements from the current selector so far
  5994. currentElements = [];
  5995. // the current list of new selectors to add to the path.
  5996. // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
  5997. // by the parents
  5998. newSelectors = [[]];
  5999. for (i = 0; i < selector.elements.length; i++) {
  6000. el = selector.elements[i];
  6001. // non parent reference elements just get added
  6002. if (el.value !== "&") {
  6003. currentElements.push(el);
  6004. } else {
  6005. // the new list of selectors to add
  6006. selectorsMultiplied = [];
  6007. // merge the current list of non parent selector elements
  6008. // on to the current list of selectors to add
  6009. if (currentElements.length > 0) {
  6010. this.mergeElementsOnToSelectors(currentElements, newSelectors);
  6011. }
  6012. // loop through our current selectors
  6013. for (j = 0; j < newSelectors.length; j++) {
  6014. sel = newSelectors[j];
  6015. // if we don't have any parent paths, the & might be in a mixin so that it can be used
  6016. // whether there are parents or not
  6017. if (context.length === 0) {
  6018. // the combinator used on el should now be applied to the next element instead so that
  6019. // it is not lost
  6020. if (sel.length > 0) {
  6021. sel[0].elements = sel[0].elements.slice(0);
  6022. sel[0].elements.push(new(Element)(el.combinator, '', el.index, el.currentFileInfo));
  6023. }
  6024. selectorsMultiplied.push(sel);
  6025. }
  6026. else {
  6027. // and the parent selectors
  6028. for (k = 0; k < context.length; k++) {
  6029. parentSel = context[k];
  6030. // We need to put the current selectors
  6031. // then join the last selector's elements on to the parents selectors
  6032. // our new selector path
  6033. newSelectorPath = [];
  6034. // selectors from the parent after the join
  6035. afterParentJoin = [];
  6036. newJoinedSelectorEmpty = true;
  6037. //construct the joined selector - if & is the first thing this will be empty,
  6038. // if not newJoinedSelector will be the last set of elements in the selector
  6039. if (sel.length > 0) {
  6040. newSelectorPath = sel.slice(0);
  6041. lastSelector = newSelectorPath.pop();
  6042. newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0));
  6043. newJoinedSelectorEmpty = false;
  6044. }
  6045. else {
  6046. newJoinedSelector = selector.createDerived([]);
  6047. }
  6048. //put together the parent selectors after the join
  6049. if (parentSel.length > 1) {
  6050. afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
  6051. }
  6052. if (parentSel.length > 0) {
  6053. newJoinedSelectorEmpty = false;
  6054. // join the elements so far with the first part of the parent
  6055. newJoinedSelector.elements.push(new(Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo));
  6056. newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
  6057. }
  6058. if (!newJoinedSelectorEmpty) {
  6059. // now add the joined selector
  6060. newSelectorPath.push(newJoinedSelector);
  6061. }
  6062. // and the rest of the parent
  6063. newSelectorPath = newSelectorPath.concat(afterParentJoin);
  6064. // add that to our new set of selectors
  6065. selectorsMultiplied.push(newSelectorPath);
  6066. }
  6067. }
  6068. }
  6069. // our new selectors has been multiplied, so reset the state
  6070. newSelectors = selectorsMultiplied;
  6071. currentElements = [];
  6072. }
  6073. }
  6074. // if we have any elements left over (e.g. .a& .b == .b)
  6075. // add them on to all the current selectors
  6076. if (currentElements.length > 0) {
  6077. this.mergeElementsOnToSelectors(currentElements, newSelectors);
  6078. }
  6079. for (i = 0; i < newSelectors.length; i++) {
  6080. if (newSelectors[i].length > 0) {
  6081. paths.push(newSelectors[i]);
  6082. }
  6083. }
  6084. };
  6085. Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) {
  6086. var i, sel;
  6087. if (selectors.length === 0) {
  6088. selectors.push([ new(Selector)(elements) ]);
  6089. return;
  6090. }
  6091. for (i = 0; i < selectors.length; i++) {
  6092. sel = selectors[i];
  6093. // if the previous thing in sel is a parent this needs to join on to it
  6094. if (sel.length > 0) {
  6095. sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
  6096. }
  6097. else {
  6098. sel.push(new(Selector)(elements));
  6099. }
  6100. }
  6101. };
  6102. module.exports = Ruleset;
  6103. },{"../contexts.js":2,"../functions/default.js":10,"./debug-info.js":35,"./element.js":39,"./node.js":51,"./rule.js":55,"./selector.js":58}],58:[function(require,module,exports){
  6104. var Node = require("./node.js");
  6105. var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) {
  6106. this.elements = elements;
  6107. this.extendList = extendList;
  6108. this.condition = condition;
  6109. this.currentFileInfo = currentFileInfo || {};
  6110. this.isReferenced = isReferenced;
  6111. if (!condition) {
  6112. this.evaldCondition = true;
  6113. }
  6114. };
  6115. Selector.prototype = new Node();
  6116. Selector.prototype.type = "Selector";
  6117. Selector.prototype.accept = function (visitor) {
  6118. if (this.elements) {
  6119. this.elements = visitor.visitArray(this.elements);
  6120. }
  6121. if (this.extendList) {
  6122. this.extendList = visitor.visitArray(this.extendList);
  6123. }
  6124. if (this.condition) {
  6125. this.condition = visitor.visit(this.condition);
  6126. }
  6127. };
  6128. Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
  6129. evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
  6130. var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
  6131. newSelector.evaldCondition = evaldCondition;
  6132. newSelector.mediaEmpty = this.mediaEmpty;
  6133. return newSelector;
  6134. };
  6135. Selector.prototype.match = function (other) {
  6136. var elements = this.elements,
  6137. len = elements.length,
  6138. olen, i;
  6139. other.CacheElements();
  6140. olen = other._elements.length;
  6141. if (olen === 0 || len < olen) {
  6142. return 0;
  6143. } else {
  6144. for (i = 0; i < olen; i++) {
  6145. if (elements[i].value !== other._elements[i]) {
  6146. return 0;
  6147. }
  6148. }
  6149. }
  6150. return olen; // return number of matched elements
  6151. };
  6152. Selector.prototype.CacheElements = function(){
  6153. var css = '', len, v, i;
  6154. if( !this._elements ){
  6155. len = this.elements.length;
  6156. for(i = 0; i < len; i++){
  6157. v = this.elements[i];
  6158. css += v.combinator.value;
  6159. if( !v.value.value ){
  6160. css += v.value;
  6161. continue;
  6162. }
  6163. if( typeof v.value.value !== "string" ){
  6164. css = '';
  6165. break;
  6166. }
  6167. css += v.value.value;
  6168. }
  6169. this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
  6170. if (this._elements) {
  6171. if (this._elements[0] === "&") {
  6172. this._elements.shift();
  6173. }
  6174. } else {
  6175. this._elements = [];
  6176. }
  6177. }
  6178. };
  6179. Selector.prototype.isJustParentSelector = function() {
  6180. return !this.mediaEmpty &&
  6181. this.elements.length === 1 &&
  6182. this.elements[0].value === '&' &&
  6183. (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
  6184. };
  6185. Selector.prototype.eval = function (env) {
  6186. var evaldCondition = this.condition && this.condition.eval(env),
  6187. elements = this.elements, extendList = this.extendList;
  6188. elements = elements && elements.map(function (e) { return e.eval(env); });
  6189. extendList = extendList && extendList.map(function(extend) { return extend.eval(env); });
  6190. return this.createDerived(elements, extendList, evaldCondition);
  6191. };
  6192. Selector.prototype.genCSS = function (env, output) {
  6193. var i, element;
  6194. if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") {
  6195. output.add(' ', this.currentFileInfo, this.index);
  6196. }
  6197. if (!this._css) {
  6198. //TODO caching? speed comparison?
  6199. for(i = 0; i < this.elements.length; i++) {
  6200. element = this.elements[i];
  6201. element.genCSS(env, output);
  6202. }
  6203. }
  6204. };
  6205. Selector.prototype.markReferenced = function () {
  6206. this.isReferenced = true;
  6207. };
  6208. Selector.prototype.getIsReferenced = function() {
  6209. return !this.currentFileInfo.reference || this.isReferenced;
  6210. };
  6211. Selector.prototype.getIsOutput = function() {
  6212. return this.evaldCondition;
  6213. };
  6214. module.exports = Selector;
  6215. },{"./node.js":51}],59:[function(require,module,exports){
  6216. var Node = require("./node.js");
  6217. var UnicodeDescriptor = function (value) {
  6218. this.value = value;
  6219. };
  6220. UnicodeDescriptor.prototype = new Node();
  6221. UnicodeDescriptor.prototype.type = "UnicodeDescriptor";
  6222. module.exports = UnicodeDescriptor;
  6223. },{"./node.js":51}],60:[function(require,module,exports){
  6224. var Node = require("./node.js"),
  6225. unitConversions = require("../data/unit-conversions.js");
  6226. var Unit = function (numerator, denominator, backupUnit) {
  6227. this.numerator = numerator ? numerator.slice(0).sort() : [];
  6228. this.denominator = denominator ? denominator.slice(0).sort() : [];
  6229. this.backupUnit = backupUnit;
  6230. };
  6231. Unit.prototype = new Node();
  6232. Unit.prototype.type = "Unit";
  6233. Unit.prototype.clone = function () {
  6234. return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
  6235. };
  6236. Unit.prototype.genCSS = function (env, output) {
  6237. if (this.numerator.length >= 1) {
  6238. output.add(this.numerator[0]);
  6239. } else
  6240. if (this.denominator.length >= 1) {
  6241. output.add(this.denominator[0]);
  6242. } else
  6243. if ((!env || !env.strictUnits) && this.backupUnit) {
  6244. output.add(this.backupUnit);
  6245. }
  6246. };
  6247. Unit.prototype.toString = function () {
  6248. var i, returnStr = this.numerator.join("*");
  6249. for (i = 0; i < this.denominator.length; i++) {
  6250. returnStr += "/" + this.denominator[i];
  6251. }
  6252. return returnStr;
  6253. };
  6254. Unit.prototype.compare = function (other) {
  6255. return this.is(other.toString()) ? 0 : -1;
  6256. };
  6257. Unit.prototype.is = function (unitString) {
  6258. return this.toString() === unitString;
  6259. };
  6260. Unit.prototype.isLength = function () {
  6261. return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
  6262. };
  6263. Unit.prototype.isEmpty = function () {
  6264. return this.numerator.length === 0 && this.denominator.length === 0;
  6265. };
  6266. Unit.prototype.isSingular = function() {
  6267. return this.numerator.length <= 1 && this.denominator.length === 0;
  6268. };
  6269. Unit.prototype.map = function(callback) {
  6270. var i;
  6271. for (i = 0; i < this.numerator.length; i++) {
  6272. this.numerator[i] = callback(this.numerator[i], false);
  6273. }
  6274. for (i = 0; i < this.denominator.length; i++) {
  6275. this.denominator[i] = callback(this.denominator[i], true);
  6276. }
  6277. };
  6278. Unit.prototype.usedUnits = function() {
  6279. var group, result = {}, mapUnit;
  6280. mapUnit = function (atomicUnit) {
  6281. /*jshint loopfunc:true */
  6282. if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
  6283. result[groupName] = atomicUnit;
  6284. }
  6285. return atomicUnit;
  6286. };
  6287. for (var groupName in unitConversions) {
  6288. if (unitConversions.hasOwnProperty(groupName)) {
  6289. group = unitConversions[groupName];
  6290. this.map(mapUnit);
  6291. }
  6292. }
  6293. return result;
  6294. };
  6295. Unit.prototype.cancel = function () {
  6296. var counter = {}, atomicUnit, i, backup;
  6297. for (i = 0; i < this.numerator.length; i++) {
  6298. atomicUnit = this.numerator[i];
  6299. if (!backup) {
  6300. backup = atomicUnit;
  6301. }
  6302. counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
  6303. }
  6304. for (i = 0; i < this.denominator.length; i++) {
  6305. atomicUnit = this.denominator[i];
  6306. if (!backup) {
  6307. backup = atomicUnit;
  6308. }
  6309. counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
  6310. }
  6311. this.numerator = [];
  6312. this.denominator = [];
  6313. for (atomicUnit in counter) {
  6314. if (counter.hasOwnProperty(atomicUnit)) {
  6315. var count = counter[atomicUnit];
  6316. if (count > 0) {
  6317. for (i = 0; i < count; i++) {
  6318. this.numerator.push(atomicUnit);
  6319. }
  6320. } else if (count < 0) {
  6321. for (i = 0; i < -count; i++) {
  6322. this.denominator.push(atomicUnit);
  6323. }
  6324. }
  6325. }
  6326. }
  6327. if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
  6328. this.backupUnit = backup;
  6329. }
  6330. this.numerator.sort();
  6331. this.denominator.sort();
  6332. };
  6333. module.exports = Unit;
  6334. },{"../data/unit-conversions.js":5,"./node.js":51}],61:[function(require,module,exports){
  6335. var Node = require("./node.js");
  6336. var URL = function (val, currentFileInfo, isEvald) {
  6337. this.value = val;
  6338. this.currentFileInfo = currentFileInfo;
  6339. this.isEvald = isEvald;
  6340. };
  6341. URL.prototype = new Node();
  6342. URL.prototype.type = "Url";
  6343. URL.prototype.accept = function (visitor) {
  6344. this.value = visitor.visit(this.value);
  6345. };
  6346. URL.prototype.genCSS = function (env, output) {
  6347. output.add("url(");
  6348. this.value.genCSS(env, output);
  6349. output.add(")");
  6350. };
  6351. URL.prototype.eval = function (ctx) {
  6352. var val = this.value.eval(ctx),
  6353. rootpath;
  6354. if (!this.isEvald) {
  6355. // Add the base path if the URL is relative
  6356. rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
  6357. if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
  6358. if (!val.quote) {
  6359. rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
  6360. }
  6361. val.value = rootpath + val.value;
  6362. }
  6363. val.value = ctx.normalizePath(val.value);
  6364. // Add url args if enabled
  6365. if (ctx.urlArgs) {
  6366. if (!val.value.match(/^\s*data:/)) {
  6367. var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
  6368. var urlArgs = delimiter + ctx.urlArgs;
  6369. if (val.value.indexOf('#') !== -1) {
  6370. val.value = val.value.replace('#', urlArgs + '#');
  6371. } else {
  6372. val.value += urlArgs;
  6373. }
  6374. }
  6375. }
  6376. }
  6377. return new(URL)(val, this.currentFileInfo, true);
  6378. };
  6379. module.exports = URL;
  6380. },{"./node.js":51}],62:[function(require,module,exports){
  6381. var Node = require("./node.js");
  6382. var Value = function (value) {
  6383. this.value = value;
  6384. };
  6385. Value.prototype = new Node();
  6386. Value.prototype.type = "Value";
  6387. Value.prototype.accept = function (visitor) {
  6388. if (this.value) {
  6389. this.value = visitor.visitArray(this.value);
  6390. }
  6391. };
  6392. Value.prototype.eval = function (env) {
  6393. if (this.value.length === 1) {
  6394. return this.value[0].eval(env);
  6395. } else {
  6396. return new(Value)(this.value.map(function (v) {
  6397. return v.eval(env);
  6398. }));
  6399. }
  6400. };
  6401. Value.prototype.genCSS = function (env, output) {
  6402. var i;
  6403. for(i = 0; i < this.value.length; i++) {
  6404. this.value[i].genCSS(env, output);
  6405. if (i+1 < this.value.length) {
  6406. output.add((env && env.compress) ? ',' : ', ');
  6407. }
  6408. }
  6409. };
  6410. module.exports = Value;
  6411. },{"./node.js":51}],63:[function(require,module,exports){
  6412. var Node = require("./node.js");
  6413. var Variable = function (name, index, currentFileInfo) {
  6414. this.name = name;
  6415. this.index = index;
  6416. this.currentFileInfo = currentFileInfo || {};
  6417. };
  6418. Variable.prototype = new Node();
  6419. Variable.prototype.type = "Variable";
  6420. Variable.prototype.eval = function (env) {
  6421. var variable, name = this.name;
  6422. if (name.indexOf('@@') === 0) {
  6423. name = '@' + new(Variable)(name.slice(1)).eval(env).value;
  6424. }
  6425. if (this.evaluating) {
  6426. throw { type: 'Name',
  6427. message: "Recursive variable definition for " + name,
  6428. filename: this.currentFileInfo.file,
  6429. index: this.index };
  6430. }
  6431. this.evaluating = true;
  6432. variable = this.find(env.frames, function (frame) {
  6433. var v = frame.variable(name);
  6434. if (v) {
  6435. return v.value.eval(env);
  6436. }
  6437. });
  6438. if (variable) {
  6439. this.evaluating = false;
  6440. return variable;
  6441. } else {
  6442. throw { type: 'Name',
  6443. message: "variable " + name + " is undefined",
  6444. filename: this.currentFileInfo.filename,
  6445. index: this.index };
  6446. }
  6447. };
  6448. Variable.prototype.find = function (obj, fun) {
  6449. for (var i = 0, r; i < obj.length; i++) {
  6450. r = fun.call(obj, obj[i]);
  6451. if (r) { return r; }
  6452. }
  6453. return null;
  6454. };
  6455. module.exports = Variable;
  6456. },{"./node.js":51}],64:[function(require,module,exports){
  6457. var tree = require("../tree/index.js"),
  6458. Visitor = require("./visitor.js");
  6459. /*jshint loopfunc:true */
  6460. var ExtendFinderVisitor = function() {
  6461. this._visitor = new Visitor(this);
  6462. this.contexts = [];
  6463. this.allExtendsStack = [[]];
  6464. };
  6465. ExtendFinderVisitor.prototype = {
  6466. run: function (root) {
  6467. root = this._visitor.visit(root);
  6468. root.allExtends = this.allExtendsStack[0];
  6469. return root;
  6470. },
  6471. visitRule: function (ruleNode, visitArgs) {
  6472. visitArgs.visitDeeper = false;
  6473. },
  6474. visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
  6475. visitArgs.visitDeeper = false;
  6476. },
  6477. visitRuleset: function (rulesetNode, visitArgs) {
  6478. if (rulesetNode.root) {
  6479. return;
  6480. }
  6481. var i, j, extend, allSelectorsExtendList = [], extendList;
  6482. // get &:extend(.a); rules which apply to all selectors in this ruleset
  6483. var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0;
  6484. for(i = 0; i < ruleCnt; i++) {
  6485. if (rulesetNode.rules[i] instanceof tree.Extend) {
  6486. allSelectorsExtendList.push(rules[i]);
  6487. rulesetNode.extendOnEveryPath = true;
  6488. }
  6489. }
  6490. // now find every selector and apply the extends that apply to all extends
  6491. // and the ones which apply to an individual extend
  6492. var paths = rulesetNode.paths;
  6493. for(i = 0; i < paths.length; i++) {
  6494. var selectorPath = paths[i],
  6495. selector = selectorPath[selectorPath.length - 1],
  6496. selExtendList = selector.extendList;
  6497. extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList)
  6498. : allSelectorsExtendList;
  6499. if (extendList) {
  6500. extendList = extendList.map(function(allSelectorsExtend) {
  6501. return allSelectorsExtend.clone();
  6502. });
  6503. }
  6504. for(j = 0; j < extendList.length; j++) {
  6505. this.foundExtends = true;
  6506. extend = extendList[j];
  6507. extend.findSelfSelectors(selectorPath);
  6508. extend.ruleset = rulesetNode;
  6509. if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
  6510. this.allExtendsStack[this.allExtendsStack.length-1].push(extend);
  6511. }
  6512. }
  6513. this.contexts.push(rulesetNode.selectors);
  6514. },
  6515. visitRulesetOut: function (rulesetNode) {
  6516. if (!rulesetNode.root) {
  6517. this.contexts.length = this.contexts.length - 1;
  6518. }
  6519. },
  6520. visitMedia: function (mediaNode, visitArgs) {
  6521. mediaNode.allExtends = [];
  6522. this.allExtendsStack.push(mediaNode.allExtends);
  6523. },
  6524. visitMediaOut: function (mediaNode) {
  6525. this.allExtendsStack.length = this.allExtendsStack.length - 1;
  6526. },
  6527. visitDirective: function (directiveNode, visitArgs) {
  6528. directiveNode.allExtends = [];
  6529. this.allExtendsStack.push(directiveNode.allExtends);
  6530. },
  6531. visitDirectiveOut: function (directiveNode) {
  6532. this.allExtendsStack.length = this.allExtendsStack.length - 1;
  6533. }
  6534. };
  6535. var ProcessExtendsVisitor = function() {
  6536. this._visitor = new Visitor(this);
  6537. };
  6538. ProcessExtendsVisitor.prototype = {
  6539. run: function(root) {
  6540. var extendFinder = new ExtendFinderVisitor();
  6541. extendFinder.run(root);
  6542. if (!extendFinder.foundExtends) { return root; }
  6543. root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
  6544. this.allExtendsStack = [root.allExtends];
  6545. return this._visitor.visit(root);
  6546. },
  6547. doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
  6548. //
  6549. // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
  6550. // the selector we would do normally, but we are also adding an extend with the same target selector
  6551. // this means this new extend can then go and alter other extends
  6552. //
  6553. // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
  6554. // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
  6555. // we look at each selector at a time, as is done in visitRuleset
  6556. var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend;
  6557. iterationCount = iterationCount || 0;
  6558. //loop through comparing every extend with every target extend.
  6559. // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
  6560. // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
  6561. // and the second is the target.
  6562. // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
  6563. // case when processing media queries
  6564. for(extendIndex = 0; extendIndex < extendsList.length; extendIndex++){
  6565. for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){
  6566. extend = extendsList[extendIndex];
  6567. targetExtend = extendsListTarget[targetExtendIndex];
  6568. // look for circular references
  6569. if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; }
  6570. // find a match in the target extends self selector (the bit before :extend)
  6571. selectorPath = [targetExtend.selfSelectors[0]];
  6572. matches = extendVisitor.findMatch(extend, selectorPath);
  6573. if (matches.length) {
  6574. // we found a match, so for each self selector..
  6575. extend.selfSelectors.forEach(function(selfSelector) {
  6576. // process the extend as usual
  6577. newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector);
  6578. // but now we create a new extend from it
  6579. newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0);
  6580. newExtend.selfSelectors = newSelector;
  6581. // add the extend onto the list of extends for that selector
  6582. newSelector[newSelector.length-1].extendList = [newExtend];
  6583. // record that we need to add it.
  6584. extendsToAdd.push(newExtend);
  6585. newExtend.ruleset = targetExtend.ruleset;
  6586. //remember its parents for circular references
  6587. newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids);
  6588. // only process the selector once.. if we have :extend(.a,.b) then multiple
  6589. // extends will look at the same selector path, so when extending
  6590. // we know that any others will be duplicates in terms of what is added to the css
  6591. if (targetExtend.firstExtendOnThisSelectorPath) {
  6592. newExtend.firstExtendOnThisSelectorPath = true;
  6593. targetExtend.ruleset.paths.push(newSelector);
  6594. }
  6595. });
  6596. }
  6597. }
  6598. }
  6599. if (extendsToAdd.length) {
  6600. // try to detect circular references to stop a stack overflow.
  6601. // may no longer be needed.
  6602. this.extendChainCount++;
  6603. if (iterationCount > 100) {
  6604. var selectorOne = "{unable to calculate}";
  6605. var selectorTwo = "{unable to calculate}";
  6606. try
  6607. {
  6608. selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
  6609. selectorTwo = extendsToAdd[0].selector.toCSS();
  6610. }
  6611. catch(e) {}
  6612. throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"};
  6613. }
  6614. // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
  6615. return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1));
  6616. } else {
  6617. return extendsToAdd;
  6618. }
  6619. },
  6620. visitRule: function (ruleNode, visitArgs) {
  6621. visitArgs.visitDeeper = false;
  6622. },
  6623. visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
  6624. visitArgs.visitDeeper = false;
  6625. },
  6626. visitSelector: function (selectorNode, visitArgs) {
  6627. visitArgs.visitDeeper = false;
  6628. },
  6629. visitRuleset: function (rulesetNode, visitArgs) {
  6630. if (rulesetNode.root) {
  6631. return;
  6632. }
  6633. var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath;
  6634. // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
  6635. for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
  6636. for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
  6637. selectorPath = rulesetNode.paths[pathIndex];
  6638. // extending extends happens initially, before the main pass
  6639. if (rulesetNode.extendOnEveryPath) { continue; }
  6640. var extendList = selectorPath[selectorPath.length-1].extendList;
  6641. if (extendList && extendList.length) { continue; }
  6642. matches = this.findMatch(allExtends[extendIndex], selectorPath);
  6643. if (matches.length) {
  6644. allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
  6645. selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
  6646. });
  6647. }
  6648. }
  6649. }
  6650. rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
  6651. },
  6652. findMatch: function (extend, haystackSelectorPath) {
  6653. //
  6654. // look through the haystack selector path to try and find the needle - extend.selector
  6655. // returns an array of selector matches that can then be replaced
  6656. //
  6657. var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement,
  6658. targetCombinator, i,
  6659. extendVisitor = this,
  6660. needleElements = extend.selector.elements,
  6661. potentialMatches = [], potentialMatch, matches = [];
  6662. // loop through the haystack elements
  6663. for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
  6664. hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
  6665. for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
  6666. haystackElement = hackstackSelector.elements[hackstackElementIndex];
  6667. // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
  6668. if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
  6669. potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
  6670. }
  6671. for(i = 0; i < potentialMatches.length; i++) {
  6672. potentialMatch = potentialMatches[i];
  6673. // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
  6674. // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
  6675. // what the resulting combinator will be
  6676. targetCombinator = haystackElement.combinator.value;
  6677. if (targetCombinator === '' && hackstackElementIndex === 0) {
  6678. targetCombinator = ' ';
  6679. }
  6680. // if we don't match, null our match to indicate failure
  6681. if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) ||
  6682. (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
  6683. potentialMatch = null;
  6684. } else {
  6685. potentialMatch.matched++;
  6686. }
  6687. // if we are still valid and have finished, test whether we have elements after and whether these are allowed
  6688. if (potentialMatch) {
  6689. potentialMatch.finished = potentialMatch.matched === needleElements.length;
  6690. if (potentialMatch.finished &&
  6691. (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) {
  6692. potentialMatch = null;
  6693. }
  6694. }
  6695. // if null we remove, if not, we are still valid, so either push as a valid match or continue
  6696. if (potentialMatch) {
  6697. if (potentialMatch.finished) {
  6698. potentialMatch.length = needleElements.length;
  6699. potentialMatch.endPathIndex = haystackSelectorIndex;
  6700. potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
  6701. potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
  6702. matches.push(potentialMatch);
  6703. }
  6704. } else {
  6705. potentialMatches.splice(i, 1);
  6706. i--;
  6707. }
  6708. }
  6709. }
  6710. }
  6711. return matches;
  6712. },
  6713. isElementValuesEqual: function(elementValue1, elementValue2) {
  6714. if (typeof elementValue1 === "string" || typeof elementValue2 === "string") {
  6715. return elementValue1 === elementValue2;
  6716. }
  6717. if (elementValue1 instanceof tree.Attribute) {
  6718. if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) {
  6719. return false;
  6720. }
  6721. if (!elementValue1.value || !elementValue2.value) {
  6722. if (elementValue1.value || elementValue2.value) {
  6723. return false;
  6724. }
  6725. return true;
  6726. }
  6727. elementValue1 = elementValue1.value.value || elementValue1.value;
  6728. elementValue2 = elementValue2.value.value || elementValue2.value;
  6729. return elementValue1 === elementValue2;
  6730. }
  6731. elementValue1 = elementValue1.value;
  6732. elementValue2 = elementValue2.value;
  6733. if (elementValue1 instanceof tree.Selector) {
  6734. if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
  6735. return false;
  6736. }
  6737. for(var i = 0; i <elementValue1.elements.length; i++) {
  6738. if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
  6739. if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
  6740. return false;
  6741. }
  6742. }
  6743. if (!this.isElementValuesEqual(elementValue1.elements[i].value, elementValue2.elements[i].value)) {
  6744. return false;
  6745. }
  6746. }
  6747. return true;
  6748. }
  6749. return false;
  6750. },
  6751. extendSelector:function (matches, selectorPath, replacementSelector) {
  6752. //for a set of matches, replace each match with the replacement selector
  6753. var currentSelectorPathIndex = 0,
  6754. currentSelectorPathElementIndex = 0,
  6755. path = [],
  6756. matchIndex,
  6757. selector,
  6758. firstElement,
  6759. match,
  6760. newElements;
  6761. for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
  6762. match = matches[matchIndex];
  6763. selector = selectorPath[match.pathIndex];
  6764. firstElement = new tree.Element(
  6765. match.initialCombinator,
  6766. replacementSelector.elements[0].value,
  6767. replacementSelector.elements[0].index,
  6768. replacementSelector.elements[0].currentFileInfo
  6769. );
  6770. if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
  6771. path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
  6772. currentSelectorPathElementIndex = 0;
  6773. currentSelectorPathIndex++;
  6774. }
  6775. newElements = selector.elements
  6776. .slice(currentSelectorPathElementIndex, match.index)
  6777. .concat([firstElement])
  6778. .concat(replacementSelector.elements.slice(1));
  6779. if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) {
  6780. path[path.length - 1].elements =
  6781. path[path.length - 1].elements.concat(newElements);
  6782. } else {
  6783. path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
  6784. path.push(new tree.Selector(
  6785. newElements
  6786. ));
  6787. }
  6788. currentSelectorPathIndex = match.endPathIndex;
  6789. currentSelectorPathElementIndex = match.endPathElementIndex;
  6790. if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) {
  6791. currentSelectorPathElementIndex = 0;
  6792. currentSelectorPathIndex++;
  6793. }
  6794. }
  6795. if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
  6796. path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
  6797. currentSelectorPathIndex++;
  6798. }
  6799. path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
  6800. return path;
  6801. },
  6802. visitRulesetOut: function (rulesetNode) {
  6803. },
  6804. visitMedia: function (mediaNode, visitArgs) {
  6805. var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
  6806. newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
  6807. this.allExtendsStack.push(newAllExtends);
  6808. },
  6809. visitMediaOut: function (mediaNode) {
  6810. this.allExtendsStack.length = this.allExtendsStack.length - 1;
  6811. },
  6812. visitDirective: function (directiveNode, visitArgs) {
  6813. var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
  6814. newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
  6815. this.allExtendsStack.push(newAllExtends);
  6816. },
  6817. visitDirectiveOut: function (directiveNode) {
  6818. this.allExtendsStack.length = this.allExtendsStack.length - 1;
  6819. }
  6820. };
  6821. module.exports = ProcessExtendsVisitor;
  6822. },{"../tree/index.js":43,"./visitor.js":69}],65:[function(require,module,exports){
  6823. var contexts = require("../contexts.js"),
  6824. Visitor = require("./visitor.js");
  6825. var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) {
  6826. this._visitor = new Visitor(this);
  6827. this._importer = importer;
  6828. this._finish = finish;
  6829. this.env = evalEnv || new contexts.evalEnv();
  6830. this.importCount = 0;
  6831. this.onceFileDetectionMap = onceFileDetectionMap || {};
  6832. this.recursionDetector = {};
  6833. if (recursionDetector) {
  6834. for(var fullFilename in recursionDetector) {
  6835. if (recursionDetector.hasOwnProperty(fullFilename)) {
  6836. this.recursionDetector[fullFilename] = true;
  6837. }
  6838. }
  6839. }
  6840. };
  6841. ImportVisitor.prototype = {
  6842. isReplacing: true,
  6843. run: function (root) {
  6844. var error;
  6845. try {
  6846. // process the contents
  6847. this._visitor.visit(root);
  6848. }
  6849. catch(e) {
  6850. error = e;
  6851. }
  6852. this.isFinished = true;
  6853. if (this.importCount === 0) {
  6854. this._finish(error);
  6855. }
  6856. },
  6857. visitImport: function (importNode, visitArgs) {
  6858. var importVisitor = this,
  6859. evaldImportNode,
  6860. inlineCSS = importNode.options.inline;
  6861. if (!importNode.css || inlineCSS) {
  6862. try {
  6863. evaldImportNode = importNode.evalForImport(this.env);
  6864. } catch(e){
  6865. if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
  6866. // attempt to eval properly and treat as css
  6867. importNode.css = true;
  6868. // if that fails, this error will be thrown
  6869. importNode.error = e;
  6870. }
  6871. if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
  6872. importNode = evaldImportNode;
  6873. this.importCount++;
  6874. var env = new contexts.evalEnv(this.env, this.env.frames.slice(0));
  6875. if (importNode.options.multiple) {
  6876. env.importMultiple = true;
  6877. }
  6878. this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) {
  6879. if (e && !e.filename) {
  6880. e.index = importNode.index; e.filename = importNode.currentFileInfo.filename;
  6881. }
  6882. var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
  6883. if (!env.importMultiple) {
  6884. if (duplicateImport) {
  6885. importNode.skip = true;
  6886. } else {
  6887. importNode.skip = function() {
  6888. if (fullPath in importVisitor.onceFileDetectionMap) {
  6889. return true;
  6890. }
  6891. importVisitor.onceFileDetectionMap[fullPath] = true;
  6892. return false;
  6893. };
  6894. }
  6895. }
  6896. var subFinish = function(e) {
  6897. importVisitor.importCount--;
  6898. if (importVisitor.importCount === 0 && importVisitor.isFinished) {
  6899. importVisitor._finish(e);
  6900. }
  6901. };
  6902. if (root) {
  6903. importNode.root = root;
  6904. importNode.importedFilename = fullPath;
  6905. if (!inlineCSS && (env.importMultiple || !duplicateImport)) {
  6906. importVisitor.recursionDetector[fullPath] = true;
  6907. new(ImportVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector)
  6908. .run(root);
  6909. return;
  6910. }
  6911. }
  6912. subFinish();
  6913. });
  6914. }
  6915. }
  6916. visitArgs.visitDeeper = false;
  6917. return importNode;
  6918. },
  6919. visitRule: function (ruleNode, visitArgs) {
  6920. visitArgs.visitDeeper = false;
  6921. return ruleNode;
  6922. },
  6923. visitDirective: function (directiveNode, visitArgs) {
  6924. this.env.frames.unshift(directiveNode);
  6925. return directiveNode;
  6926. },
  6927. visitDirectiveOut: function (directiveNode) {
  6928. this.env.frames.shift();
  6929. },
  6930. visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
  6931. this.env.frames.unshift(mixinDefinitionNode);
  6932. return mixinDefinitionNode;
  6933. },
  6934. visitMixinDefinitionOut: function (mixinDefinitionNode) {
  6935. this.env.frames.shift();
  6936. },
  6937. visitRuleset: function (rulesetNode, visitArgs) {
  6938. this.env.frames.unshift(rulesetNode);
  6939. return rulesetNode;
  6940. },
  6941. visitRulesetOut: function (rulesetNode) {
  6942. this.env.frames.shift();
  6943. },
  6944. visitMedia: function (mediaNode, visitArgs) {
  6945. this.env.frames.unshift(mediaNode.rules[0]);
  6946. return mediaNode;
  6947. },
  6948. visitMediaOut: function (mediaNode) {
  6949. this.env.frames.shift();
  6950. }
  6951. };
  6952. module.exports = ImportVisitor;
  6953. },{"../contexts.js":2,"./visitor.js":69}],66:[function(require,module,exports){
  6954. var visitors = {
  6955. Visitor: require("./visitor"),
  6956. ImportVisitor: require('./import-visitor.js'),
  6957. ExtendVisitor: require('./extend-visitor.js'),
  6958. JoinSelectorVisitor: require('./join-selector-visitor.js'),
  6959. ToCSSVisitor: require('./to-css-visitor.js')
  6960. };
  6961. module.exports = visitors;
  6962. },{"./extend-visitor.js":64,"./import-visitor.js":65,"./join-selector-visitor.js":67,"./to-css-visitor.js":68,"./visitor":69}],67:[function(require,module,exports){
  6963. var Visitor = require("./visitor.js");
  6964. var JoinSelectorVisitor = function() {
  6965. this.contexts = [[]];
  6966. this._visitor = new Visitor(this);
  6967. };
  6968. JoinSelectorVisitor.prototype = {
  6969. run: function (root) {
  6970. return this._visitor.visit(root);
  6971. },
  6972. visitRule: function (ruleNode, visitArgs) {
  6973. visitArgs.visitDeeper = false;
  6974. },
  6975. visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
  6976. visitArgs.visitDeeper = false;
  6977. },
  6978. visitRuleset: function (rulesetNode, visitArgs) {
  6979. var context = this.contexts[this.contexts.length - 1],
  6980. paths = [], selectors;
  6981. this.contexts.push(paths);
  6982. if (! rulesetNode.root) {
  6983. selectors = rulesetNode.selectors;
  6984. if (selectors) {
  6985. selectors = selectors.filter(function(selector) { return selector.getIsOutput(); });
  6986. rulesetNode.selectors = selectors.length ? selectors : (selectors = null);
  6987. if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); }
  6988. }
  6989. if (!selectors) { rulesetNode.rules = null; }
  6990. rulesetNode.paths = paths;
  6991. }
  6992. },
  6993. visitRulesetOut: function (rulesetNode) {
  6994. this.contexts.length = this.contexts.length - 1;
  6995. },
  6996. visitMedia: function (mediaNode, visitArgs) {
  6997. var context = this.contexts[this.contexts.length - 1];
  6998. mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
  6999. }
  7000. };
  7001. module.exports = JoinSelectorVisitor;
  7002. },{"./visitor.js":69}],68:[function(require,module,exports){
  7003. var tree = require("../tree/index.js"),
  7004. Visitor = require("./visitor.js");
  7005. var ToCSSVisitor = function(env) {
  7006. this._visitor = new Visitor(this);
  7007. this._env = env;
  7008. };
  7009. ToCSSVisitor.prototype = {
  7010. isReplacing: true,
  7011. run: function (root) {
  7012. return this._visitor.visit(root);
  7013. },
  7014. visitRule: function (ruleNode, visitArgs) {
  7015. if (ruleNode.variable) {
  7016. return [];
  7017. }
  7018. return ruleNode;
  7019. },
  7020. visitMixinDefinition: function (mixinNode, visitArgs) {
  7021. // mixin definitions do not get eval'd - this means they keep state
  7022. // so we have to clear that state here so it isn't used if toCSS is called twice
  7023. mixinNode.frames = [];
  7024. return [];
  7025. },
  7026. visitExtend: function (extendNode, visitArgs) {
  7027. return [];
  7028. },
  7029. visitComment: function (commentNode, visitArgs) {
  7030. if (commentNode.isSilent(this._env)) {
  7031. return [];
  7032. }
  7033. return commentNode;
  7034. },
  7035. visitMedia: function(mediaNode, visitArgs) {
  7036. mediaNode.accept(this._visitor);
  7037. visitArgs.visitDeeper = false;
  7038. if (!mediaNode.rules.length) {
  7039. return [];
  7040. }
  7041. return mediaNode;
  7042. },
  7043. visitDirective: function(directiveNode, visitArgs) {
  7044. if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) {
  7045. return [];
  7046. }
  7047. if (directiveNode.name === "@charset") {
  7048. // Only output the debug info together with subsequent @charset definitions
  7049. // a comment (or @media statement) before the actual @charset directive would
  7050. // be considered illegal css as it has to be on the first line
  7051. if (this.charset) {
  7052. if (directiveNode.debugInfo) {
  7053. var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n");
  7054. comment.debugInfo = directiveNode.debugInfo;
  7055. return this._visitor.visit(comment);
  7056. }
  7057. return [];
  7058. }
  7059. this.charset = true;
  7060. }
  7061. if (directiveNode.rules && directiveNode.rules.rules) {
  7062. this._mergeRules(directiveNode.rules.rules);
  7063. }
  7064. return directiveNode;
  7065. },
  7066. checkPropertiesInRoot: function(rules) {
  7067. var ruleNode;
  7068. for(var i = 0; i < rules.length; i++) {
  7069. ruleNode = rules[i];
  7070. if (ruleNode instanceof tree.Rule && !ruleNode.variable) {
  7071. throw { message: "properties must be inside selector blocks, they cannot be in the root.",
  7072. index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null};
  7073. }
  7074. }
  7075. },
  7076. visitRuleset: function (rulesetNode, visitArgs) {
  7077. var rule, rulesets = [];
  7078. if (rulesetNode.firstRoot) {
  7079. this.checkPropertiesInRoot(rulesetNode.rules);
  7080. }
  7081. if (! rulesetNode.root) {
  7082. if (rulesetNode.paths) {
  7083. rulesetNode.paths = rulesetNode.paths
  7084. .filter(function(p) {
  7085. var i;
  7086. if (p[0].elements[0].combinator.value === ' ') {
  7087. p[0].elements[0].combinator = new(tree.Combinator)('');
  7088. }
  7089. for(i = 0; i < p.length; i++) {
  7090. if (p[i].getIsReferenced() && p[i].getIsOutput()) {
  7091. return true;
  7092. }
  7093. }
  7094. return false;
  7095. });
  7096. }
  7097. // Compile rules and rulesets
  7098. var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0;
  7099. for (var i = 0; i < nodeRuleCnt; ) {
  7100. rule = nodeRules[i];
  7101. if (rule && rule.rules) {
  7102. // visit because we are moving them out from being a child
  7103. rulesets.push(this._visitor.visit(rule));
  7104. nodeRules.splice(i, 1);
  7105. nodeRuleCnt--;
  7106. continue;
  7107. }
  7108. i++;
  7109. }
  7110. // accept the visitor to remove rules and refactor itself
  7111. // then we can decide now whether we want it or not
  7112. if (nodeRuleCnt > 0) {
  7113. rulesetNode.accept(this._visitor);
  7114. } else {
  7115. rulesetNode.rules = null;
  7116. }
  7117. visitArgs.visitDeeper = false;
  7118. nodeRules = rulesetNode.rules;
  7119. if (nodeRules) {
  7120. this._mergeRules(nodeRules);
  7121. nodeRules = rulesetNode.rules;
  7122. }
  7123. if (nodeRules) {
  7124. this._removeDuplicateRules(nodeRules);
  7125. nodeRules = rulesetNode.rules;
  7126. }
  7127. // now decide whether we keep the ruleset
  7128. if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) {
  7129. rulesets.splice(0, 0, rulesetNode);
  7130. }
  7131. } else {
  7132. rulesetNode.accept(this._visitor);
  7133. visitArgs.visitDeeper = false;
  7134. if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) {
  7135. rulesets.splice(0, 0, rulesetNode);
  7136. }
  7137. }
  7138. if (rulesets.length === 1) {
  7139. return rulesets[0];
  7140. }
  7141. return rulesets;
  7142. },
  7143. _removeDuplicateRules: function(rules) {
  7144. if (!rules) { return; }
  7145. // remove duplicates
  7146. var ruleCache = {},
  7147. ruleList, rule, i;
  7148. for(i = rules.length - 1; i >= 0 ; i--) {
  7149. rule = rules[i];
  7150. if (rule instanceof tree.Rule) {
  7151. if (!ruleCache[rule.name]) {
  7152. ruleCache[rule.name] = rule;
  7153. } else {
  7154. ruleList = ruleCache[rule.name];
  7155. if (ruleList instanceof tree.Rule) {
  7156. ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)];
  7157. }
  7158. var ruleCSS = rule.toCSS(this._env);
  7159. if (ruleList.indexOf(ruleCSS) !== -1) {
  7160. rules.splice(i, 1);
  7161. } else {
  7162. ruleList.push(ruleCSS);
  7163. }
  7164. }
  7165. }
  7166. }
  7167. },
  7168. _mergeRules: function (rules) {
  7169. if (!rules) { return; }
  7170. var groups = {},
  7171. parts,
  7172. rule,
  7173. key;
  7174. for (var i = 0; i < rules.length; i++) {
  7175. rule = rules[i];
  7176. if ((rule instanceof tree.Rule) && rule.merge) {
  7177. key = [rule.name,
  7178. rule.important ? "!" : ""].join(",");
  7179. if (!groups[key]) {
  7180. groups[key] = [];
  7181. } else {
  7182. rules.splice(i--, 1);
  7183. }
  7184. groups[key].push(rule);
  7185. }
  7186. }
  7187. Object.keys(groups).map(function (k) {
  7188. function toExpression(values) {
  7189. return new (tree.Expression)(values.map(function (p) {
  7190. return p.value;
  7191. }));
  7192. }
  7193. function toValue(values) {
  7194. return new (tree.Value)(values.map(function (p) {
  7195. return p;
  7196. }));
  7197. }
  7198. parts = groups[k];
  7199. if (parts.length > 1) {
  7200. rule = parts[0];
  7201. var spacedGroups = [];
  7202. var lastSpacedGroup = [];
  7203. parts.map(function (p) {
  7204. if (p.merge==="+") {
  7205. if (lastSpacedGroup.length > 0) {
  7206. spacedGroups.push(toExpression(lastSpacedGroup));
  7207. }
  7208. lastSpacedGroup = [];
  7209. }
  7210. lastSpacedGroup.push(p);
  7211. });
  7212. spacedGroups.push(toExpression(lastSpacedGroup));
  7213. rule.value = toValue(spacedGroups);
  7214. }
  7215. });
  7216. }
  7217. };
  7218. module.exports = ToCSSVisitor;
  7219. },{"../tree/index.js":43,"./visitor.js":69}],69:[function(require,module,exports){
  7220. var tree = require("../tree/index.js");
  7221. var _visitArgs = { visitDeeper: true },
  7222. _hasIndexed = false;
  7223. function _noop(node) {
  7224. return node;
  7225. }
  7226. function indexNodeTypes(parent, ticker) {
  7227. // add .typeIndex to tree node types for lookup table
  7228. var key, child;
  7229. for (key in parent) {
  7230. if (parent.hasOwnProperty(key)) {
  7231. child = parent[key];
  7232. switch (typeof child) {
  7233. case "function":
  7234. // ignore bound functions directly on tree which do not have a prototype
  7235. // or aren't nodes
  7236. if (child.prototype && child.prototype.type) {
  7237. child.prototype.typeIndex = ticker++;
  7238. }
  7239. break;
  7240. case "object":
  7241. ticker = indexNodeTypes(child, ticker);
  7242. break;
  7243. }
  7244. }
  7245. }
  7246. return ticker;
  7247. }
  7248. var Visitor = function(implementation) {
  7249. this._implementation = implementation;
  7250. this._visitFnCache = [];
  7251. if (!_hasIndexed) {
  7252. indexNodeTypes(tree, 1);
  7253. _hasIndexed = true;
  7254. }
  7255. };
  7256. Visitor.prototype = {
  7257. visit: function(node) {
  7258. if (!node) {
  7259. return node;
  7260. }
  7261. var nodeTypeIndex = node.typeIndex;
  7262. if (!nodeTypeIndex) {
  7263. return node;
  7264. }
  7265. var visitFnCache = this._visitFnCache,
  7266. impl = this._implementation,
  7267. aryIndx = nodeTypeIndex << 1,
  7268. outAryIndex = aryIndx | 1,
  7269. func = visitFnCache[aryIndx],
  7270. funcOut = visitFnCache[outAryIndex],
  7271. visitArgs = _visitArgs,
  7272. fnName;
  7273. visitArgs.visitDeeper = true;
  7274. if (!func) {
  7275. fnName = "visit" + node.type;
  7276. func = impl[fnName] || _noop;
  7277. funcOut = impl[fnName + "Out"] || _noop;
  7278. visitFnCache[aryIndx] = func;
  7279. visitFnCache[outAryIndex] = funcOut;
  7280. }
  7281. if (func !== _noop) {
  7282. var newNode = func.call(impl, node, visitArgs);
  7283. if (impl.isReplacing) {
  7284. node = newNode;
  7285. }
  7286. }
  7287. if (visitArgs.visitDeeper && node && node.accept) {
  7288. node.accept(this);
  7289. }
  7290. if (funcOut != _noop) {
  7291. funcOut.call(impl, node);
  7292. }
  7293. return node;
  7294. },
  7295. visitArray: function(nodes, nonReplacing) {
  7296. if (!nodes) {
  7297. return nodes;
  7298. }
  7299. var cnt = nodes.length, i;
  7300. // Non-replacing
  7301. if (nonReplacing || !this._implementation.isReplacing) {
  7302. for (i = 0; i < cnt; i++) {
  7303. this.visit(nodes[i]);
  7304. }
  7305. return nodes;
  7306. }
  7307. // Replacing
  7308. var out = [];
  7309. for (i = 0; i < cnt; i++) {
  7310. var evald = this.visit(nodes[i]);
  7311. if (!evald.splice) {
  7312. out.push(evald);
  7313. } else if (evald.length) {
  7314. this.flatten(evald, out);
  7315. }
  7316. }
  7317. return out;
  7318. },
  7319. flatten: function(arr, out) {
  7320. if (!out) {
  7321. out = [];
  7322. }
  7323. var cnt, i, item,
  7324. nestedCnt, j, nestedItem;
  7325. for (i = 0, cnt = arr.length; i < cnt; i++) {
  7326. item = arr[i];
  7327. if (!item.splice) {
  7328. out.push(item);
  7329. continue;
  7330. }
  7331. for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) {
  7332. nestedItem = item[j];
  7333. if (!nestedItem.splice) {
  7334. out.push(nestedItem);
  7335. } else if (nestedItem.length) {
  7336. this.flatten(nestedItem, out);
  7337. }
  7338. }
  7339. }
  7340. return out;
  7341. }
  7342. };
  7343. module.exports = Visitor;
  7344. },{"../tree/index.js":43}]},{},[1])