From 84450de9763ec5ae8ca1647ecd85293faf4c8a6d Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 18 Mar 2015 15:10:12 +0100 Subject: Add libraries (require.js and less) --- lib/less.js | 9849 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/require.js | 2083 ++++++++++++ 2 files changed, 11932 insertions(+) create mode 100644 lib/less.js create mode 100644 lib/require.js (limited to 'lib') diff --git a/lib/less.js b/lib/less.js new file mode 100644 index 0000000..9256199 --- /dev/null +++ b/lib/less.js @@ -0,0 +1,9849 @@ +/*! + * Less - Leaner CSS v2.4.0 + * http://lesscss.org + * + * Copyright (c) 2009-2015, Alexis Sellier + * Licensed under the Apache v2 License. + * + */ + + /** * @license Apache v2 + */ + +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.less=e()}}(function(){var define,module,exports;return (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) || + options.isFileProtocol ? 'development' + : 'production'); + + var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(window.location.hash); + if (dumpLineNumbers) { + options.dumpLineNumbers = dumpLineNumbers[1]; + } + + if (options.useFileCache === undefined) { + options.useFileCache = true; + } + + if (options.onReady === undefined) { + options.onReady = true; + } + +}; + +},{"./browser":3,"./utils":9}],2:[function(require,module,exports){ +/** + * Kicks off less and compiles any stylesheets + * used in the browser distributed version of less + * to kick-start less using the browser api + */ +/*global window */ + +// shim Promise if required +require('promise/polyfill.js'); + +var options = window.less || {}; +require("./add-default-options")(window, options); + +var less = module.exports = require("./index")(window, options); + +if (options.onReady) { + if (/!watch/.test(window.location.hash)) { + less.watch(); + } + + less.pageLoadFinished = less.registerStylesheets().then( + function () { + return less.refresh(less.env === 'development'); + } + ); +} +},{"./add-default-options":1,"./index":7,"promise/polyfill.js":"promise/polyfill.js"}],3:[function(require,module,exports){ +var utils = require("./utils"); +module.exports = { + createCSS: function (document, styles, sheet) { + // Strip the query-string + var href = sheet.href || ''; + + // If there is no title set, use the filename, minus the extension + var id = 'less:' + (sheet.title || utils.extractId(href)); + + // If this has already been inserted into the DOM, we may need to replace it + var oldStyleNode = document.getElementById(id); + var keepOldStyleNode = false; + + // Create a new stylesheet node for insertion or (if necessary) replacement + var styleNode = document.createElement('style'); + styleNode.setAttribute('type', 'text/css'); + if (sheet.media) { + styleNode.setAttribute('media', sheet.media); + } + styleNode.id = id; + + if (!styleNode.styleSheet) { + styleNode.appendChild(document.createTextNode(styles)); + + // If new contents match contents of oldStyleNode, don't replace oldStyleNode + keepOldStyleNode = (oldStyleNode !== null && oldStyleNode.childNodes.length > 0 && styleNode.childNodes.length > 0 && + oldStyleNode.firstChild.nodeValue === styleNode.firstChild.nodeValue); + } + + var head = document.getElementsByTagName('head')[0]; + + // If there is no oldStyleNode, just append; otherwise, only append if we need + // to replace oldStyleNode with an updated stylesheet + if (oldStyleNode === null || keepOldStyleNode === false) { + var nextEl = sheet && sheet.nextSibling || null; + if (nextEl) { + nextEl.parentNode.insertBefore(styleNode, nextEl); + } else { + head.appendChild(styleNode); + } + } + if (oldStyleNode && keepOldStyleNode === false) { + oldStyleNode.parentNode.removeChild(oldStyleNode); + } + + // For IE. + // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. + // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head + if (styleNode.styleSheet) { + try { + styleNode.styleSheet.cssText = styles; + } catch (e) { + throw new Error("Couldn't reassign styleSheet.cssText."); + } + } + }, + currentScript: function(window) { + var document = window.document; + return document.currentScript || (function() { + var scripts = document.getElementsByTagName("script"); + return scripts[scripts.length - 1]; + })(); + } +}; + +},{"./utils":9}],4:[function(require,module,exports){ +// Cache system is a bit outdated and could do with work + +module.exports = function(window, options, logger) { + var cache = null; + if (options.env !== 'development') { + try { + cache = (typeof window.localStorage === 'undefined') ? null : window.localStorage; + } catch (_) {} + } + return { + setCSS: function(path, lastModified, styles) { + if (cache) { + logger.info('saving ' + path + ' to cache.'); + try { + cache.setItem(path, styles); + cache.setItem(path + ':timestamp', lastModified); + } catch(e) { + //TODO - could do with adding more robust error handling + logger.error('failed to save "' + path + '" to local storage for caching.'); + } + } + }, + getCSS: function(path, webInfo) { + var css = cache && cache.getItem(path), + timestamp = cache && cache.getItem(path + ':timestamp'); + + if (timestamp && webInfo.lastModified && + (new Date(webInfo.lastModified).valueOf() === + new Date(timestamp).valueOf())) { + // Use local copy + return css; + } + } + }; +}; + +},{}],5:[function(require,module,exports){ +var utils = require("./utils"), + browser = require("./browser"); + +module.exports = function(window, less, options) { + + function errorHTML(e, rootHref) { + var id = 'less-error-message:' + utils.extractId(rootHref || ""); + var template = '
  • {content}
  • '; + var elem = window.document.createElement('div'), timer, content, errors = []; + var filename = e.filename || rootHref; + var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; + + elem.id = id; + elem.className = "less-error-message"; + + content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + + '

    ' + '

    in ' + filenameNoPath + " "; + + var errorline = function (e, i, classname) { + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) + .replace(/\{class\}/, classname) + .replace(/\{content\}/, e.extract[i])); + } + }; + + if (e.extract) { + errorline(e, 0, ''); + errorline(e, 1, 'line'); + errorline(e, 2, ''); + content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + + '
      ' + errors.join('') + '
    '; + } + if (e.stack && (e.extract || options.logLevel >= 4)) { + content += '
    Stack Trace
    ' + e.stack.split('\n').slice(1).join('
    '); + } + elem.innerHTML = content; + + // CSS for error messages + browser.createCSS(window.document, [ + '.less-error-message ul, .less-error-message li {', + 'list-style-type: none;', + 'margin-right: 15px;', + 'padding: 4px 0;', + 'margin: 0;', + '}', + '.less-error-message label {', + 'font-size: 12px;', + 'margin-right: 15px;', + 'padding: 4px 0;', + 'color: #cc7777;', + '}', + '.less-error-message pre {', + 'color: #dd6666;', + 'padding: 4px 0;', + 'margin: 0;', + 'display: inline-block;', + '}', + '.less-error-message pre.line {', + 'color: #ff0000;', + '}', + '.less-error-message h3 {', + 'font-size: 20px;', + 'font-weight: bold;', + 'padding: 15px 0 5px 0;', + 'margin: 0;', + '}', + '.less-error-message a {', + 'color: #10a', + '}', + '.less-error-message .error {', + 'color: red;', + 'font-weight: bold;', + 'padding-bottom: 2px;', + 'border-bottom: 1px dashed red;', + '}' + ].join('\n'), { title: 'error-message' }); + + elem.style.cssText = [ + "font-family: Arial, sans-serif", + "border: 1px solid #e00", + "background-color: #eee", + "border-radius: 5px", + "-webkit-border-radius: 5px", + "-moz-border-radius: 5px", + "color: #e00", + "padding: 15px", + "margin-bottom: 15px" + ].join(';'); + + if (options.env === 'development') { + timer = setInterval(function () { + var document = window.document, + body = document.body; + if (body) { + if (document.getElementById(id)) { + body.replaceChild(elem, document.getElementById(id)); + } else { + body.insertBefore(elem, body.firstChild); + } + clearInterval(timer); + } + }, 10); + } + } + + function error(e, rootHref) { + if (!options.errorReporting || options.errorReporting === "html") { + errorHTML(e, rootHref); + } else if (options.errorReporting === "console") { + errorConsole(e, rootHref); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("add", e, rootHref); + } + } + + function removeErrorHTML(path) { + var node = window.document.getElementById('less-error-message:' + utils.extractId(path)); + if (node) { + node.parentNode.removeChild(node); + } + } + + function removeErrorConsole(path) { + //no action + } + + function removeError(path) { + if (!options.errorReporting || options.errorReporting === "html") { + removeErrorHTML(path); + } else if (options.errorReporting === "console") { + removeErrorConsole(path); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("remove", path); + } + } + + function errorConsole(e, rootHref) { + var template = '{line} {content}'; + var filename = e.filename || rootHref; + var errors = []; + var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + + " in " + filename + " "; + + var errorline = function (e, i, classname) { + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) + .replace(/\{class\}/, classname) + .replace(/\{content\}/, e.extract[i])); + } + }; + + if (e.extract) { + errorline(e, 0, ''); + errorline(e, 1, 'line'); + errorline(e, 2, ''); + content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + + errors.join('\n'); + } + if (e.stack && (e.extract || options.logLevel >= 4)) { + content += '\nStack Trace\n' + e.stack; + } + less.logger.error(content); + } + + return { + add: error, + remove: removeError + }; +}; + +},{"./browser":3,"./utils":9}],6:[function(require,module,exports){ +/*global window, XMLHttpRequest */ + +module.exports = function(options, logger) { + + var AbstractFileManager = require("../less/environment/abstract-file-manager.js"); + + var fileCache = {}; + + //TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load + + function getXMLHttpRequest() { + if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { + return new XMLHttpRequest(); + } else { + try { + /*global ActiveXObject */ + return new ActiveXObject("Microsoft.XMLHTTP"); + } catch (e) { + logger.error("browser doesn't support AJAX."); + return null; + } + } + } + + var FileManager = function() { + }; + + FileManager.prototype = new AbstractFileManager(); + + FileManager.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() { + return true; + }; + FileManager.prototype.join = function join(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return this.extractUrlParts(laterPath, basePath).path; + }; + FileManager.prototype.doXHR = function doXHR(url, type, callback, errback) { + + var xhr = getXMLHttpRequest(); + var async = options.isFileProtocol ? options.fileAsync : options.async; + + if (typeof xhr.overrideMimeType === 'function') { + xhr.overrideMimeType('text/css'); + } + logger.debug("XHR: Getting '" + url + "'"); + xhr.open('GET', url, async); + xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); + xhr.send(null); + + function handleResponse(xhr, callback, errback) { + if (xhr.status >= 200 && xhr.status < 300) { + callback(xhr.responseText, + xhr.getResponseHeader("Last-Modified")); + } else if (typeof errback === 'function') { + errback(xhr.status, url); + } + } + + if (options.isFileProtocol && !options.fileAsync) { + if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { + callback(xhr.responseText); + } else { + errback(xhr.status, url); + } + } else if (async) { + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + handleResponse(xhr, callback, errback); + } + }; + } else { + handleResponse(xhr, callback, errback); + } + }; + FileManager.prototype.supports = function(filename, currentDirectory, options, environment) { + return true; + }; + + FileManager.prototype.clearFileCache = function() { + fileCache = {}; + }; + + FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment, callback) { + if (currentDirectory && !this.isPathAbsolute(filename)) { + filename = currentDirectory + filename; + } + + options = options || {}; + + // sheet may be set to the stylesheet for the initial load or a collection of properties including + // some context variables for imports + var hrefParts = this.extractUrlParts(filename, window.location.href); + var href = hrefParts.url; + + if (options.useFileCache && fileCache[href]) { + try { + var lessText = fileCache[href]; + callback(null, { contents: lessText, filename: href, webInfo: { lastModified: new Date() }}); + } catch (e) { + callback({filename: href, message: "Error loading file " + href + " error was " + e.message}); + } + return; + } + + this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) { + // per file cache + fileCache[href] = data; + + // Use remote copy (re-parse) + callback(null, { contents: data, filename: href, webInfo: { lastModified: lastModified }}); + }, function doXHRError(status, url) { + callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href }); + }); + }; + + return FileManager; +}; + +},{"../less/environment/abstract-file-manager.js":14}],7:[function(require,module,exports){ +// +// index.js +// Should expose the additional browser functions on to the less object +// +var addDataAttr = require("./utils").addDataAttr, + browser = require("./browser"); + +module.exports = function(window, options) { + var document = window.document; + var less = require('../less')(); + //module.exports = less; + less.options = options; + var environment = less.environment, + FileManager = require("./file-manager")(options, less.logger), + fileManager = new FileManager(); + environment.addFileManager(fileManager); + less.FileManager = FileManager; + + require("./log-listener")(less, options); + var errors = require("./error-reporting")(window, less, options); + var cache = less.cache = options.cache || require("./cache")(window, options, less.logger); + + //Setup user functions + if (options.functions) { + less.functions.functionRegistry.addMultiple(options.functions); + } + + var typePattern = /^text\/(x-)?less$/; + + function postProcessCSS(styles) { + if (options.postProcessor && typeof options.postProcessor === 'function') { + styles = options.postProcessor.call(styles, styles) || styles; + } + return styles; + } + + function clone(obj) { + var cloned = {}; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + return cloned; + } + + // only really needed for phantom + function bind(func, thisArg) { + var curryArgs = Array.prototype.slice.call(arguments, 2); + return function() { + var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0)); + return func.apply(thisArg, args); + }; + } + + function loadStyles(modifyVars) { + var styles = document.getElementsByTagName('style'), + style; + + for (var i = 0; i < styles.length; i++) { + style = styles[i]; + if (style.type.match(typePattern)) { + var instanceOptions = clone(options); + instanceOptions.modifyVars = modifyVars; + var lessText = style.innerHTML || ''; + instanceOptions.filename = document.location.href.replace(/#.*$/, ''); + + /*jshint loopfunc:true */ + // use closure to store current style + less.render(lessText, instanceOptions, + bind(function(style, e, result) { + if (e) { + errors.add(e, "inline"); + } else { + style.type = 'text/css'; + if (style.styleSheet) { + style.styleSheet.cssText = result.css; + } else { + style.innerHTML = result.css; + } + } + }, null, style)); + } + } + } + + function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { + + var instanceOptions = clone(options); + addDataAttr(instanceOptions, sheet); + instanceOptions.mime = sheet.type; + + if (modifyVars) { + instanceOptions.modifyVars = modifyVars; + } + + function loadInitialFileCallback(loadedFile) { + + var data = loadedFile.contents, + path = loadedFile.filename, + webInfo = loadedFile.webInfo; + + var newFileInfo = { + currentDirectory: fileManager.getPath(path), + filename: path, + rootFilename: path, + relativeUrls: instanceOptions.relativeUrls}; + + newFileInfo.entryPath = newFileInfo.currentDirectory; + newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory; + + if (webInfo) { + webInfo.remaining = remaining; + + if (!instanceOptions.modifyVars) { + var css = cache.getCSS(path, webInfo); + if (!reload && css) { + webInfo.local = true; + callback(null, css, data, sheet, webInfo, path); + return; + } + } + } + + //TODO add tests around how this behaves when reloading + errors.remove(path); + + instanceOptions.rootFileInfo = newFileInfo; + less.render(data, instanceOptions, function(e, result) { + if (e) { + e.href = path; + callback(e); + } else { + result.css = postProcessCSS(result.css); + if (!instanceOptions.modifyVars) { + cache.setCSS(sheet.href, webInfo.lastModified, result.css); + } + callback(null, result.css, data, sheet, webInfo, path); + } + }); + } + + fileManager.loadFile(sheet.href, null, instanceOptions, environment, function(e, loadedFile) { + if (e) { + callback(e); + return; + } + loadInitialFileCallback(loadedFile); + }); + } + + function loadStyleSheets(callback, reload, modifyVars) { + for (var i = 0; i < less.sheets.length; i++) { + loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); + } + } + + function initRunningMode() { + if (less.env === 'development') { + less.watchTimer = setInterval(function () { + if (less.watchMode) { + fileManager.clearFileCache(); + loadStyleSheets(function (e, css, _, sheet, webInfo) { + if (e) { + errors.add(e, e.href || sheet.href); + } else if (css) { + browser.createCSS(window.document, css, sheet); + } + }); + } + }, options.poll); + } + } + + // + // Watch mode + // + less.watch = function () { + if (!less.watchMode ) { + less.env = 'development'; + initRunningMode(); + } + this.watchMode = true; + return true; + }; + + less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; + + // + // Get all tags with the 'rel' attribute set to "stylesheet/less" + // + less.registerStylesheets = function() { + return new Promise(function(resolve, reject) { + var links = document.getElementsByTagName('link'); + less.sheets = []; + + for (var i = 0; i < links.length; i++) { + if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && + (links[i].type.match(typePattern)))) { + less.sheets.push(links[i]); + } + } + + resolve(); + }); + }; + + // + // With this function, it's possible to alter variables and re-render + // CSS without reloading less-files + // + less.modifyVars = function(record) { + return less.refresh(true, record, false); + }; + + less.refresh = function (reload, modifyVars, clearFileCache) { + if ((reload || clearFileCache) && clearFileCache !== false) { + fileManager.clearFileCache(); + } + return new Promise(function (resolve, reject) { + var startTime, endTime, totalMilliseconds; + startTime = endTime = new Date(); + + loadStyleSheets(function (e, css, _, sheet, webInfo) { + if (e) { + errors.add(e, e.href || sheet.href); + reject(e); + return; + } + if (webInfo.local) { + less.logger.info("loading " + sheet.href + " from cache."); + } else { + less.logger.info("rendered " + sheet.href + " successfully."); + } + browser.createCSS(window.document, css, sheet); + less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); + if (webInfo.remaining === 0) { + totalMilliseconds = new Date() - startTime; + less.logger.info("less has finished. css generated in " + totalMilliseconds + 'ms'); + resolve({ + startTime: startTime, + endTime: endTime, + totalMilliseconds: totalMilliseconds, + sheets: less.sheets.length + }); + } + endTime = new Date(); + }, reload, modifyVars); + + loadStyles(modifyVars); + }); + }; + + less.refreshStyles = loadStyles; + return less; +}; + +},{"../less":29,"./browser":3,"./cache":4,"./error-reporting":5,"./file-manager":6,"./log-listener":8,"./utils":9}],8:[function(require,module,exports){ +module.exports = function(less, options) { + + var logLevel_debug = 4, + logLevel_info = 3, + logLevel_warn = 2, + logLevel_error = 1; + + // The amount of logging in the javascript console. + // 3 - Debug, information and errors + // 2 - Information and errors + // 1 - Errors + // 0 - None + // Defaults to 2 + options.logLevel = typeof options.logLevel !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_info : logLevel_error); + + if (!options.loggers) { + options.loggers = [{ + debug: function(msg) { + if (options.logLevel >= logLevel_debug) { + console.log(msg); + } + }, + info: function(msg) { + if (options.logLevel >= logLevel_info) { + console.log(msg); + } + }, + warn: function(msg) { + if (options.logLevel >= logLevel_warn) { + console.warn(msg); + } + }, + error: function(msg) { + if (options.logLevel >= logLevel_error) { + console.error(msg); + } + } + }]; + } + for (var i = 0; i < options.loggers.length; i++) { + less.logger.addListener(options.loggers[i]); + } +}; + +},{}],9:[function(require,module,exports){ +module.exports = { + extractId: function(href) { + return href.replace(/^[a-z-]+:\/+?[^\/]+/, '') // Remove protocol & domain + .replace(/[\?\&]livereload=\w+/, '') // Remove LiveReload cachebuster + .replace(/^\//, '') // Remove root / + .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension + .replace(/[^\.\w-]+/g, '-') // Replace illegal characters + .replace(/\./g, ':'); // Replace dots with colons(for valid id) + }, + addDataAttr: function(options, tag) { + for (var opt in tag.dataset) { + if (tag.dataset.hasOwnProperty(opt)) { + if (opt === "env" || opt === "dumpLineNumbers" || opt === "rootpath" || opt === "errorReporting") { + options[opt] = tag.dataset[opt]; + } else { + try { + options[opt] = JSON.parse(tag.dataset[opt]); + } + catch(_) {} + } + } + } + } +}; + +},{}],10:[function(require,module,exports){ +var contexts = {}; +module.exports = contexts; + +var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) { + if (!original) { return; } + + for (var i = 0; i < propertiesToCopy.length; i++) { + if (original.hasOwnProperty(propertiesToCopy[i])) { + destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; + } + } +}; + +/* + parse is used whilst parsing + */ +var parseCopyProperties = [ + // options + 'paths', // option - unmodified - paths to search for imports on + 'relativeUrls', // option - whether to adjust URL's to be relative + 'rootpath', // option - rootpath to append to URL's + 'strictImports', // option - + 'insecure', // option - whether to allow imports from insecure ssl hosts + 'dumpLineNumbers', // option - whether to dump line numbers + 'compress', // option - whether to compress + 'syncImport', // option - whether to import synchronously + 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. + 'mime', // browser only - mime type for sheet import + 'useFileCache', // browser only - whether to use the per file session cache + // context + 'processImports', // option & context - whether to process imports. if false then imports will not be imported. + // Used by the import manager to stop multiple import visitors being created. + 'reference', // Used to indicate that the contents are imported by reference + 'pluginManager' // Used as the plugin manager for the session +]; + +contexts.Parse = function(options) { + copyFromOriginal(options, this, parseCopyProperties); + + if (typeof this.paths === "string") { this.paths = [this.paths]; } +}; + +var evalCopyProperties = [ + 'paths', // additional include paths + 'compress', // whether to compress + 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) + 'strictMath', // whether math has to be within parenthesis + 'strictUnits', // whether units need to evaluate correctly + 'sourceMap', // whether to output a source map + 'importMultiple', // whether we are currently importing multiple copies + 'urlArgs', // whether to add args into url tokens + 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true + 'pluginManager', // Used as the plugin manager for the session + 'importantScope' // used to bubble up !important statements + ]; + +contexts.Eval = function(options, frames) { + copyFromOriginal(options, this, evalCopyProperties); + + this.frames = frames || []; + this.importantScope = this.importantScope || []; +}; + +contexts.Eval.prototype.inParenthesis = function () { + if (!this.parensStack) { + this.parensStack = []; + } + this.parensStack.push(true); +}; + +contexts.Eval.prototype.outOfParenthesis = function () { + this.parensStack.pop(); +}; + +contexts.Eval.prototype.isMathOn = function () { + return this.strictMath ? (this.parensStack && this.parensStack.length) : true; +}; + +contexts.Eval.prototype.isPathRelative = function (path) { + return !/^(?:[a-z-]+:|\/|#)/i.test(path); +}; + +contexts.Eval.prototype.normalizePath = function( path ) { + var + segments = path.split("/").reverse(), + segment; + + path = []; + while (segments.length !== 0 ) { + segment = segments.pop(); + switch( segment ) { + case ".": + break; + case "..": + if ((path.length === 0) || (path[path.length - 1] === "..")) { + path.push( segment ); + } else { + path.pop(); + } + break; + default: + path.push( segment ); + break; + } + } + + return path.join("/"); +}; + +//todo - do the same for the toCSS ? + +},{}],11:[function(require,module,exports){ +module.exports = { + 'aliceblue':'#f0f8ff', + 'antiquewhite':'#faebd7', + 'aqua':'#00ffff', + 'aquamarine':'#7fffd4', + 'azure':'#f0ffff', + 'beige':'#f5f5dc', + 'bisque':'#ffe4c4', + 'black':'#000000', + 'blanchedalmond':'#ffebcd', + 'blue':'#0000ff', + 'blueviolet':'#8a2be2', + 'brown':'#a52a2a', + 'burlywood':'#deb887', + 'cadetblue':'#5f9ea0', + 'chartreuse':'#7fff00', + 'chocolate':'#d2691e', + 'coral':'#ff7f50', + 'cornflowerblue':'#6495ed', + 'cornsilk':'#fff8dc', + 'crimson':'#dc143c', + 'cyan':'#00ffff', + 'darkblue':'#00008b', + 'darkcyan':'#008b8b', + 'darkgoldenrod':'#b8860b', + 'darkgray':'#a9a9a9', + 'darkgrey':'#a9a9a9', + 'darkgreen':'#006400', + 'darkkhaki':'#bdb76b', + 'darkmagenta':'#8b008b', + 'darkolivegreen':'#556b2f', + 'darkorange':'#ff8c00', + 'darkorchid':'#9932cc', + 'darkred':'#8b0000', + 'darksalmon':'#e9967a', + 'darkseagreen':'#8fbc8f', + 'darkslateblue':'#483d8b', + 'darkslategray':'#2f4f4f', + 'darkslategrey':'#2f4f4f', + 'darkturquoise':'#00ced1', + 'darkviolet':'#9400d3', + 'deeppink':'#ff1493', + 'deepskyblue':'#00bfff', + 'dimgray':'#696969', + 'dimgrey':'#696969', + 'dodgerblue':'#1e90ff', + 'firebrick':'#b22222', + 'floralwhite':'#fffaf0', + 'forestgreen':'#228b22', + 'fuchsia':'#ff00ff', + 'gainsboro':'#dcdcdc', + 'ghostwhite':'#f8f8ff', + 'gold':'#ffd700', + 'goldenrod':'#daa520', + 'gray':'#808080', + 'grey':'#808080', + 'green':'#008000', + 'greenyellow':'#adff2f', + 'honeydew':'#f0fff0', + 'hotpink':'#ff69b4', + 'indianred':'#cd5c5c', + 'indigo':'#4b0082', + 'ivory':'#fffff0', + 'khaki':'#f0e68c', + 'lavender':'#e6e6fa', + 'lavenderblush':'#fff0f5', + 'lawngreen':'#7cfc00', + 'lemonchiffon':'#fffacd', + 'lightblue':'#add8e6', + 'lightcoral':'#f08080', + 'lightcyan':'#e0ffff', + 'lightgoldenrodyellow':'#fafad2', + 'lightgray':'#d3d3d3', + 'lightgrey':'#d3d3d3', + 'lightgreen':'#90ee90', + 'lightpink':'#ffb6c1', + 'lightsalmon':'#ffa07a', + 'lightseagreen':'#20b2aa', + 'lightskyblue':'#87cefa', + 'lightslategray':'#778899', + 'lightslategrey':'#778899', + 'lightsteelblue':'#b0c4de', + 'lightyellow':'#ffffe0', + 'lime':'#00ff00', + 'limegreen':'#32cd32', + 'linen':'#faf0e6', + 'magenta':'#ff00ff', + 'maroon':'#800000', + 'mediumaquamarine':'#66cdaa', + 'mediumblue':'#0000cd', + 'mediumorchid':'#ba55d3', + 'mediumpurple':'#9370d8', + 'mediumseagreen':'#3cb371', + 'mediumslateblue':'#7b68ee', + 'mediumspringgreen':'#00fa9a', + 'mediumturquoise':'#48d1cc', + 'mediumvioletred':'#c71585', + 'midnightblue':'#191970', + 'mintcream':'#f5fffa', + 'mistyrose':'#ffe4e1', + 'moccasin':'#ffe4b5', + 'navajowhite':'#ffdead', + 'navy':'#000080', + 'oldlace':'#fdf5e6', + 'olive':'#808000', + 'olivedrab':'#6b8e23', + 'orange':'#ffa500', + 'orangered':'#ff4500', + 'orchid':'#da70d6', + 'palegoldenrod':'#eee8aa', + 'palegreen':'#98fb98', + 'paleturquoise':'#afeeee', + 'palevioletred':'#d87093', + 'papayawhip':'#ffefd5', + 'peachpuff':'#ffdab9', + 'peru':'#cd853f', + 'pink':'#ffc0cb', + 'plum':'#dda0dd', + 'powderblue':'#b0e0e6', + 'purple':'#800080', + 'rebeccapurple':'#663399', + 'red':'#ff0000', + 'rosybrown':'#bc8f8f', + 'royalblue':'#4169e1', + 'saddlebrown':'#8b4513', + 'salmon':'#fa8072', + 'sandybrown':'#f4a460', + 'seagreen':'#2e8b57', + 'seashell':'#fff5ee', + 'sienna':'#a0522d', + 'silver':'#c0c0c0', + 'skyblue':'#87ceeb', + 'slateblue':'#6a5acd', + 'slategray':'#708090', + 'slategrey':'#708090', + 'snow':'#fffafa', + 'springgreen':'#00ff7f', + 'steelblue':'#4682b4', + 'tan':'#d2b48c', + 'teal':'#008080', + 'thistle':'#d8bfd8', + 'tomato':'#ff6347', + 'turquoise':'#40e0d0', + 'violet':'#ee82ee', + 'wheat':'#f5deb3', + 'white':'#ffffff', + 'whitesmoke':'#f5f5f5', + 'yellow':'#ffff00', + 'yellowgreen':'#9acd32' +}; +},{}],12:[function(require,module,exports){ +module.exports = { + colors: require("./colors"), + unitConversions: require("./unit-conversions") +}; + +},{"./colors":11,"./unit-conversions":13}],13:[function(require,module,exports){ +module.exports = { + length: { + 'm': 1, + 'cm': 0.01, + 'mm': 0.001, + 'in': 0.0254, + 'px': 0.0254 / 96, + 'pt': 0.0254 / 72, + 'pc': 0.0254 / 72 * 12 + }, + duration: { + 's': 1, + 'ms': 0.001 + }, + angle: { + 'rad': 1 / (2 * Math.PI), + 'deg': 1 / 360, + 'grad': 1 / 400, + 'turn': 1 + } +}; +},{}],14:[function(require,module,exports){ +var abstractFileManager = function() { +}; + +abstractFileManager.prototype.getPath = function (filename) { + var j = filename.lastIndexOf('?'); + if (j > 0) { + filename = filename.slice(0, j); + } + j = filename.lastIndexOf('/'); + if (j < 0) { + j = filename.lastIndexOf('\\'); + } + if (j < 0) { + return ""; + } + return filename.slice(0, j + 1); +}; + +abstractFileManager.prototype.tryAppendLessExtension = function(path) { + return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + '.less'; +}; + +abstractFileManager.prototype.supportsSync = function() { + return false; +}; + +abstractFileManager.prototype.alwaysMakePathsAbsolute = function() { + return false; +}; + +abstractFileManager.prototype.isPathAbsolute = function(filename) { + return (/^(?:[a-z-]+:|\/|\\|#)/i).test(filename); +}; + +abstractFileManager.prototype.join = function(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return basePath + laterPath; +}; +abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) { + // diff between two paths to create a relative path + + var urlParts = this.extractUrlParts(url), + baseUrlParts = this.extractUrlParts(baseUrl), + i, max, urlDirectories, baseUrlDirectories, diff = ""; + if (urlParts.hostPart !== baseUrlParts.hostPart) { + return ""; + } + max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); + for (i = 0; i < max; i++) { + if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } + } + baseUrlDirectories = baseUrlParts.directories.slice(i); + urlDirectories = urlParts.directories.slice(i); + for (i = 0; i < baseUrlDirectories.length - 1; i++) { + diff += "../"; + } + for (i = 0; i < urlDirectories.length - 1; i++) { + diff += urlDirectories[i] + "/"; + } + return diff; +}; +// helper function, not part of API +abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) { + // urlParts[1] = protocol&hostname || / + // urlParts[2] = / if path relative to host base + // urlParts[3] = directories + // urlParts[4] = filename + // urlParts[5] = parameters + + var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, + urlParts = url.match(urlPartsRegex), + returner = {}, directories = [], i, baseUrlParts; + + if (!urlParts) { + throw new Error("Could not parse sheet href - '" + url + "'"); + } + + // Stylesheets in IE don't always return the full path + if (baseUrl && (!urlParts[1] || urlParts[2])) { + baseUrlParts = baseUrl.match(urlPartsRegex); + if (!baseUrlParts) { + throw new Error("Could not parse page url - '" + baseUrl + "'"); + } + urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; + if (!urlParts[2]) { + urlParts[3] = baseUrlParts[3] + urlParts[3]; + } + } + + if (urlParts[3]) { + directories = urlParts[3].replace(/\\/g, "/").split("/"); + + // extract out . before .. so .. doesn't absorb a non-directory + for (i = 0; i < directories.length; i++) { + if (directories[i] === ".") { + directories.splice(i, 1); + i -= 1; + } + } + + for (i = 0; i < directories.length; i++) { + if (directories[i] === ".." && i > 0) { + directories.splice(i - 1, 2); + i -= 2; + } + } + } + + returner.hostPart = urlParts[1]; + returner.directories = directories; + returner.path = (urlParts[1] || "") + directories.join("/"); + returner.fileUrl = returner.path + (urlParts[4] || ""); + returner.url = returner.fileUrl + (urlParts[5] || ""); + return returner; +}; + +module.exports = abstractFileManager; + +},{}],15:[function(require,module,exports){ +var logger = require("../logger"); +var environment = function(externalEnvironment, fileManagers) { + this.fileManagers = fileManagers || []; + externalEnvironment = externalEnvironment || {}; + + var optionalFunctions = ["encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"], + requiredFunctions = [], + functions = requiredFunctions.concat(optionalFunctions); + + for (var i = 0; i < functions.length; i++) { + var propName = functions[i], + environmentFunc = externalEnvironment[propName]; + if (environmentFunc) { + this[propName] = environmentFunc.bind(externalEnvironment); + } else if (i < requiredFunctions.length) { + this.warn("missing required function in environment - " + propName); + } + } +}; + +environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { + + if (!filename) { + logger.warn("getFileManager called with no filename.. Please report this issue. continuing."); + } + if (currentDirectory == null) { + logger.warn("getFileManager called with null directory.. Please report this issue. continuing."); + } + + var fileManagers = this.fileManagers; + if (options.pluginManager) { + fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers()); + } + for (var i = fileManagers.length - 1; i >= 0 ; i--) { + var fileManager = fileManagers[i]; + if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) { + return fileManager; + } + } + return null; +}; + +environment.prototype.addFileManager = function (fileManager) { + this.fileManagers.push(fileManager); +}; + +environment.prototype.clearFileManagers = function () { + this.fileManagers = []; +}; + +module.exports = environment; + +},{"../logger":31}],16:[function(require,module,exports){ +var Color = require("../tree/color"), + functionRegistry = require("./function-registry"); + +// Color Blending +// ref: http://www.w3.org/TR/compositing-1 + +function colorBlend(mode, color1, color2) { + var ab = color1.alpha, cb, // backdrop + as = color2.alpha, cs, // source + ar, cr, r = []; // result + + ar = as + ab * (1 - as); + for (var i = 0; i < 3; i++) { + cb = color1.rgb[i] / 255; + cs = color2.rgb[i] / 255; + cr = mode(cb, cs); + if (ar) { + cr = (as * cs + ab * (cb - + as * (cb + cs - cr))) / ar; + } + r[i] = cr * 255; + } + + return new Color(r, ar); +} + +var colorBlendModeFunctions = { + multiply: function(cb, cs) { + return cb * cs; + }, + screen: function(cb, cs) { + return cb + cs - cb * cs; + }, + overlay: function(cb, cs) { + cb *= 2; + return (cb <= 1) ? + colorBlendModeFunctions.multiply(cb, cs) : + colorBlendModeFunctions.screen(cb - 1, cs); + }, + softlight: function(cb, cs) { + var d = 1, e = cb; + if (cs > 0.5) { + e = 1; + d = (cb > 0.25) ? Math.sqrt(cb) + : ((16 * cb - 12) * cb + 4) * cb; + } + return cb - (1 - 2 * cs) * e * (d - cb); + }, + hardlight: function(cb, cs) { + return colorBlendModeFunctions.overlay(cs, cb); + }, + difference: function(cb, cs) { + return Math.abs(cb - cs); + }, + exclusion: function(cb, cs) { + return cb + cs - 2 * cb * cs; + }, + + // non-w3c functions: + average: function(cb, cs) { + return (cb + cs) / 2; + }, + negation: function(cb, cs) { + return 1 - Math.abs(cb + cs - 1); + } +}; + +for (var f in colorBlendModeFunctions) { + if (colorBlendModeFunctions.hasOwnProperty(f)) { + colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]); + } +} + +functionRegistry.addMultiple(colorBlend); + +},{"../tree/color":47,"./function-registry":21}],17:[function(require,module,exports){ +var Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + functionRegistry = require("./function-registry"), + colorFunctions; + +function clamp(val) { + return Math.min(1, Math.max(0, val)); +} +function hsla(color) { + return colorFunctions.hsla(color.h, color.s, color.l, color.a); +} +function number(n) { + if (n instanceof Dimension) { + return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); + } else if (typeof n === 'number') { + return n; + } else { + throw { + type: "Argument", + message: "color functions take numbers as parameters" + }; + } +} +function scaled(n, size) { + if (n instanceof Dimension && n.unit.is('%')) { + return parseFloat(n.value * size / 100); + } else { + return number(n); + } +} +colorFunctions = { + rgb: function (r, g, b) { + return colorFunctions.rgba(r, g, b, 1.0); + }, + rgba: function (r, g, b, a) { + var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); + a = number(a); + return new Color(rgb, a); + }, + hsl: function (h, s, l) { + return colorFunctions.hsla(h, s, l, 1.0); + }, + hsla: function (h, s, l, a) { + function hue(h) { + h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h); + if (h * 6 < 1) { + return m1 + (m2 - m1) * h * 6; + } + else if (h * 2 < 1) { + return m2; + } + else if (h * 3 < 2) { + return m1 + (m2 - m1) * (2 / 3 - h) * 6; + } + else { + return m1; + } + } + + h = (number(h) % 360) / 360; + s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); + + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + return colorFunctions.rgba(hue(h + 1 / 3) * 255, + hue(h) * 255, + hue(h - 1 / 3) * 255, + a); + }, + + hsv: function(h, s, v) { + return colorFunctions.hsva(h, s, v, 1.0); + }, + + hsva: function(h, s, v, a) { + h = ((number(h) % 360) / 360) * 360; + s = number(s); v = number(v); a = number(a); + + var i, f; + i = Math.floor((h / 60) % 6); + f = (h / 60) - i; + + var vs = [v, + v * (1 - s), + v * (1 - f * s), + v * (1 - (1 - f) * s)]; + var perm = [[0, 3, 1], + [2, 0, 1], + [1, 0, 3], + [1, 2, 0], + [3, 1, 0], + [0, 1, 2]]; + + return colorFunctions.rgba(vs[perm[i][0]] * 255, + vs[perm[i][1]] * 255, + vs[perm[i][2]] * 255, + a); + }, + + hue: function (color) { + return new Dimension(color.toHSL().h); + }, + saturation: function (color) { + return new Dimension(color.toHSL().s * 100, '%'); + }, + lightness: function (color) { + return new Dimension(color.toHSL().l * 100, '%'); + }, + hsvhue: function(color) { + return new Dimension(color.toHSV().h); + }, + hsvsaturation: function (color) { + return new Dimension(color.toHSV().s * 100, '%'); + }, + hsvvalue: function (color) { + return new Dimension(color.toHSV().v * 100, '%'); + }, + red: function (color) { + return new Dimension(color.rgb[0]); + }, + green: function (color) { + return new Dimension(color.rgb[1]); + }, + blue: function (color) { + return new Dimension(color.rgb[2]); + }, + alpha: function (color) { + return new Dimension(color.toHSL().a); + }, + luma: function (color) { + return new Dimension(color.luma() * color.alpha * 100, '%'); + }, + luminance: function (color) { + var luminance = + (0.2126 * color.rgb[0] / 255) + + (0.7152 * color.rgb[1] / 255) + + (0.0722 * color.rgb[2] / 255); + + return new Dimension(luminance * color.alpha * 100, '%'); + }, + saturate: function (color, amount) { + // filter: saturate(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + var hsl = color.toHSL(); + + hsl.s += amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + desaturate: function (color, amount) { + var hsl = color.toHSL(); + + hsl.s -= amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + lighten: function (color, amount) { + var hsl = color.toHSL(); + + hsl.l += amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + darken: function (color, amount) { + var hsl = color.toHSL(); + + hsl.l -= amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + fadein: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a += amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fadeout: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a -= amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fade: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a = amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + spin: function (color, amount) { + var hsl = color.toHSL(); + var hue = (hsl.h + amount.value) % 360; + + hsl.h = hue < 0 ? 360 + hue : hue; + + return hsla(hsl); + }, + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + mix: function (color1, color2, weight) { + if (!weight) { + weight = new Dimension(50); + } + var p = weight.value / 100.0; + var w = p * 2 - 1; + var a = color1.toHSL().a - color2.toHSL().a; + + var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, + color1.rgb[1] * w1 + color2.rgb[1] * w2, + color1.rgb[2] * w1 + color2.rgb[2] * w2]; + + var alpha = color1.alpha * p + color2.alpha * (1 - p); + + return new Color(rgb, alpha); + }, + greyscale: function (color) { + return colorFunctions.desaturate(color, new Dimension(100)); + }, + contrast: function (color, dark, light, threshold) { + // filter: contrast(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + if (typeof light === 'undefined') { + light = colorFunctions.rgba(255, 255, 255, 1.0); + } + if (typeof dark === 'undefined') { + dark = colorFunctions.rgba(0, 0, 0, 1.0); + } + //Figure out which is actually light and dark! + if (dark.luma() > light.luma()) { + var t = light; + light = dark; + dark = t; + } + if (typeof threshold === 'undefined') { + threshold = 0.43; + } else { + threshold = number(threshold); + } + if (color.luma() < threshold) { + return light; + } else { + return dark; + } + }, + argb: function (color) { + return new Anonymous(color.toARGB()); + }, + color: function(c) { + if ((c instanceof Quoted) && + (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { + return new Color(c.value.slice(1)); + } + if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) { + c.value = undefined; + return c; + } + throw { + type: "Argument", + message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" + }; + }, + tint: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount); + }, + shade: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount); + } +}; +functionRegistry.addMultiple(colorFunctions); + +},{"../tree/anonymous":43,"../tree/color":47,"../tree/dimension":53,"../tree/quoted":70,"./function-registry":21}],18:[function(require,module,exports){ +module.exports = function(environment) { + var Quoted = require("../tree/quoted"), + URL = require("../tree/url"), + functionRegistry = require("./function-registry"), + fallback = function(functionThis, node) { + return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context); + }, + logger = require('../logger'); + + functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { + + if (!filePathNode) { + filePathNode = mimetypeNode; + mimetypeNode = null; + } + + var mimetype = mimetypeNode && mimetypeNode.value; + var filePath = filePathNode.value; + var currentFileInfo = this.currentFileInfo; + var currentDirectory = currentFileInfo.relativeUrls ? + currentFileInfo.currentDirectory : currentFileInfo.entryPath; + + var fragmentStart = filePath.indexOf('#'); + var fragment = ''; + if (fragmentStart !== -1) { + fragment = filePath.slice(fragmentStart); + filePath = filePath.slice(0, fragmentStart); + } + + var fileManager = environment.getFileManager(filePath, currentDirectory, this.context, environment, true); + + if (!fileManager) { + return fallback(this, filePathNode); + } + + var useBase64 = false; + + // detect the mimetype if not given + if (!mimetypeNode) { + + mimetype = environment.mimeLookup(filePath); + + if (mimetype === "image/svg+xml") { + useBase64 = false; + } else { + // use base 64 unless it's an ASCII or UTF-8 format + var charset = environment.charsetLookup(mimetype); + useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; + } + if (useBase64) { mimetype += ';base64'; } + } + else { + useBase64 = /;base64$/.test(mimetype); + } + + var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment); + if (!fileSync.contents) { + logger.warn("Skipped data-uri embedding of " + filePath + " because file not found"); + return fallback(this, filePathNode || mimetypeNode); + } + var buf = fileSync.contents; + if (useBase64 && !environment.encodeBase64) { + return fallback(this, filePathNode); + } + + buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf); + + var uri = "data:" + mimetype + ',' + buf + fragment; + + // IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded + // and the --ieCompat flag is enabled, return a normal url() instead. + var DATA_URI_MAX = 32768; + if (uri.length >= DATA_URI_MAX) { + + if (this.context.ieCompat !== false) { + logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length + + " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!"); + + return fallback(this, filePathNode || mimetypeNode); + } + } + + return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo); + }); +}; + +},{"../logger":31,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],19:[function(require,module,exports){ +var Keyword = require("../tree/keyword"), + functionRegistry = require("./function-registry"); + +var defaultFunc = { + eval: function () { + var v = this.value_, e = this.error_; + if (e) { + throw e; + } + if (v != null) { + return v ? Keyword.True : Keyword.False; + } + }, + value: function (v) { + this.value_ = v; + }, + error: function (e) { + this.error_ = e; + }, + reset: function () { + this.value_ = this.error_ = null; + } +}; + +functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); + +module.exports = defaultFunc; + +},{"../tree/keyword":62,"./function-registry":21}],20:[function(require,module,exports){ +var functionRegistry = require("./function-registry"); + +var functionCaller = function(name, context, index, currentFileInfo) { + this.name = name.toLowerCase(); + this.func = functionRegistry.get(this.name); + this.index = index; + this.context = context; + this.currentFileInfo = currentFileInfo; +}; +functionCaller.prototype.isValid = function() { + return Boolean(this.func); +}; +functionCaller.prototype.call = function(args) { + return this.func.apply(this, args); +}; + +module.exports = functionCaller; + +},{"./function-registry":21}],21:[function(require,module,exports){ +module.exports = { + _data: {}, + add: function(name, func) { + if (this._data.hasOwnProperty(name)) { + //TODO warn + } + this._data[name] = func; + }, + addMultiple: function(functions) { + Object.keys(functions).forEach( + function(name) { + this.add(name, functions[name]); + }.bind(this)); + }, + get: function(name) { + return this._data[name]; + } +}; + +},{}],22:[function(require,module,exports){ +module.exports = function(environment) { + var functions = { + functionRegistry: require("./function-registry"), + functionCaller: require("./function-caller") + }; + + //register functions + require("./default"); + require("./color"); + require("./color-blending"); + require("./data-uri")(environment); + require("./math"); + require("./number"); + require("./string"); + require("./svg")(environment); + require("./types"); + + return functions; +}; + +},{"./color":17,"./color-blending":16,"./data-uri":18,"./default":19,"./function-caller":20,"./function-registry":21,"./math":23,"./number":24,"./string":25,"./svg":26,"./types":27}],23:[function(require,module,exports){ +var Dimension = require("../tree/dimension"), + functionRegistry = require("./function-registry"); + +var mathFunctions = { + // name, unit + ceil: null, + floor: null, + sqrt: null, + abs: null, + tan: "", + sin: "", + cos: "", + atan: "rad", + asin: "rad", + acos: "rad" +}; + +function _math(fn, unit, n) { + if (!(n instanceof Dimension)) { + throw { type: "Argument", message: "argument must be a number" }; + } + if (unit == null) { + unit = n.unit; + } else { + n = n.unify(); + } + return new Dimension(fn(parseFloat(n.value)), unit); +} + +for (var f in mathFunctions) { + if (mathFunctions.hasOwnProperty(f)) { + mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]); + } +} + +mathFunctions.round = function (n, f) { + var fraction = typeof f === "undefined" ? 0 : f.value; + return _math(function(num) { return num.toFixed(fraction); }, null, n); +}; + +functionRegistry.addMultiple(mathFunctions); + +},{"../tree/dimension":53,"./function-registry":21}],24:[function(require,module,exports){ +var Dimension = require("../tree/dimension"), + Anonymous = require("../tree/anonymous"), + functionRegistry = require("./function-registry"); + +var minMax = function (isMin, args) { + args = Array.prototype.slice.call(args); + switch(args.length) { + case 0: throw { type: "Argument", message: "one or more arguments required" }; + } + var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, + order = [], // elems only contains original argument values. + values = {}; // key is the unit.toString() for unified Dimension values, + // value is the index into the order array. + for (i = 0; i < args.length; i++) { + current = args[i]; + if (!(current instanceof Dimension)) { + if (Array.isArray(args[i].value)) { + Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); + } + continue; + } + currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify(); + unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); + unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; + unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; + j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; + if (j === undefined) { + if (unitStatic !== undefined && unit !== unitStatic) { + throw{ type: "Argument", message: "incompatible types" }; + } + values[unit] = order.length; + order.push(current); + continue; + } + referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify(); + if ( isMin && currentUnified.value < referenceUnified.value || + !isMin && currentUnified.value > referenceUnified.value) { + order[j] = current; + } + } + if (order.length == 1) { + return order[0]; + } + args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? "," : ", "); + return new Anonymous((isMin ? "min" : "max") + "(" + args + ")"); +}; +functionRegistry.addMultiple({ + min: function () { + return minMax(true, arguments); + }, + max: function () { + return minMax(false, arguments); + }, + convert: function (val, unit) { + return val.convertTo(unit.value); + }, + pi: function () { + return new Dimension(Math.PI); + }, + mod: function(a, b) { + return new Dimension(a.value % b.value, a.unit); + }, + pow: function(x, y) { + if (typeof x === "number" && typeof y === "number") { + x = new Dimension(x); + y = new Dimension(y); + } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) { + throw { type: "Argument", message: "arguments must be numbers" }; + } + + return new Dimension(Math.pow(x.value, y.value), x.unit); + }, + percentage: function (n) { + return new Dimension(n.value * 100, '%'); + } +}); + +},{"../tree/anonymous":43,"../tree/dimension":53,"./function-registry":21}],25:[function(require,module,exports){ +var Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + JavaScript = require("../tree/javascript"), + functionRegistry = require("./function-registry"); + +functionRegistry.addMultiple({ + e: function (str) { + return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value); + }, + escape: function (str) { + return new Anonymous( + encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B") + .replace(/\(/g, "%28").replace(/\)/g, "%29")); + }, + replace: function (string, pattern, replacement, flags) { + var result = string.value; + + result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); + return new Quoted(string.quote || '', result, string.escaped); + }, + '%': function (string /* arg, arg, ...*/) { + var args = Array.prototype.slice.call(arguments, 1), + result = string.value; + + for (var i = 0; i < args.length; i++) { + /*jshint loopfunc:true */ + result = result.replace(/%[sda]/i, function(token) { + var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); + return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; + }); + } + result = result.replace(/%%/g, '%'); + return new Quoted(string.quote || '', result, string.escaped); + } +}); + +},{"../tree/anonymous":43,"../tree/javascript":60,"../tree/quoted":70,"./function-registry":21}],26:[function(require,module,exports){ +module.exports = function(environment) { + var Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Expression = require("../tree/expression"), + Quoted = require("../tree/quoted"), + URL = require("../tree/url"), + functionRegistry = require("./function-registry"); + + functionRegistry.add("svg-gradient", function(direction) { + + function throwArgumentDescriptor() { + throw { type: "Argument", + message: "svg-gradient expects direction, start_color [start_position], [color position,]...," + + " end_color [end_position]" }; + } + + if (arguments.length < 3) { + throwArgumentDescriptor(); + } + var stops = Array.prototype.slice.call(arguments, 1), + gradientDirectionSvg, + gradientType = "linear", + rectangleDimension = 'x="0" y="0" width="1" height="1"', + renderEnv = {compress: false}, + returner, + directionValue = direction.toCSS(renderEnv), + i, color, position, positionValue, alpha; + + switch (directionValue) { + case "to bottom": + gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"'; + break; + case "to right": + gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"'; + break; + case "to bottom right": + gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"'; + break; + case "to top right": + gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"'; + break; + case "ellipse": + case "ellipse at center": + gradientType = "radial"; + gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"'; + rectangleDimension = 'x="-50" y="-50" width="101" height="101"'; + break; + default: + throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right'," + + " 'to bottom right', 'to top right' or 'ellipse at center'" }; + } + returner = '' + + '' + + '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>'; + + for (i = 0; i < stops.length; i+= 1) { + if (stops[i] instanceof Expression) { + color = stops[i].value[0]; + position = stops[i].value[1]; + } else { + color = stops[i]; + position = undefined; + } + + if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) { + throwArgumentDescriptor(); + } + positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; + alpha = color.alpha; + returner += ''; + } + returner += '' + + ''; + + returner = encodeURIComponent(returner); + + returner = "data:image/svg+xml," + returner; + return new URL(new Quoted("'" + returner + "'", returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo); + }); +}; + +},{"../tree/color":47,"../tree/dimension":53,"../tree/expression":56,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],27:[function(require,module,exports){ +var Keyword = require("../tree/keyword"), + DetachedRuleset = require("../tree/detached-ruleset"), + Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + Operation = require("../tree/operation"), + functionRegistry = require("./function-registry"); + +var isa = function (n, Type) { + return (n instanceof Type) ? Keyword.True : Keyword.False; + }, + isunit = function (n, unit) { + if (unit === undefined) { + throw { type: "Argument", message: "missing the required second argument to isunit." }; + } + unit = typeof unit.value === "string" ? unit.value : unit; + if (typeof unit !== "string") { + throw { type: "Argument", message: "Second argument to isunit should be a unit or a string." }; + } + return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False; + }, + getItemsFromNode = function(node) { + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + var items = Array.isArray(node.value) ? + node.value : Array(node); + + return items.filter(function(item) { + if (item.type === "Comment") { + return false; + } + return true; + }); + }; +functionRegistry.addMultiple({ + isruleset: function (n) { + return isa(n, DetachedRuleset); + }, + iscolor: function (n) { + return isa(n, Color); + }, + isnumber: function (n) { + return isa(n, Dimension); + }, + isstring: function (n) { + return isa(n, Quoted); + }, + iskeyword: function (n) { + return isa(n, Keyword); + }, + isurl: function (n) { + return isa(n, URL); + }, + ispixel: function (n) { + return isunit(n, 'px'); + }, + ispercentage: function (n) { + return isunit(n, '%'); + }, + isem: function (n) { + return isunit(n, 'em'); + }, + isunit: isunit, + unit: function (val, unit) { + if (!(val instanceof Dimension)) { + throw { type: "Argument", + message: "the first argument to unit must be a number" + + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") }; + } + if (unit) { + if (unit instanceof Keyword) { + unit = unit.value; + } else { + unit = unit.toCSS(); + } + } else { + unit = ""; + } + return new Dimension(val.value, unit); + }, + "get-unit": function (n) { + return new Anonymous(n.unit); + }, + extract: function(values, index) { + index = index.value - 1; // (1-based index) + + return getItemsFromNode(values)[index]; + }, + length: function(values) { + return new Dimension(getItemsFromNode(values).length); + } +}); + +},{"../tree/anonymous":43,"../tree/color":47,"../tree/detached-ruleset":52,"../tree/dimension":53,"../tree/keyword":62,"../tree/operation":68,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],28:[function(require,module,exports){ +var contexts = require("./contexts"), + Parser = require('./parser/parser'); + +module.exports = function(environment) { + + // FileInfo = { + // 'relativeUrls' - option - whether to adjust URL's to be relative + // 'filename' - full resolved filename of current file + // 'rootpath' - path to append to normal URLs for this node + // 'currentDirectory' - path to the current file, absolute + // 'rootFilename' - filename of the base file + // 'entryPath' - absolute path to the entry file + // 'reference' - whether the file should not be output and only output parts that are referenced + + var ImportManager = function(context, rootFileInfo) { + this.rootFilename = rootFileInfo.filename; + this.paths = context.paths || []; // Search paths, when importing + this.contents = {}; // map - filename to contents of all the files + this.contentsIgnoredChars = {}; // map - filename to lines at the beginning of each file to ignore + this.mime = context.mime; + this.error = null; + this.context = context; + // Deprecated? Unused outside of here, could be useful. + this.queue = []; // Files which haven't been imported yet + this.files = {}; // Holds the imported parse trees. + }; + /** + * Add an import to be imported + * @param path - the raw path + * @param tryAppendLessExtension - whether to try appending the less extension (if the path has no extension) + * @param currentFileInfo - the current file info (used for instance to work out relative paths) + * @param importOptions - import options + * @param callback - callback for when it is imported + */ + ImportManager.prototype.push = function (path, tryAppendLessExtension, currentFileInfo, importOptions, callback) { + var importManager = this; + this.queue.push(path); + + var fileParsedFunc = function (e, root, fullPath) { + importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue + + var importedEqualsRoot = fullPath === importManager.rootFilename; + if (importOptions.optional && e) { + callback(null, {rules:[]}, false, null); + } + else { + importManager.files[fullPath] = root; + if (e && !importManager.error) { importManager.error = e; } + callback(e, root, importedEqualsRoot, fullPath); + } + }; + + var newFileInfo = { + relativeUrls: this.context.relativeUrls, + entryPath: currentFileInfo.entryPath, + rootpath: currentFileInfo.rootpath, + rootFilename: currentFileInfo.rootFilename + }; + + var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment); + + if (!fileManager) { + fileParsedFunc({ message: "Could not find a file-manager for " + path }); + return; + } + + if (tryAppendLessExtension) { + path = fileManager.tryAppendLessExtension(path); + } + + var loadFileCallback = function(loadedFile) { + var resolvedFilename = loadedFile.filename, + contents = loadedFile.contents.replace(/^\uFEFF/, ''); + + // Pass on an updated rootpath if path of imported file is relative and file + // is in a (sub|sup) directory + // + // Examples: + // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', + // then rootpath should become 'less/module/nav/' + // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // then rootpath should become 'less/../' + newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename); + if (newFileInfo.relativeUrls) { + newFileInfo.rootpath = fileManager.join( + (importManager.context.rootpath || ""), + fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + + if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath); + } + } + newFileInfo.filename = resolvedFilename; + + var newEnv = new contexts.Parse(importManager.context); + + newEnv.processImports = false; + importManager.contents[resolvedFilename] = contents; + + if (currentFileInfo.reference || importOptions.reference) { + newFileInfo.reference = true; + } + + if (importOptions.inline) { + fileParsedFunc(null, contents, resolvedFilename); + } else { + new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) { + fileParsedFunc(e, root, resolvedFilename); + }); + } + }; + + var promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment, + function(err, loadedFile) { + if (err) { + fileParsedFunc(err); + } else { + loadFileCallback(loadedFile); + } + }); + if (promise) { + promise.then(loadFileCallback, fileParsedFunc); + } + }; + return ImportManager; +}; + +},{"./contexts":10,"./parser/parser":36}],29:[function(require,module,exports){ +module.exports = function(environment, fileManagers) { + var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment; + + var less = { + version: [2, 4, 0], + data: require('./data'), + tree: require('./tree'), + Environment: (Environment = require("./environment/environment")), + AbstractFileManager: require("./environment/abstract-file-manager"), + environment: (environment = new Environment(environment, fileManagers)), + visitors: require('./visitors'), + Parser: require('./parser/parser'), + functions: require('./functions')(environment), + contexts: require("./contexts"), + SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)), + SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput, environment)), + ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)), + ImportManager: (ImportManager = require('./import-manager')(environment)), + render: require("./render")(environment, ParseTree, ImportManager), + parse: require("./parse")(environment, ParseTree, ImportManager), + LessError: require('./less-error'), + transformTree: require('./transform-tree'), + utils: require('./utils'), + PluginManager: require('./plugin-manager'), + logger: require('./logger') + }; + + return less; +}; + +},{"./contexts":10,"./data":12,"./environment/abstract-file-manager":14,"./environment/environment":15,"./functions":22,"./import-manager":28,"./less-error":30,"./logger":31,"./parse":33,"./parse-tree":32,"./parser/parser":36,"./plugin-manager":37,"./render":38,"./source-map-builder":39,"./source-map-output":40,"./transform-tree":41,"./tree":59,"./utils":80,"./visitors":84}],30:[function(require,module,exports){ +var utils = require("./utils"); + +var LessError = module.exports = function LessError(e, importManager, currentFilename) { + + Error.call(this); + + var filename = e.filename || currentFilename; + + if (importManager && filename) { + var input = importManager.contents[filename], + loc = utils.getLocation(e.index, input), + line = loc.line, + col = loc.column, + callLine = e.call && utils.getLocation(e.call, input).line, + lines = input.split('\n'); + + this.type = e.type || 'Syntax'; + this.filename = filename; + this.index = e.index; + this.line = typeof line === 'number' ? line + 1 : null; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; + this.column = col; + this.extract = [ + lines[line - 1], + lines[line], + lines[line + 1] + ]; + } + this.message = e.message; + this.stack = e.stack; +}; + +if (typeof Object.create === 'undefined') { + var F = function () {}; + F.prototype = Error.prototype; + LessError.prototype = new F(); +} else { + LessError.prototype = Object.create(Error.prototype); +} + +LessError.prototype.constructor = LessError; + +},{"./utils":80}],31:[function(require,module,exports){ +module.exports = { + error: function(msg) { + this._fireEvent("error", msg); + }, + warn: function(msg) { + this._fireEvent("warn", msg); + }, + info: function(msg) { + this._fireEvent("info", msg); + }, + debug: function(msg) { + this._fireEvent("debug", msg); + }, + addListener: function(listener) { + this._listeners.push(listener); + }, + removeListener: function(listener) { + for (var i = 0; i < this._listeners.length; i++) { + if (this._listeners[i] === listener) { + this._listeners.splice(i, 1); + return; + } + } + }, + _fireEvent: function(type, msg) { + for (var i = 0; i < this._listeners.length; i++) { + var logFunction = this._listeners[i][type]; + if (logFunction) { + logFunction(msg); + } + } + }, + _listeners: [] +}; + +},{}],32:[function(require,module,exports){ +var LessError = require('./less-error'), + transformTree = require("./transform-tree"), + logger = require("./logger"); + +module.exports = function(SourceMapBuilder) { + var ParseTree = function(root, imports) { + this.root = root; + this.imports = imports; + }; + + ParseTree.prototype.toCSS = function(options) { + var evaldRoot, result = {}, sourceMapBuilder; + try { + evaldRoot = transformTree(this.root, options); + } catch (e) { + throw new LessError(e, this.imports); + } + + try { + var compress = Boolean(options.compress); + if (compress) { + logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css."); + } + + var toCSSOptions = { + compress: compress, + dumpLineNumbers: options.dumpLineNumbers, + strictUnits: Boolean(options.strictUnits), + numPrecision: 8}; + + if (options.sourceMap) { + sourceMapBuilder = new SourceMapBuilder(options.sourceMap); + result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports); + } else { + result.css = evaldRoot.toCSS(toCSSOptions); + } + } catch (e) { + throw new LessError(e, this.imports); + } + + if (options.pluginManager) { + var postProcessors = options.pluginManager.getPostProcessors(); + for (var i = 0; i < postProcessors.length; i++) { + result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports }); + } + } + if (options.sourceMap) { + result.map = sourceMapBuilder.getExternalSourceMap(); + } + + result.imports = []; + for (var file in this.imports.files) { + if (this.imports.files.hasOwnProperty(file) && file !== this.imports.rootFilename) { + result.imports.push(file); + } + } + return result; + }; + return ParseTree; +}; + +},{"./less-error":30,"./logger":31,"./transform-tree":41}],33:[function(require,module,exports){ +var PromiseConstructor, + contexts = require("./contexts"), + Parser = require('./parser/parser'), + PluginManager = require('./plugin-manager'); + +module.exports = function(environment, ParseTree, ImportManager) { + var parse = function (input, options, callback) { + options = options || {}; + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (!callback) { + if (!PromiseConstructor) { + PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; + } + var self = this; + return new PromiseConstructor(function (resolve, reject) { + parse.call(self, input, options, function(err, output) { + if (err) { + reject(err); + } else { + resolve(output); + } + }); + }); + } else { + var context, + rootFileInfo, + pluginManager = new PluginManager(this); + + pluginManager.addPlugins(options.plugins); + options.pluginManager = pluginManager; + + context = new contexts.Parse(options); + + if (options.rootFileInfo) { + rootFileInfo = options.rootFileInfo; + } else { + var filename = options.filename || "input"; + var entryPath = filename.replace(/[^\/\\]*$/, ""); + rootFileInfo = { + filename: filename, + relativeUrls: context.relativeUrls, + rootpath: context.rootpath || "", + currentDirectory: entryPath, + entryPath: entryPath, + rootFilename: filename + }; + // add in a missing trailing slash + if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== "/") { + rootFileInfo.rootpath += "/"; + } + } + + var imports = new ImportManager(context, rootFileInfo); + + new Parser(context, imports, rootFileInfo) + .parse(input, function (e, root) { + if (e) { return callback(e); } + callback(null, root, imports, options); + }, options); + } + }; + return parse; +}; + +},{"./contexts":10,"./parser/parser":36,"./plugin-manager":37,"promise":undefined}],34:[function(require,module,exports){ +// Split the input into chunks. +module.exports = function (input, fail) { + var len = input.length, level = 0, parenLevel = 0, + lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, + chunks = [], emitFrom = 0, + chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched; + + function emitChunk(force) { + var len = chunkerCurrentIndex - emitFrom; + if (((len < 512) && !force) || !len) { + return; + } + chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1)); + emitFrom = chunkerCurrentIndex + 1; + } + + for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc = input.charCodeAt(chunkerCurrentIndex); + if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { + // a-z or whitespace + continue; + } + + switch (cc) { + case 40: // ( + parenLevel++; + lastOpeningParen = chunkerCurrentIndex; + continue; + case 41: // ) + if (--parenLevel < 0) { + return fail("missing opening `(`", chunkerCurrentIndex); + } + continue; + case 59: // ; + if (!parenLevel) { emitChunk(); } + continue; + case 123: // { + level++; + lastOpening = chunkerCurrentIndex; + continue; + case 125: // } + if (--level < 0) { + return fail("missing opening `{`", chunkerCurrentIndex); + } + if (!level && !parenLevel) { emitChunk(); } + continue; + case 92: // \ + if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; } + return fail("unescaped `\\`", chunkerCurrentIndex); + case 34: + case 39: + case 96: // ", ' and ` + matched = 0; + currentChunkStartIndex = chunkerCurrentIndex; + for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if (cc2 > 96) { continue; } + if (cc2 == cc) { matched = 1; break; } + if (cc2 == 92) { // \ + if (chunkerCurrentIndex == len - 1) { + return fail("unescaped `\\`", chunkerCurrentIndex); + } + chunkerCurrentIndex++; + } + } + if (matched) { continue; } + return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); + case 47: // /, check for comment + if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; } + cc2 = input.charCodeAt(chunkerCurrentIndex + 1); + if (cc2 == 47) { + // //, find lnfeed + for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } + } + } else if (cc2 == 42) { + // /*, find */ + lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex; + for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; } + if (cc2 != 42) { continue; } + if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; } + } + if (chunkerCurrentIndex == len - 1) { + return fail("missing closing `*/`", currentChunkStartIndex); + } + chunkerCurrentIndex++; + } + continue; + case 42: // *, check for unmatched */ + if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) { + return fail("unmatched `/*`", chunkerCurrentIndex); + } + continue; + } + } + + if (level !== 0) { + if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { + return fail("missing closing `}` or `*/`", lastOpening); + } else { + return fail("missing closing `}`", lastOpening); + } + } else if (parenLevel !== 0) { + return fail("missing closing `)`", lastOpeningParen); + } + + emitChunk(true); + return chunks; +}; + +},{}],35:[function(require,module,exports){ +var chunker = require('./chunker'); + +module.exports = function() { + var input, // LeSS input string + j, // current chunk + saveStack = [], // holds state for backtracking + furthest, // furthest index the parser has gone to + furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause + chunks, // chunkified input + current, // current chunk + currentPos, // index of current chunk, in `input` + parserInput = {}; + + parserInput.save = function() { + currentPos = parserInput.i; + saveStack.push( { current: current, i: parserInput.i, j: j }); + }; + parserInput.restore = function(possibleErrorMessage) { + + if (parserInput.i > furthest || (parserInput.i === furthest && possibleErrorMessage && !furthestPossibleErrorMessage)) { + furthest = parserInput.i; + furthestPossibleErrorMessage = possibleErrorMessage; + } + var state = saveStack.pop(); + current = state.current; + currentPos = parserInput.i = state.i; + j = state.j; + }; + parserInput.forget = function() { + saveStack.pop(); + }; + function sync() { + if (parserInput.i > currentPos) { + current = current.slice(parserInput.i - currentPos); + currentPos = parserInput.i; + } + } + parserInput.isWhitespace = function (offset) { + var pos = parserInput.i + (offset || 0), + code = input.charCodeAt(pos); + return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF); + }; + // + // Parse from a token, regexp or string, and move forward if match + // + parserInput.$ = function(tok) { + var tokType = typeof tok, + match, length; + + // Either match a single character in the input, + // or match a regexp in the current chunk (`current`). + // + if (tokType === "string") { + if (input.charAt(parserInput.i) !== tok) { + return null; + } + skipWhitespace(1); + return tok; + } + + // regexp + sync(); + if (! (match = tok.exec(current))) { + return null; + } + + length = match[0].length; + + // The match is confirmed, add the match length to `i`, + // and consume any extra white-space characters (' ' || '\n') + // which come after that. The reason for this is that LeSS's + // grammar is mostly white-space insensitive. + // + skipWhitespace(length); + + if (typeof match === 'string') { + return match; + } else { + return match.length === 1 ? match[0] : match; + } + }; + + // Specialization of $(tok) + parserInput.$re = function(tok) { + if (parserInput.i > currentPos) { + current = current.slice(parserInput.i - currentPos); + currentPos = parserInput.i; + } + var m = tok.exec(current); + if (!m) { + return null; + } + + skipWhitespace(m[0].length); + if (typeof m === "string") { + return m; + } + + return m.length === 1 ? m[0] : m; + }; + + // Specialization of $(tok) + parserInput.$char = function(tok) { + if (input.charAt(parserInput.i) !== tok) { + return null; + } + skipWhitespace(1); + return tok; + }; + + var CHARCODE_SPACE = 32, + CHARCODE_TAB = 9, + CHARCODE_LF = 10, + CHARCODE_CR = 13, + CHARCODE_PLUS = 43, + CHARCODE_COMMA = 44, + CHARCODE_FORWARD_SLASH = 47, + CHARCODE_9 = 57; + + parserInput.autoCommentAbsorb = true; + parserInput.commentStore = []; + parserInput.finished = false; + + var skipWhitespace = function(length) { + var oldi = parserInput.i, oldj = j, + curr = parserInput.i - currentPos, + endIndex = parserInput.i + current.length - curr, + mem = (parserInput.i += length), + inp = input, + c, nextChar, comment; + + for (; parserInput.i < endIndex; parserInput.i++) { + c = inp.charCodeAt(parserInput.i); + + if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) { + nextChar = inp.charAt(parserInput.i + 1); + if (nextChar === '/') { + comment = {index: parserInput.i, isLineComment: true}; + var nextNewLine = inp.indexOf("\n", parserInput.i + 1); + if (nextNewLine < 0) { + nextNewLine = endIndex; + } + parserInput.i = nextNewLine; + comment.text = inp.substr(comment.i, parserInput.i - comment.i); + parserInput.commentStore.push(comment); + continue; + } else if (nextChar === '*') { + var haystack = inp.substr(parserInput.i); + var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//); + if (comment_search_result) { + comment = { + index: parserInput.i, + text: comment_search_result[0], + isLineComment: false + }; + parserInput.i += comment.text.length - 1; + parserInput.commentStore.push(comment); + continue; + } + } + break; + } + + if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) { + break; + } + } + + current = current.slice(length + parserInput.i - mem + curr); + currentPos = parserInput.i; + + if (!current.length) { + if (j < chunks.length - 1) + { + current = chunks[++j]; + skipWhitespace(0); // skip space at the beginning of a chunk + return true; // things changed + } + parserInput.finished = true; + } + + return oldi !== parserInput.i || oldj !== j; + }; + + // Same as $(), but don't change the state of the parser, + // just return the match. + parserInput.peek = function(tok) { + if (typeof tok === 'string') { + return input.charAt(parserInput.i) === tok; + } else { + return tok.test(current); + } + }; + + // Specialization of peek() + // TODO remove or change some currentChar calls to peekChar + parserInput.peekChar = function(tok) { + return input.charAt(parserInput.i) === tok; + }; + + parserInput.currentChar = function() { + return input.charAt(parserInput.i); + }; + + parserInput.getInput = function() { + return input; + }; + + parserInput.peekNotNumeric = function() { + var c = input.charCodeAt(parserInput.i); + //Is the first char of the dimension 0-9, '.', '+' or '-' + return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; + }; + + parserInput.start = function(str, chunkInput, failFunction) { + input = str; + parserInput.i = j = currentPos = furthest = 0; + + // chunking apparantly makes things quicker (but my tests indicate + // it might actually make things slower in node at least) + // and it is a non-perfect parse - it can't recognise + // unquoted urls, meaning it can't distinguish comments + // meaning comments with quotes or {}() in them get 'counted' + // and then lead to parse errors. + // In addition if the chunking chunks in the wrong place we might + // not be able to parse a parser statement in one go + // this is officially deprecated but can be switched on via an option + // in the case it causes too much performance issues. + if (chunkInput) { + chunks = chunker(str, failFunction); + } else { + chunks = [str]; + } + + current = chunks[0]; + + skipWhitespace(0); + }; + + parserInput.end = function() { + var message, + isFinished = parserInput.i >= input.length; + + if (parserInput.i < furthest) { + message = furthestPossibleErrorMessage; + parserInput.i = furthest; + } + return { + isFinished: isFinished, + furthest: parserInput.i, + furthestPossibleErrorMessage: message, + furthestReachedEnd: parserInput.i >= input.length - 1, + furthestChar: input[parserInput.i] + }; + }; + + return parserInput; +}; + +},{"./chunker":34}],36:[function(require,module,exports){ +var LessError = require('../less-error'), + tree = require("../tree"), + visitors = require("../visitors"), + getParserInput = require("./parser-input"), + utils = require("../utils"); + +// +// less.js - parser +// +// A relatively straight-forward predictive parser. +// There is no tokenization/lexing stage, the input is parsed +// in one sweep. +// +// To make the parser fast enough to run in the browser, several +// optimization had to be made: +// +// - Matching and slicing on a huge input is often cause of slowdowns. +// The solution is to chunkify the input into smaller strings. +// The chunks are stored in the `chunks` var, +// `j` holds the current chunk index, and `currentPos` holds +// the index of the current chunk in relation to `input`. +// This gives us an almost 4x speed-up. +// +// - In many cases, we don't need to match individual tokens; +// for example, if a value doesn't hold any variables, operations +// or dynamic references, the parser can effectively 'skip' it, +// treating it as a literal. +// An example would be '1px solid #000' - which evaluates to itself, +// we don't need to know what the individual components are. +// The drawback, of course is that you don't get the benefits of +// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, +// and a smaller speed-up in the code-gen. +// +// +// Token matching is done with the `$` function, which either takes +// a terminal string or regexp, or a non-terminal function to call. +// It also takes care of moving all the indices forwards. +// +// +var Parser = function Parser(context, imports, fileInfo) { + var parsers, + parserInput = getParserInput(); + + function expect(arg, msg, index) { + // some older browsers return typeof 'function' for RegExp + var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg); + if (result) { + return result; + } + error(msg || (typeof arg === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'" + : "unexpected token")); + } + + // Specialization of expect() + function expectChar(arg, msg) { + if (parserInput.$char(arg)) { + return arg; + } + error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'"); + } + + function error(msg, type) { + throw new LessError( + { + index: parserInput.i, + filename: fileInfo.filename, + type: type || 'Syntax', + message: msg + }, + imports + ); + } + + function getDebugInfo(index) { + var filename = fileInfo.filename; + + return { + lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, + fileName: filename + }; + } + + // + // The Parser + // + return { + + // + // Parse an input string into an abstract syntax tree, + // @param str A string containing 'less' markup + // @param callback call `callback` when done. + // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply + // + parse: function (str, callback, additionalData) { + var root, error = null, globalVars, modifyVars, ignored, preText = ""; + + globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : ''; + modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : ''; + + if (context.pluginManager) { + var preProcessors = context.pluginManager.getPreProcessors(); + for (var i = 0; i < preProcessors.length; i++) { + str = preProcessors[i].process(str, { context: context, imports: imports, fileInfo: fileInfo }); + } + } + + if (globalVars || (additionalData && additionalData.banner)) { + preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; + ignored = imports.contentsIgnoredChars; + ignored[fileInfo.filename] = ignored[fileInfo.filename] || 0; + ignored[fileInfo.filename] += preText.length; + } + + str = str.replace(/\r\n?/g, '\n'); + // Remove potential UTF Byte Order Mark + str = preText + str.replace(/^\uFEFF/, '') + modifyVars; + imports.contents[fileInfo.filename] = str; + + // Start with the primary rule. + // The whole syntax tree is held under a Ruleset node, + // with the `root` property set to true, so no `{}` are + // output. The callback is called when the input is parsed. + try { + parserInput.start(str, context.chunkInput, function fail(msg, index) { + throw LessError({ + index: index, + type: 'Parse', + message: msg, + filename: fileInfo.filename + }, imports); + }); + + root = new(tree.Ruleset)(null, this.parsers.primary()); + root.root = true; + root.firstRoot = true; + } catch (e) { + return callback(new LessError(e, imports, fileInfo.filename)); + } + + // If `i` is smaller than the `input.length - 1`, + // it means the parser wasn't able to parse the whole + // string, so we've got a parsing error. + // + // We try to extract a \n delimited string, + // showing the line where the parse error occurred. + // We split it up into two parts (the part which parsed, + // and the part which didn't), so we can color them differently. + var endInfo = parserInput.end(); + if (!endInfo.isFinished) { + + var message = endInfo.furthestPossibleErrorMessage; + + if (!message) { + message = "Unrecognised input"; + if (endInfo.furthestChar === '}') { + message += ". Possibly missing opening '{'"; + } else if (endInfo.furthestChar === ')') { + message += ". Possibly missing opening '('"; + } else if (endInfo.furthestReachedEnd) { + message += ". Possibly missing something"; + } + } + + error = new LessError({ + type: "Parse", + message: message, + index: endInfo.furthest, + filename: fileInfo.filename + }, imports); + } + + var finish = function (e) { + e = error || e || imports.error; + + if (e) { + if (!(e instanceof LessError)) { + e = new LessError(e, imports, fileInfo.filename); + } + + return callback(e); + } + else { + return callback(null, root); + } + }; + + if (context.processImports !== false) { + new visitors.ImportVisitor(imports, finish) + .run(root); + } else { + return finish(); + } + }, + + // + // Here in, the parsing rules/functions + // + // The basic structure of the syntax tree generated is as follows: + // + // Ruleset -> Rule -> Value -> Expression -> Entity + // + // Here's some Less code: + // + // .class { + // color: #fff; + // border: 1px solid #000; + // width: @w + 4px; + // > .child {...} + // } + // + // And here's what the parse tree might look like: + // + // Ruleset (Selector '.class', [ + // Rule ("color", Value ([Expression [Color #fff]])) + // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]])) + // Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]])) + // Ruleset (Selector [Element '>', '.child'], [...]) + // ]) + // + // In general, most rules will try to parse a token with the `$()` function, and if the return + // value is truly, will return a new node, of the relevant type. Sometimes, we need to check + // first, before parsing, that's when we use `peek()`. + // + parsers: parsers = { + // + // The `primary` rule is the *entry* and *exit* point of the parser. + // The rules here can appear at any level of the parse tree. + // + // The recursive nature of the grammar is an interplay between the `block` + // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, + // as represented by this simplified grammar: + // + // primary → (ruleset | rule)+ + // ruleset → selector+ block + // block → '{' primary '}' + // + // Only at one point is the primary rule not called from the + // block rule: at the root level. + // + primary: function () { + var mixin = this.mixin, root = [], node; + + while (true) + { + while (true) { + node = this.comment(); + if (!node) { break; } + root.push(node); + } + // always process comments before deciding if finished + if (parserInput.finished) { + break; + } + if (parserInput.peek('}')) { + break; + } + + node = this.extendRule(); + if (node) { + root = root.concat(node); + continue; + } + + node = mixin.definition() || this.rule() || this.ruleset() || + mixin.call() || this.rulesetCall() || this.directive(); + if (node) { + root.push(node); + } else { + if (!(parserInput.$re(/^[\s\n]+/) || parserInput.$re(/^;+/))) { + break; + } + } + } + + return root; + }, + + // comments are collected by the main parsing mechanism and then assigned to nodes + // where the current structure allows it + comment: function () { + if (parserInput.commentStore.length) { + var comment = parserInput.commentStore.shift(); + return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo); + } + }, + + // + // Entities are tokens which can be found inside an Expression + // + entities: { + // + // A string, which supports escaping " and ' + // + // "milky way" 'he\'s the one!' + // + quoted: function () { + var str, index = parserInput.i; + + str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/); + if (str) { + return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, fileInfo); + } + }, + + // + // A catch-all word, such as: + // + // black border-collapse + // + keyword: function () { + var k = parserInput.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); + if (k) { + return tree.Color.fromKeyword(k) || new(tree.Keyword)(k); + } + }, + + // + // A function call + // + // rgb(255, 0, 255) + // + // We also try to catch IE's `alpha()`, but let the `alpha` parser + // deal with the details. + // + // The arguments are parsed with the `entities.arguments` parser. + // + call: function () { + var name, nameLC, args, alpha, index = parserInput.i; + + if (parserInput.peek(/^url\(/i)) { + return; + } + + parserInput.save(); + + name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/); + if (!name) { parserInput.forget(); return; } + + name = name[1]; + nameLC = name.toLowerCase(); + + if (nameLC === 'alpha') { + alpha = parsers.alpha(); + if (alpha) { + return alpha; + } + } + + args = this.arguments(); + + if (! parserInput.$char(')')) { + parserInput.restore("Could not parse call arguments or missing ')'"); + return; + } + + parserInput.forget(); + return new(tree.Call)(name, args, index, fileInfo); + }, + arguments: function () { + var args = [], arg; + + while (true) { + arg = this.assignment() || parsers.expression(); + if (!arg) { + break; + } + args.push(arg); + if (! parserInput.$char(',')) { + break; + } + } + return args; + }, + literal: function () { + return this.dimension() || + this.color() || + this.quoted() || + this.unicodeDescriptor(); + }, + + // Assignments are argument entities for calls. + // They are present in ie filter properties as shown below. + // + // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) + // + + assignment: function () { + var key, value; + key = parserInput.$re(/^\w+(?=\s?=)/i); + if (!key) { + return; + } + if (!parserInput.$char('=')) { + return; + } + value = parsers.entity(); + if (value) { + return new(tree.Assignment)(key, value); + } + }, + + // + // Parse url() tokens + // + // We use a specific rule for urls, because they don't really behave like + // standard function calls. The difference is that the argument doesn't have + // to be enclosed within a string, so it can't be parsed as an Expression. + // + url: function () { + var value, index = parserInput.i; + + parserInput.autoCommentAbsorb = false; + + if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) { + parserInput.autoCommentAbsorb = true; + return; + } + + value = this.quoted() || this.variable() || + parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; + + parserInput.autoCommentAbsorb = true; + + expectChar(')'); + + return new(tree.URL)((value.value != null || value instanceof tree.Variable) ? + value : new(tree.Anonymous)(value), index, fileInfo); + }, + + // + // A Variable entity, such as `@fink`, in + // + // width: @fink + 2px + // + // We use a different parser for variable definitions, + // see `parsers.variable`. + // + variable: function () { + var name, index = parserInput.i; + + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) { + return new(tree.Variable)(name, index, fileInfo); + } + }, + + // A variable entity useing the protective {} e.g. @{var} + variableCurly: function () { + var curly, index = parserInput.i; + + if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) { + return new(tree.Variable)("@" + curly[1], index, fileInfo); + } + }, + + // + // A Hexadecimal color + // + // #4F3C2F + // + // `rgb` and `hsl` colors are parsed through the `entities.call` parser. + // + color: function () { + var rgb; + + if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { + // strip colons, brackets, whitespaces and other characters that should not + // definitely be part of color string + var colorCandidateString = rgb.input.match(/^#([\w]+).*/); + colorCandidateString = colorCandidateString[1]; + if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters + error("Invalid HEX color code"); + } + return new(tree.Color)(rgb[1]); + } + }, + + // + // A Dimension, that is, a number and a unit + // + // 0.5em 95% + // + dimension: function () { + if (parserInput.peekNotNumeric()) { + return; + } + + var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i); + if (value) { + return new(tree.Dimension)(value[1], value[2]); + } + }, + + // + // A unicode descriptor, as is used in unicode-range + // + // U+0?? or U+00A1-00A9 + // + unicodeDescriptor: function () { + var ud; + + ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); + if (ud) { + return new(tree.UnicodeDescriptor)(ud[0]); + } + }, + + // + // JavaScript code to be evaluated + // + // `window.location.href` + // + javascript: function () { + var js, index = parserInput.i; + + js = parserInput.$re(/^(~)?`([^`]*)`/); + if (js) { + return new(tree.JavaScript)(js[2], Boolean(js[1]), index, fileInfo); + } + } + }, + + // + // The variable part of a variable definition. Used in the `rule` parser + // + // @fink: + // + variable: function () { + var name; + + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; } + }, + + // + // The variable part of a variable definition. Used in the `rule` parser + // + // @fink(); + // + rulesetCall: function () { + var name; + + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { + return new tree.RulesetCall(name[1]); + } + }, + + // + // extend syntax - used to extend selectors + // + extend: function(isRule) { + var elements, e, index = parserInput.i, option, extendList, extend; + + if (!(isRule ? parserInput.$re(/^&:extend\(/) : parserInput.$re(/^:extend\(/))) { return; } + + do { + option = null; + elements = null; + while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) { + e = this.element(); + if (!e) { + break; + } + if (elements) { + elements.push(e); + } else { + elements = [ e ]; + } + } + + option = option && option[1]; + if (!elements) { + error("Missing target selector for :extend()."); + } + extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); + if (extendList) { + extendList.push(extend); + } else { + extendList = [ extend ]; + } + } while (parserInput.$char(",")); + + expect(/^\)/); + + if (isRule) { + expect(/^;/); + } + + return extendList; + }, + + // + // extendRule - used in a rule to extend all the parent selectors + // + extendRule: function() { + return this.extend(true); + }, + + // + // Mixins + // + mixin: { + // + // A Mixin call, with an optional argument list + // + // #mixins > .square(#fff); + // .rounded(4px, black); + // .button; + // + // The `while` loop is there because mixins can be + // namespaced, but we only support the child and descendant + // selector for now. + // + call: function () { + var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex, + elements, elem, e, c, args; + + if (s !== '.' && s !== '#') { return; } + + parserInput.save(); // stop us absorbing part of an invalid selector + + while (true) { + elemIndex = parserInput.i; + e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); + if (!e) { + break; + } + elem = new(tree.Element)(c, e, elemIndex, fileInfo); + if (elements) { + elements.push(elem); + } else { + elements = [ elem ]; + } + c = parserInput.$char('>'); + } + + if (elements) { + if (parserInput.$char('(')) { + args = this.args(true).args; + expectChar(')'); + } + + if (parsers.important()) { + important = true; + } + + if (parsers.end()) { + parserInput.forget(); + return new(tree.mixin.Call)(elements, args, index, fileInfo, important); + } + } + + parserInput.restore(); + }, + args: function (isCall) { + var entities = parsers.entities, + returner = { args:null, variadic: false }, + expressions = [], argsSemiColon = [], argsComma = [], + isSemiColonSeparated, expressionContainsNamed, name, nameLoop, value, arg; + + parserInput.save(); + + while (true) { + if (isCall) { + arg = parsers.detachedRuleset() || parsers.expression(); + } else { + parserInput.commentStore.length = 0; + if (parserInput.currentChar() === '.' && parserInput.$re(/^\.{3}/)) { + returner.variadic = true; + if (parserInput.$char(";") && !isSemiColonSeparated) { + isSemiColonSeparated = true; + } + (isSemiColonSeparated ? argsSemiColon : argsComma) + .push({ variadic: true }); + break; + } + arg = entities.variable() || entities.literal() || entities.keyword(); + } + + if (!arg) { + break; + } + + nameLoop = null; + if (arg.throwAwayComments) { + arg.throwAwayComments(); + } + value = arg; + var val = null; + + if (isCall) { + // Variable + if (arg.value && arg.value.length == 1) { + val = arg.value[0]; + } + } else { + val = arg; + } + + if (val && val instanceof tree.Variable) { + if (parserInput.$char(':')) { + if (expressions.length > 0) { + if (isSemiColonSeparated) { + error("Cannot mix ; and , as delimiter types"); + } + expressionContainsNamed = true; + } + + // we do not support setting a ruleset as a default variable - it doesn't make sense + // However if we do want to add it, there is nothing blocking it, just don't error + // and remove isCall dependency below + value = (isCall && parsers.detachedRuleset()) || parsers.expression(); + + if (!value) { + if (isCall) { + error("could not understand value for named argument"); + } else { + parserInput.restore(); + returner.args = []; + return returner; + } + } + nameLoop = (name = val.name); + } else if (!isCall && parserInput.$re(/^\.{3}/)) { + returner.variadic = true; + if (parserInput.$char(";") && !isSemiColonSeparated) { + isSemiColonSeparated = true; + } + (isSemiColonSeparated ? argsSemiColon : argsComma) + .push({ name: arg.name, variadic: true }); + break; + } else if (!isCall) { + name = nameLoop = val.name; + value = null; + } + } + + if (value) { + expressions.push(value); + } + + argsComma.push({ name:nameLoop, value:value }); + + if (parserInput.$char(',')) { + continue; + } + + if (parserInput.$char(';') || isSemiColonSeparated) { + + if (expressionContainsNamed) { + error("Cannot mix ; and , as delimiter types"); + } + + isSemiColonSeparated = true; + + if (expressions.length > 1) { + value = new(tree.Value)(expressions); + } + argsSemiColon.push({ name:name, value:value }); + + name = null; + expressions = []; + expressionContainsNamed = false; + } + } + + parserInput.forget(); + returner.args = isSemiColonSeparated ? argsSemiColon : argsComma; + return returner; + }, + // + // A Mixin definition, with a list of parameters + // + // .rounded (@radius: 2px, @color) { + // ... + // } + // + // Until we have a finer grained state-machine, we have to + // do a look-ahead, to make sure we don't have a mixin call. + // See the `rule` function for more information. + // + // We start by matching `.rounded (`, and then proceed on to + // the argument list, which has optional default values. + // We store the parameters in `params`, with a `value` key, + // if there is a value, such as in the case of `@radius`. + // + // Once we've got our params list, and a closing `)`, we parse + // the `{...}` block. + // + definition: function () { + var name, params = [], match, ruleset, cond, variadic = false; + if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') || + parserInput.peek(/^[^{]*\}/)) { + return; + } + + parserInput.save(); + + match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); + if (match) { + name = match[1]; + + var argInfo = this.args(false); + params = argInfo.args; + variadic = argInfo.variadic; + + // .mixincall("@{a}"); + // looks a bit like a mixin definition.. + // also + // .mixincall(@a: {rule: set;}); + // so we have to be nice and restore + if (!parserInput.$char(')')) { + parserInput.restore("Missing closing ')'"); + return; + } + + parserInput.commentStore.length = 0; + + if (parserInput.$re(/^when/)) { // Guard + cond = expect(parsers.conditions, 'expected condition'); + } + + ruleset = parsers.block(); + + if (ruleset) { + parserInput.forget(); + return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); + } else { + parserInput.restore(); + } + } else { + parserInput.forget(); + } + } + }, + + // + // Entities are the smallest recognized token, + // and can be found inside a rule's value. + // + entity: function () { + var entities = this.entities; + + return this.comment() || entities.literal() || entities.variable() || entities.url() || + entities.call() || entities.keyword() || entities.javascript(); + }, + + // + // A Rule terminator. Note that we use `peek()` to check for '}', + // because the `block` rule will be expecting it, but we still need to make sure + // it's there, if ';' was ommitted. + // + end: function () { + return parserInput.$char(';') || parserInput.peek('}'); + }, + + // + // IE's alpha function + // + // alpha(opacity=88) + // + alpha: function () { + var value; + + if (! parserInput.$re(/^opacity=/i)) { return; } + value = parserInput.$re(/^\d+/); + if (!value) { + value = expect(this.entities.variable, "Could not parse alpha"); + } + expectChar(')'); + return new(tree.Alpha)(value); + }, + + // + // A Selector Element + // + // div + // + h1 + // #socks + // input[type="text"] + // + // Elements are the building blocks for Selectors, + // they are made out of a `Combinator` (see combinator rule), + // and an element name, such as a tag a class, or `*`. + // + element: function () { + var e, c, v, index = parserInput.i; + + c = this.combinator(); + + e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || + parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || + parserInput.$char('*') || parserInput.$char('&') || this.attribute() || + parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) || + this.entities.variableCurly(); + + if (! e) { + parserInput.save(); + if (parserInput.$char('(')) { + if ((v = this.selector()) && parserInput.$char(')')) { + e = new(tree.Paren)(v); + parserInput.forget(); + } else { + parserInput.restore("Missing closing ')'"); + } + } else { + parserInput.forget(); + } + } + + if (e) { return new(tree.Element)(c, e, index, fileInfo); } + }, + + // + // Combinators combine elements together, in a Selector. + // + // Because our parser isn't white-space sensitive, special care + // has to be taken, when parsing the descendant combinator, ` `, + // as it's an empty space. We have to check the previous character + // in the input, to see if it's a ` ` character. More info on how + // we deal with this in *combinator.js*. + // + combinator: function () { + var c = parserInput.currentChar(); + + if (c === '/') { + parserInput.save(); + var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i); + if (slashedCombinator) { + parserInput.forget(); + return new(tree.Combinator)(slashedCombinator); + } + parserInput.restore(); + } + + if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { + parserInput.i++; + if (c === '^' && parserInput.currentChar() === '^') { + c = '^^'; + parserInput.i++; + } + while (parserInput.isWhitespace()) { parserInput.i++; } + return new(tree.Combinator)(c); + } else if (parserInput.isWhitespace(-1)) { + return new(tree.Combinator)(" "); + } else { + return new(tree.Combinator)(null); + } + }, + // + // A CSS selector (see selector below) + // with less extensions e.g. the ability to extend and guard + // + lessSelector: function () { + return this.selector(true); + }, + // + // A CSS Selector + // + // .class > div + h1 + // li a:hover + // + // Selectors are made out of one or more Elements, see above. + // + selector: function (isLess) { + var index = parserInput.i, elements, extendList, c, e, allExtends, when, condition; + + while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$re(/^when/))) || (e = this.element())) { + if (when) { + condition = expect(this.conditions, 'expected condition'); + } else if (condition) { + error("CSS guard can only be used at the end of selector"); + } else if (extendList) { + if (allExtends) { + allExtends = allExtends.concat(extendList); + } else { + allExtends = extendList; + } + } else { + if (allExtends) { error("Extend can only be used at the end of selector"); } + c = parserInput.currentChar(); + if (elements) { + elements.push(e); + } else { + elements = [ e ]; + } + e = null; + } + if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { + break; + } + } + + if (elements) { return new(tree.Selector)(elements, allExtends, condition, index, fileInfo); } + if (allExtends) { error("Extend must be used to extend a selector, it cannot be used on its own"); } + }, + attribute: function () { + if (! parserInput.$char('[')) { return; } + + var entities = this.entities, + key, val, op; + + if (!(key = entities.variableCurly())) { + key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); + } + + op = parserInput.$re(/^[|~*$^]?=/); + if (op) { + val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly(); + } + + expectChar(']'); + + return new(tree.Attribute)(key, op, val); + }, + + // + // The `block` rule is used by `ruleset` and `mixin.definition`. + // It's a wrapper around the `primary` rule, with added `{}`. + // + block: function () { + var content; + if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) { + return content; + } + }, + + blockRuleset: function() { + var block = this.block(); + + if (block) { + block = new tree.Ruleset(null, block); + } + return block; + }, + + detachedRuleset: function() { + var blockRuleset = this.blockRuleset(); + if (blockRuleset) { + return new tree.DetachedRuleset(blockRuleset); + } + }, + + // + // div, .class, body > p {...} + // + ruleset: function () { + var selectors, s, rules, debugInfo; + + parserInput.save(); + + if (context.dumpLineNumbers) { + debugInfo = getDebugInfo(parserInput.i); + } + + while (true) { + s = this.lessSelector(); + if (!s) { + break; + } + if (selectors) { + selectors.push(s); + } else { + selectors = [ s ]; + } + parserInput.commentStore.length = 0; + if (s.condition && selectors.length > 1) { + error("Guards are only currently allowed on a single selector."); + } + if (! parserInput.$char(',')) { break; } + if (s.condition) { + error("Guards are only currently allowed on a single selector."); + } + parserInput.commentStore.length = 0; + } + + if (selectors && (rules = this.block())) { + parserInput.forget(); + var ruleset = new(tree.Ruleset)(selectors, rules, context.strictImports); + if (context.dumpLineNumbers) { + ruleset.debugInfo = debugInfo; + } + return ruleset; + } else { + parserInput.restore(); + } + }, + rule: function (tryAnonymous) { + var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable; + + if (c === '.' || c === '#' || c === '&') { return; } + + parserInput.save(); + + name = this.variable() || this.ruleProperty(); + if (name) { + isVariable = typeof name === "string"; + + if (isVariable) { + value = this.detachedRuleset(); + } + + parserInput.commentStore.length = 0; + if (!value) { + // a name returned by this.ruleProperty() is always an array of the form: + // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"] + // where each item is a tree.Keyword or tree.Variable + merge = !isVariable && name.pop().value; + + // prefer to try to parse first if its a variable or we are compressing + // but always fallback on the other one + var tryValueFirst = !tryAnonymous && (context.compress || isVariable); + + if (tryValueFirst) { + value = this.value(); + } + if (!value) { + value = this.anonymousValue(); + if (value) { + parserInput.forget(); + // anonymous values absorb the end ';' which is reequired for them to work + return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo); + } + } + if (!tryValueFirst && !value) { + value = this.value(); + } + + important = this.important(); + } + + if (value && this.end()) { + parserInput.forget(); + return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo); + } else { + parserInput.restore(); + if (value && !tryAnonymous) { + return this.rule(true); + } + } + } else { + parserInput.forget(); + } + }, + anonymousValue: function () { + var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/); + if (match) { + return new(tree.Anonymous)(match[1]); + } + }, + + // + // An @import directive + // + // @import "lib"; + // + // Depending on our environment, importing is done differently: + // In the browser, it's an XHR request, in Node, it would be a + // file-system operation. The function used for importing is + // stored in `import`, which we pass to the Import constructor. + // + "import": function () { + var path, features, index = parserInput.i; + + var dir = parserInput.$re(/^@import?\s+/); + + if (dir) { + var options = (dir ? this.importOptions() : null) || {}; + + if ((path = this.entities.quoted() || this.entities.url())) { + features = this.mediaFeatures(); + + if (!parserInput.$(';')) { + parserInput.i = index; + error("missing semi-colon or unrecognised media features on import"); + } + features = features && new(tree.Value)(features); + return new(tree.Import)(path, features, options, index, fileInfo); + } + else + { + parserInput.i = index; + error("malformed import statement"); + } + } + }, + + importOptions: function() { + var o, options = {}, optionName, value; + + // list of options, surrounded by parens + if (! parserInput.$char('(')) { return null; } + do { + o = this.importOption(); + if (o) { + optionName = o; + value = true; + switch(optionName) { + case "css": + optionName = "less"; + value = false; + break; + case "once": + optionName = "multiple"; + value = false; + break; + } + options[optionName] = value; + if (! parserInput.$char(',')) { break; } + } + } while (o); + expectChar(')'); + return options; + }, + + importOption: function() { + var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/); + if (opt) { + return opt[1]; + } + }, + + mediaFeature: function () { + var entities = this.entities, nodes = [], e, p; + parserInput.save(); + do { + e = entities.keyword() || entities.variable(); + if (e) { + nodes.push(e); + } else if (parserInput.$char('(')) { + p = this.property(); + e = this.value(); + if (parserInput.$char(')')) { + if (p && e) { + nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true))); + } else if (e) { + nodes.push(new(tree.Paren)(e)); + } else { + parserInput.restore("badly formed media feature definition"); + return null; + } + } else { + parserInput.restore("Missing closing ')'"); + return null; + } + } + } while (e); + + parserInput.forget(); + if (nodes.length > 0) { + return new(tree.Expression)(nodes); + } + }, + + mediaFeatures: function () { + var entities = this.entities, features = [], e; + do { + e = this.mediaFeature(); + if (e) { + features.push(e); + if (! parserInput.$char(',')) { break; } + } else { + e = entities.variable(); + if (e) { + features.push(e); + if (! parserInput.$char(',')) { break; } + } + } + } while (e); + + return features.length > 0 ? features : null; + }, + + media: function () { + var features, rules, media, debugInfo; + + if (context.dumpLineNumbers) { + debugInfo = getDebugInfo(parserInput.i); + } + + if (parserInput.$re(/^@media/)) { + features = this.mediaFeatures(); + + rules = this.block(); + if (rules) { + media = new(tree.Media)(rules, features, parserInput.i, fileInfo); + if (context.dumpLineNumbers) { + media.debugInfo = debugInfo; + } + return media; + } + } + }, + + // + // A CSS Directive + // + // @charset "utf-8"; + // + directive: function () { + var index = parserInput.i, name, value, rules, nonVendorSpecificName, + hasIdentifier, hasExpression, hasUnknown, hasBlock = true; + + if (parserInput.currentChar() !== '@') { return; } + + value = this['import']() || this.media(); + if (value) { + return value; + } + + parserInput.save(); + + name = parserInput.$re(/^@[a-z-]+/); + + if (!name) { return; } + + nonVendorSpecificName = name; + if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { + nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); + } + + switch(nonVendorSpecificName) { + /* + case "@font-face": + case "@viewport": + case "@top-left": + case "@top-left-corner": + case "@top-center": + case "@top-right": + case "@top-right-corner": + case "@bottom-left": + case "@bottom-left-corner": + case "@bottom-center": + case "@bottom-right": + case "@bottom-right-corner": + case "@left-top": + case "@left-middle": + case "@left-bottom": + case "@right-top": + case "@right-middle": + case "@right-bottom": + hasBlock = true; + break; + */ + case "@counter-style": + hasIdentifier = true; + hasBlock = true; + break; + case "@charset": + hasIdentifier = true; + hasBlock = false; + break; + case "@namespace": + hasExpression = true; + hasBlock = false; + break; + case "@keyframes": + hasIdentifier = true; + break; + case "@host": + case "@page": + case "@document": + case "@supports": + hasUnknown = true; + break; + } + + parserInput.commentStore.length = 0; + + if (hasIdentifier) { + value = this.entity(); + if (!value) { + error("expected " + name + " identifier"); + } + } else if (hasExpression) { + value = this.expression(); + if (!value) { + error("expected " + name + " expression"); + } + } else if (hasUnknown) { + value = (parserInput.$re(/^[^{;]+/) || '').trim(); + if (value) { + value = new(tree.Anonymous)(value); + } + } + + if (hasBlock) { + rules = this.blockRuleset(); + } + + if (rules || (!hasBlock && value && parserInput.$char(';'))) { + parserInput.forget(); + return new(tree.Directive)(name, value, rules, index, fileInfo, + context.dumpLineNumbers ? getDebugInfo(index) : null); + } + + parserInput.restore("directive options not recognised"); + }, + + // + // A Value is a comma-delimited list of Expressions + // + // font-family: Baskerville, Georgia, serif; + // + // In a Rule, a Value represents everything after the `:`, + // and before the `;`. + // + value: function () { + var e, expressions = []; + + do { + e = this.expression(); + if (e) { + expressions.push(e); + if (! parserInput.$char(',')) { break; } + } + } while (e); + + if (expressions.length > 0) { + return new(tree.Value)(expressions); + } + }, + important: function () { + if (parserInput.currentChar() === '!') { + return parserInput.$re(/^! *important/); + } + }, + sub: function () { + var a, e; + + parserInput.save(); + if (parserInput.$char('(')) { + a = this.addition(); + if (a && parserInput.$char(')')) { + parserInput.forget(); + e = new(tree.Expression)([a]); + e.parens = true; + return e; + } + parserInput.restore("Expected ')'"); + return; + } + parserInput.restore(); + }, + multiplication: function () { + var m, a, op, operation, isSpaced; + m = this.operand(); + if (m) { + isSpaced = parserInput.isWhitespace(-1); + while (true) { + if (parserInput.peek(/^\/[*\/]/)) { + break; + } + + parserInput.save(); + + op = parserInput.$char('/') || parserInput.$char('*'); + + if (!op) { parserInput.forget(); break; } + + a = this.operand(); + + if (!a) { parserInput.restore(); break; } + parserInput.forget(); + + m.parensInOp = true; + a.parensInOp = true; + operation = new(tree.Operation)(op, [operation || m, a], isSpaced); + isSpaced = parserInput.isWhitespace(-1); + } + return operation || m; + } + }, + addition: function () { + var m, a, op, operation, isSpaced; + m = this.multiplication(); + if (m) { + isSpaced = parserInput.isWhitespace(-1); + while (true) { + op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-'))); + if (!op) { + break; + } + a = this.multiplication(); + if (!a) { + break; + } + + m.parensInOp = true; + a.parensInOp = true; + operation = new(tree.Operation)(op, [operation || m, a], isSpaced); + isSpaced = parserInput.isWhitespace(-1); + } + return operation || m; + } + }, + conditions: function () { + var a, b, index = parserInput.i, condition; + + a = this.condition(); + if (a) { + while (true) { + if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) { + break; + } + b = this.condition(); + if (!b) { + break; + } + condition = new(tree.Condition)('or', condition || a, b, index); + } + return condition || a; + } + }, + condition: function () { + var entities = this.entities, index = parserInput.i, negate = false, + a, b, c, op; + + if (parserInput.$re(/^not/)) { negate = true; } + expectChar('('); + a = this.addition() || entities.keyword() || entities.quoted(); + if (a) { + op = parserInput.$re(/^(?:>=|<=|=<|[<=>])/); + if (op) { + b = this.addition() || entities.keyword() || entities.quoted(); + if (b) { + c = new(tree.Condition)(op, a, b, index, negate); + } else { + error('expected expression'); + } + } else { + c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); + } + expectChar(')'); + return parserInput.$re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; + } + }, + + // + // An operand is anything that can be part of an operation, + // such as a Color, or a Variable + // + operand: function () { + var entities = this.entities, negate; + + if (parserInput.peek(/^-[@\(]/)) { + negate = parserInput.$char('-'); + } + + var o = this.sub() || entities.dimension() || + entities.color() || entities.variable() || + entities.call(); + + if (negate) { + o.parensInOp = true; + o = new(tree.Negative)(o); + } + + return o; + }, + + // + // Expressions either represent mathematical operations, + // or white-space delimited Entities. + // + // 1px solid black + // @var * 2 + // + expression: function () { + var entities = [], e, delim; + + do { + e = this.comment(); + if (e) { + entities.push(e); + continue; + } + e = this.addition() || this.entity(); + if (e) { + entities.push(e); + // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here + if (!parserInput.peek(/^\/[\/*]/)) { + delim = parserInput.$char('/'); + if (delim) { + entities.push(new(tree.Anonymous)(delim)); + } + } + } + } while (e); + if (entities.length > 0) { + return new(tree.Expression)(entities); + } + }, + property: function () { + var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); + if (name) { + return name[1]; + } + }, + ruleProperty: function () { + var name = [], index = [], s, k; + + parserInput.save(); + + function match(re) { + var i = parserInput.i, + chunk = parserInput.$re(re); + if (chunk) { + index.push(i); + return name.push(chunk[1]); + } + } + + match(/^(\*?)/); + while (true) { + if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) { + break; + } + } + + if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) { + parserInput.forget(); + + // at last, we have the complete match now. move forward, + // convert name particles to tree objects and return: + if (name[0] === '') { + name.shift(); + index.shift(); + } + for (k = 0; k < name.length; k++) { + s = name[k]; + name[k] = (s.charAt(0) !== '@') ? + new(tree.Keyword)(s) : + new(tree.Variable)('@' + s.slice(2, -1), + index[k], fileInfo); + } + return name; + } + parserInput.restore(); + } + } + }; +}; +Parser.serializeVars = function(vars) { + var s = ''; + + for (var name in vars) { + if (Object.hasOwnProperty.call(vars, name)) { + var value = vars[name]; + s += ((name[0] === '@') ? '' : '@') + name + ': ' + value + + ((String(value).slice(-1) === ';') ? '' : ';'); + } + } + + return s; +}; + +module.exports = Parser; + +},{"../less-error":30,"../tree":59,"../utils":80,"../visitors":84,"./parser-input":35}],37:[function(require,module,exports){ +/** + * Plugin Manager + */ +var PluginManager = function(less) { + this.less = less; + this.visitors = []; + this.preProcessors = []; + this.postProcessors = []; + this.installedPlugins = []; + this.fileManagers = []; +}; +/** + * Adds all the plugins in the array + * @param {Array} plugins + */ +PluginManager.prototype.addPlugins = function(plugins) { + if (plugins) { + for (var i = 0; i < plugins.length; i++) { + this.addPlugin(plugins[i]); + } + } +}; +/** + * + * @param plugin + */ +PluginManager.prototype.addPlugin = function(plugin) { + this.installedPlugins.push(plugin); + plugin.install(this.less, this); +}; +/** + * Adds a visitor. The visitor object has options on itself to determine + * when it should run. + * @param visitor + */ +PluginManager.prototype.addVisitor = function(visitor) { + this.visitors.push(visitor); +}; +/** + * Adds a pre processor object + * @param {object} preProcessor + * @param {number} priority - guidelines 1 = before import, 1000 = import, 2000 = after import + */ +PluginManager.prototype.addPreProcessor = function(preProcessor, priority) { + var indexToInsertAt; + for (indexToInsertAt = 0; indexToInsertAt < this.preProcessors.length; indexToInsertAt++) { + if (this.preProcessors[indexToInsertAt].priority >= priority) { + break; + } + } + this.preProcessors.splice(indexToInsertAt, 0, {preProcessor: preProcessor, priority: priority}); +}; +/** + * Adds a post processor object + * @param {object} postProcessor + * @param {number} priority - guidelines 1 = before compression, 1000 = compression, 2000 = after compression + */ +PluginManager.prototype.addPostProcessor = function(postProcessor, priority) { + var indexToInsertAt; + for (indexToInsertAt = 0; indexToInsertAt < this.postProcessors.length; indexToInsertAt++) { + if (this.postProcessors[indexToInsertAt].priority >= priority) { + break; + } + } + this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority}); +}; +/** + * + * @param manager + */ +PluginManager.prototype.addFileManager = function(manager) { + this.fileManagers.push(manager); +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getPreProcessors = function() { + var preProcessors = []; + for (var i = 0; i < this.preProcessors.length; i++) { + preProcessors.push(this.preProcessors[i].preProcessor); + } + return preProcessors; +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getPostProcessors = function() { + var postProcessors = []; + for (var i = 0; i < this.postProcessors.length; i++) { + postProcessors.push(this.postProcessors[i].postProcessor); + } + return postProcessors; +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getVisitors = function() { + return this.visitors; +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getFileManagers = function() { + return this.fileManagers; +}; +module.exports = PluginManager; + +},{}],38:[function(require,module,exports){ +var PromiseConstructor; + +module.exports = function(environment, ParseTree, ImportManager) { + var render = function (input, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (!callback) { + if (!PromiseConstructor) { + PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; + } + var self = this; + return new PromiseConstructor(function (resolve, reject) { + render.call(self, input, options, function(err, output) { + if (err) { + reject(err); + } else { + resolve(output); + } + }); + }); + } else { + this.parse(input, options, function(err, root, imports, options) { + if (err) { return callback(err); } + + var result; + try { + var parseTree = new ParseTree(root, imports); + result = parseTree.toCSS(options); + } + catch (err) { return callback(err); } + + callback(null, result); + }); + } + }; + + return render; +}; + +},{"promise":undefined}],39:[function(require,module,exports){ +module.exports = function (SourceMapOutput, environment) { + + var SourceMapBuilder = function (options) { + this.options = options; + }; + + SourceMapBuilder.prototype.toCSS = function(rootNode, options, imports) { + var sourceMapOutput = new SourceMapOutput( + { + contentsIgnoredCharsMap: imports.contentsIgnoredChars, + rootNode: rootNode, + contentsMap: imports.contents, + sourceMapFilename: this.options.sourceMapFilename, + sourceMapURL: this.options.sourceMapURL, + outputFilename: this.options.sourceMapOutputFilename, + sourceMapBasepath: this.options.sourceMapBasepath, + sourceMapRootpath: this.options.sourceMapRootpath, + outputSourceFiles: this.options.outputSourceFiles, + sourceMapGenerator: this.options.sourceMapGenerator, + sourceMapFileInline: this.options.sourceMapFileInline + }); + + var css = sourceMapOutput.toCSS(options); + this.sourceMap = sourceMapOutput.sourceMap; + this.sourceMapURL = sourceMapOutput.sourceMapURL; + if (this.options.sourceMapInputFilename) { + this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename); + } + return css + this.getCSSAppendage(); + }; + + SourceMapBuilder.prototype.getCSSAppendage = function() { + + var sourceMapURL = this.sourceMapURL; + if (this.options.sourceMapFileInline) { + if (this.sourceMap === undefined) { + return ""; + } + sourceMapURL = "data:application/json;base64," + environment.encodeBase64(this.sourceMap); + } + + if (sourceMapURL) { + return "/*# sourceMappingURL=" + sourceMapURL + " */"; + } + return ""; + }; + + SourceMapBuilder.prototype.getExternalSourceMap = function() { + return this.sourceMap; + }; + SourceMapBuilder.prototype.setExternalSourceMap = function(sourceMap) { + this.sourceMap = sourceMap; + }; + + SourceMapBuilder.prototype.isInline = function() { + return this.options.sourceMapFileInline; + }; + SourceMapBuilder.prototype.getSourceMapURL = function() { + return this.sourceMapURL; + }; + SourceMapBuilder.prototype.getOutputFilename = function() { + return this.options.sourceMapOutputFilename; + }; + SourceMapBuilder.prototype.getInputFilename = function() { + return this.sourceMapInputFilename; + }; + + return SourceMapBuilder; +}; + +},{}],40:[function(require,module,exports){ +module.exports = function (environment) { + + var SourceMapOutput = function (options) { + this._css = []; + this._rootNode = options.rootNode; + this._contentsMap = options.contentsMap; + this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; + if (options.sourceMapFilename) { + this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/'); + } + this._outputFilename = options.outputFilename; + this.sourceMapURL = options.sourceMapURL; + if (options.sourceMapBasepath) { + this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); + } + if (options.sourceMapRootpath) { + this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/'); + if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') { + this._sourceMapRootpath += '/'; + } + } else { + this._sourceMapRootpath = ""; + } + this._outputSourceFiles = options.outputSourceFiles; + this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator(); + + this._lineNumber = 0; + this._column = 0; + }; + + SourceMapOutput.prototype.normalizeFilename = function(filename) { + filename = filename.replace(/\\/g, '/'); + + if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { + filename = filename.substring(this._sourceMapBasepath.length); + if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { + filename = filename.substring(1); + } + } + return (this._sourceMapRootpath || "") + filename; + }; + + SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { + + //ignore adding empty strings + if (!chunk) { + return; + } + + var lines, + sourceLines, + columns, + sourceColumns, + i; + + if (fileInfo) { + var inputSource = this._contentsMap[fileInfo.filename]; + + // remove vars/banner added to the top of the file + if (this._contentsIgnoredCharsMap[fileInfo.filename]) { + // adjust the index + index -= this._contentsIgnoredCharsMap[fileInfo.filename]; + if (index < 0) { index = 0; } + // adjust the source + inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); + } + inputSource = inputSource.substring(0, index); + sourceLines = inputSource.split("\n"); + sourceColumns = sourceLines[sourceLines.length - 1]; + } + + lines = chunk.split("\n"); + columns = lines[lines.length - 1]; + + if (fileInfo) { + if (!mapLines) { + this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, + original: { line: sourceLines.length, column: sourceColumns.length}, + source: this.normalizeFilename(fileInfo.filename)}); + } else { + for (i = 0; i < lines.length; i++) { + this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, + original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, + source: this.normalizeFilename(fileInfo.filename)}); + } + } + } + + if (lines.length === 1) { + this._column += columns.length; + } else { + this._lineNumber += lines.length - 1; + this._column = columns.length; + } + + this._css.push(chunk); + }; + + SourceMapOutput.prototype.isEmpty = function() { + return this._css.length === 0; + }; + + SourceMapOutput.prototype.toCSS = function(context) { + this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); + + if (this._outputSourceFiles) { + for (var filename in this._contentsMap) { + if (this._contentsMap.hasOwnProperty(filename)) + { + var source = this._contentsMap[filename]; + if (this._contentsIgnoredCharsMap[filename]) { + source = source.slice(this._contentsIgnoredCharsMap[filename]); + } + this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); + } + } + } + + this._rootNode.genCSS(context, this); + + if (this._css.length > 0) { + var sourceMapURL, + sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); + + if (this.sourceMapURL) { + sourceMapURL = this.sourceMapURL; + } else if (this._sourceMapFilename) { + sourceMapURL = this._sourceMapFilename; + } + this.sourceMapURL = sourceMapURL; + + this.sourceMap = sourceMapContent; + } + + return this._css.join(''); + }; + + return SourceMapOutput; +}; + +},{}],41:[function(require,module,exports){ +var contexts = require("./contexts"), + visitor = require("./visitors"), + tree = require("./tree"); + +module.exports = function(root, options) { + options = options || {}; + var evaldRoot, + variables = options.variables, + evalEnv = new contexts.Eval(options); + + // + // Allows setting variables with a hash, so: + // + // `{ color: new tree.Color('#f01') }` will become: + // + // new tree.Rule('@color', + // new tree.Value([ + // new tree.Expression([ + // new tree.Color('#f01') + // ]) + // ]) + // ) + // + if (typeof variables === 'object' && !Array.isArray(variables)) { + variables = Object.keys(variables).map(function (k) { + var value = variables[k]; + + if (! (value instanceof tree.Value)) { + if (! (value instanceof tree.Expression)) { + value = new tree.Expression([value]); + } + value = new tree.Value([value]); + } + return new tree.Rule('@' + k, value, false, null, 0); + }); + evalEnv.frames = [new tree.Ruleset(null, variables)]; + } + + var preEvalVisitors = [], + visitors = [ + new visitor.JoinSelectorVisitor(), + new visitor.ExtendVisitor(), + new visitor.ToCSSVisitor({compress: Boolean(options.compress)}) + ], i; + + if (options.pluginManager) { + var pluginVisitors = options.pluginManager.getVisitors(); + for (i = 0; i < pluginVisitors.length; i++) { + var pluginVisitor = pluginVisitors[i]; + if (pluginVisitor.isPreEvalVisitor) { + preEvalVisitors.push(pluginVisitor); + } else { + if (pluginVisitor.isPreVisitor) { + visitors.splice(0, 0, pluginVisitor); + } else { + visitors.push(pluginVisitor); + } + } + } + } + + for (i = 0; i < preEvalVisitors.length; i++) { + preEvalVisitors[i].run(root); + } + + evaldRoot = root.eval(evalEnv); + + for (i = 0; i < visitors.length; i++) { + visitors[i].run(evaldRoot); + } + + return evaldRoot; +}; + +},{"./contexts":10,"./tree":59,"./visitors":84}],42:[function(require,module,exports){ +var Node = require("./node"); + +var Alpha = function (val) { + this.value = val; +}; +Alpha.prototype = new Node(); +Alpha.prototype.type = "Alpha"; + +Alpha.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Alpha.prototype.eval = function (context) { + if (this.value.eval) { return new Alpha(this.value.eval(context)); } + return this; +}; +Alpha.prototype.genCSS = function (context, output) { + output.add("alpha(opacity="); + + if (this.value.genCSS) { + this.value.genCSS(context, output); + } else { + output.add(this.value); + } + + output.add(")"); +}; + +module.exports = Alpha; + +},{"./node":67}],43:[function(require,module,exports){ +var Node = require("./node"); + +var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { + this.value = value; + this.index = index; + this.mapLines = mapLines; + this.currentFileInfo = currentFileInfo; + this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike; +}; +Anonymous.prototype = new Node(); +Anonymous.prototype.type = "Anonymous"; +Anonymous.prototype.eval = function () { + return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); +}; +Anonymous.prototype.compare = function (other) { + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; +}; +Anonymous.prototype.isRulesetLike = function() { + return this.rulesetLike; +}; +Anonymous.prototype.genCSS = function (context, output) { + output.add(this.value, this.currentFileInfo, this.index, this.mapLines); +}; +module.exports = Anonymous; + +},{"./node":67}],44:[function(require,module,exports){ +var Node = require("./node"); + +var Assignment = function (key, val) { + this.key = key; + this.value = val; +}; + +Assignment.prototype = new Node(); +Assignment.prototype.type = "Assignment"; +Assignment.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Assignment.prototype.eval = function (context) { + if (this.value.eval) { + return new Assignment(this.key, this.value.eval(context)); + } + return this; +}; +Assignment.prototype.genCSS = function (context, output) { + output.add(this.key + '='); + if (this.value.genCSS) { + this.value.genCSS(context, output); + } else { + output.add(this.value); + } +}; +module.exports = Assignment; + +},{"./node":67}],45:[function(require,module,exports){ +var Node = require("./node"); + +var Attribute = function (key, op, value) { + this.key = key; + this.op = op; + this.value = value; +}; +Attribute.prototype = new Node(); +Attribute.prototype.type = "Attribute"; +Attribute.prototype.eval = function (context) { + return new Attribute(this.key.eval ? this.key.eval(context) : this.key, + this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value); +}; +Attribute.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context)); +}; +Attribute.prototype.toCSS = function (context) { + var value = this.key.toCSS ? this.key.toCSS(context) : this.key; + + if (this.op) { + value += this.op; + value += (this.value.toCSS ? this.value.toCSS(context) : this.value); + } + + return '[' + value + ']'; +}; +module.exports = Attribute; + +},{"./node":67}],46:[function(require,module,exports){ +var Node = require("./node"), + FunctionCaller = require("../functions/function-caller"); +// +// A function call node. +// +var Call = function (name, args, index, currentFileInfo) { + this.name = name; + this.args = args; + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +Call.prototype = new Node(); +Call.prototype.type = "Call"; +Call.prototype.accept = function (visitor) { + if (this.args) { + this.args = visitor.visitArray(this.args); + } +}; +// +// When evaluating a function call, +// we either find the function in the functionRegistry, +// in which case we call it, passing the evaluated arguments, +// if this returns null or we cannot find the function, we +// simply print it out as it appeared originally [2]. +// +// The reason why we evaluate the arguments, is in the case where +// we try to pass a variable to a function, like: `saturate(@color)`. +// The function should receive the value, not the variable. +// +Call.prototype.eval = function (context) { + var args = this.args.map(function (a) { return a.eval(context); }), + result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo); + + if (funcCaller.isValid()) { // 1. + try { + result = funcCaller.call(args); + if (result != null) { + return result; + } + } catch (e) { + throw { type: e.type || "Runtime", + message: "error evaluating function `" + this.name + "`" + + (e.message ? ': ' + e.message : ''), + index: this.index, filename: this.currentFileInfo.filename }; + } + } + + return new Call(this.name, args, this.index, this.currentFileInfo); +}; +Call.prototype.genCSS = function (context, output) { + output.add(this.name + "(", this.currentFileInfo, this.index); + + for (var i = 0; i < this.args.length; i++) { + this.args[i].genCSS(context, output); + if (i + 1 < this.args.length) { + output.add(", "); + } + } + + output.add(")"); +}; +module.exports = Call; + +},{"../functions/function-caller":20,"./node":67}],47:[function(require,module,exports){ +var Node = require("./node"), + colors = require("../data/colors"); + +// +// RGB Colors - #ff0014, #eee +// +var Color = function (rgb, a) { + // + // The end goal here, is to parse the arguments + // into an integer triplet, such as `128, 255, 0` + // + // This facilitates operations and conversions. + // + if (Array.isArray(rgb)) { + this.rgb = rgb; + } else if (rgb.length == 6) { + this.rgb = rgb.match(/.{2}/g).map(function (c) { + return parseInt(c, 16); + }); + } else { + this.rgb = rgb.split('').map(function (c) { + return parseInt(c + c, 16); + }); + } + this.alpha = typeof a === 'number' ? a : 1; +}; + +Color.prototype = new Node(); +Color.prototype.type = "Color"; + +function clamp(v, max) { + return Math.min(Math.max(v, 0), max); +} + +function toHex(v) { + return '#' + v.map(function (c) { + c = clamp(Math.round(c), 255); + return (c < 16 ? '0' : '') + c.toString(16); + }).join(''); +} + +Color.prototype.luma = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255; + + r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); + g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); + b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +}; +Color.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context)); +}; +Color.prototype.toCSS = function (context, doNotCompress) { + var compress = context && context.compress && !doNotCompress, color, alpha; + + // `value` is set if this color was originally + // converted from a named color string so we need + // to respect this and try to output named color too. + if (this.value) { + return this.value; + } + + // If we have some transparency, the only way to represent it + // is via `rgba`. Otherwise, we use the hex representation, + // which has better compatibility with older browsers. + // Values are capped between `0` and `255`, rounded and zero-padded. + alpha = this.fround(context, this.alpha); + if (alpha < 1) { + return "rgba(" + this.rgb.map(function (c) { + return clamp(Math.round(c), 255); + }).concat(clamp(alpha, 1)) + .join(',' + (compress ? '' : ' ')) + ")"; + } + + color = this.toRGB(); + + if (compress) { + var splitcolor = color.split(''); + + // Convert color to short format + if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { + color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; + } + } + + return color; +}; + +// +// Operations have to be done per-channel, if not, +// channels will spill onto each other. Once we have +// our result, in the form of an integer triplet, +// we create a new Color node to hold the result. +// +Color.prototype.operate = function (context, op, other) { + var rgb = []; + var alpha = this.alpha * (1 - other.alpha) + other.alpha; + for (var c = 0; c < 3; c++) { + rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]); + } + return new Color(rgb, alpha); +}; +Color.prototype.toRGB = function () { + return toHex(this.rgb); +}; +Color.prototype.toHSL = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2, d = max - min; + + if (max === min) { + h = s = 0; + } else { + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h * 360, s: s, l: l, a: a }; +}; +//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript +Color.prototype.toHSV = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + + var d = max - min; + if (max === 0) { + s = 0; + } else { + s = d / max; + } + + if (max === min) { + h = 0; + } else { + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h * 360, s: s, v: v, a: a }; +}; +Color.prototype.toARGB = function () { + return toHex([this.alpha * 255].concat(this.rgb)); +}; +Color.prototype.compare = function (x) { + return (x.rgb && + x.rgb[0] === this.rgb[0] && + x.rgb[1] === this.rgb[1] && + x.rgb[2] === this.rgb[2] && + x.alpha === this.alpha) ? 0 : undefined; +}; + +Color.fromKeyword = function(keyword) { + var c, key = keyword.toLowerCase(); + if (colors.hasOwnProperty(key)) { + c = new Color(colors[key].slice(1)); + } + else if (key === "transparent") { + c = new Color([0, 0, 0], 0); + } + + if (c) { + c.value = keyword; + return c; + } +}; +module.exports = Color; + +},{"../data/colors":11,"./node":67}],48:[function(require,module,exports){ +var Node = require("./node"); + +var Combinator = function (value) { + if (value === ' ') { + this.value = ' '; + this.emptyOrWhitespace = true; + } else { + this.value = value ? value.trim() : ""; + this.emptyOrWhitespace = this.value === ""; + } +}; +Combinator.prototype = new Node(); +Combinator.prototype.type = "Combinator"; +var _noSpaceCombinators = { + '': true, + ' ': true, + '|': true +}; +Combinator.prototype.genCSS = function (context, output) { + var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' '; + output.add(spaceOrEmpty + this.value + spaceOrEmpty); +}; +module.exports = Combinator; + +},{"./node":67}],49:[function(require,module,exports){ +var Node = require("./node"), + getDebugInfo = require("./debug-info"); + +var Comment = function (value, isLineComment, index, currentFileInfo) { + this.value = value; + this.isLineComment = isLineComment; + this.currentFileInfo = currentFileInfo; +}; +Comment.prototype = new Node(); +Comment.prototype.type = "Comment"; +Comment.prototype.genCSS = function (context, output) { + if (this.debugInfo) { + output.add(getDebugInfo(context, this), this.currentFileInfo, this.index); + } + output.add(this.value); +}; +Comment.prototype.isSilent = function(context) { + var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), + isCompressed = context.compress && this.value[2] !== "!"; + return this.isLineComment || isReference || isCompressed; +}; +Comment.prototype.markReferenced = function () { + this.isReferenced = true; +}; +Comment.prototype.isRulesetLike = function(root) { + return Boolean(root); +}; +module.exports = Comment; + +},{"./debug-info":51,"./node":67}],50:[function(require,module,exports){ +var Node = require("./node"); + +var Condition = function (op, l, r, i, negate) { + this.op = op.trim(); + this.lvalue = l; + this.rvalue = r; + this.index = i; + this.negate = negate; +}; +Condition.prototype = new Node(); +Condition.prototype.type = "Condition"; +Condition.prototype.accept = function (visitor) { + this.lvalue = visitor.visit(this.lvalue); + this.rvalue = visitor.visit(this.rvalue); +}; +Condition.prototype.eval = function (context) { + var result = (function (op, a, b) { + switch (op) { + case 'and': return a && b; + case 'or': return a || b; + default: + switch (Node.compare(a, b)) { + case -1: + return op === '<' || op === '=<' || op === '<='; + case 0: + return op === '=' || op === '>=' || op === '=<' || op === '<='; + case 1: + return op === '>' || op === '>='; + default: + return false; + } + } + })(this.op, this.lvalue.eval(context), this.rvalue.eval(context)); + + return this.negate ? !result : result; +}; +module.exports = Condition; + +},{"./node":67}],51:[function(require,module,exports){ +var debugInfo = function(context, ctx, lineSeparator) { + var result = ""; + if (context.dumpLineNumbers && !context.compress) { + switch(context.dumpLineNumbers) { + case 'comments': + result = debugInfo.asComment(ctx); + break; + case 'mediaquery': + result = debugInfo.asMediaQuery(ctx); + break; + case 'all': + result = debugInfo.asComment(ctx) + (lineSeparator || "") + debugInfo.asMediaQuery(ctx); + break; + } + } + return result; +}; + +debugInfo.asComment = function(ctx) { + return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; +}; + +debugInfo.asMediaQuery = function(ctx) { + var filenameWithProtocol = ctx.debugInfo.fileName; + if (!/^[a-z]+:\/\//i.test(filenameWithProtocol)) { + filenameWithProtocol = 'file://' + filenameWithProtocol; + } + return '@media -sass-debug-info{filename{font-family:' + + filenameWithProtocol.replace(/([.:\/\\])/g, function (a) { + if (a == '\\') { + a = '\/'; + } + return '\\' + a; + }) + + '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; +}; + +module.exports = debugInfo; + +},{}],52:[function(require,module,exports){ +var Node = require("./node"), + contexts = require("../contexts"); + +var DetachedRuleset = function (ruleset, frames) { + this.ruleset = ruleset; + this.frames = frames; +}; +DetachedRuleset.prototype = new Node(); +DetachedRuleset.prototype.type = "DetachedRuleset"; +DetachedRuleset.prototype.evalFirst = true; +DetachedRuleset.prototype.accept = function (visitor) { + this.ruleset = visitor.visit(this.ruleset); +}; +DetachedRuleset.prototype.eval = function (context) { + var frames = this.frames || context.frames.slice(0); + return new DetachedRuleset(this.ruleset, frames); +}; +DetachedRuleset.prototype.callEval = function (context) { + return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context); +}; +module.exports = DetachedRuleset; + +},{"../contexts":10,"./node":67}],53:[function(require,module,exports){ +var Node = require("./node"), + unitConversions = require("../data/unit-conversions"), + Unit = require("./unit"), + Color = require("./color"); + +// +// A number with a unit +// +var Dimension = function (value, unit) { + this.value = parseFloat(value); + this.unit = (unit && unit instanceof Unit) ? unit : + new Unit(unit ? [unit] : undefined); +}; + +Dimension.prototype = new Node(); +Dimension.prototype.type = "Dimension"; +Dimension.prototype.accept = function (visitor) { + this.unit = visitor.visit(this.unit); +}; +Dimension.prototype.eval = function (context) { + return this; +}; +Dimension.prototype.toColor = function () { + return new Color([this.value, this.value, this.value]); +}; +Dimension.prototype.genCSS = function (context, output) { + if ((context && context.strictUnits) && !this.unit.isSingular()) { + throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: " + this.unit.toString()); + } + + var value = this.fround(context, this.value), + strValue = String(value); + + if (value !== 0 && value < 0.000001 && value > -0.000001) { + // would be output 1e-6 etc. + strValue = value.toFixed(20).replace(/0+$/, ""); + } + + if (context && context.compress) { + // Zero values doesn't need a unit + if (value === 0 && this.unit.isLength()) { + output.add(strValue); + return; + } + + // Float values doesn't need a leading zero + if (value > 0 && value < 1) { + strValue = (strValue).substr(1); + } + } + + output.add(strValue); + this.unit.genCSS(context, output); +}; + +// In an operation between two Dimensions, +// we default to the first Dimension's unit, +// so `1px + 2` will yield `3px`. +Dimension.prototype.operate = function (context, op, other) { + /*jshint noempty:false */ + var value = this._operate(context, op, this.value, other.value), + unit = this.unit.clone(); + + if (op === '+' || op === '-') { + if (unit.numerator.length === 0 && unit.denominator.length === 0) { + unit.numerator = other.unit.numerator.slice(0); + unit.denominator = other.unit.denominator.slice(0); + } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { + // do nothing + } else { + other = other.convertTo(this.unit.usedUnits()); + + if (context.strictUnits && other.unit.toString() !== unit.toString()) { + throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + + "' and '" + other.unit.toString() + "'."); + } + + value = this._operate(context, op, this.value, other.value); + } + } else if (op === '*') { + unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); + unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); + unit.cancel(); + } else if (op === '/') { + unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); + unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); + unit.cancel(); + } + return new Dimension(value, unit); +}; +Dimension.prototype.compare = function (other) { + var a, b; + + if (!(other instanceof Dimension)) { + return undefined; + } + + if (this.unit.isEmpty() || other.unit.isEmpty()) { + a = this; + b = other; + } else { + a = this.unify(); + b = other.unify(); + if (a.unit.compare(b.unit) !== 0) { + return undefined; + } + } + + return Node.numericCompare(a.value, b.value); +}; +Dimension.prototype.unify = function () { + return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); +}; +Dimension.prototype.convertTo = function (conversions) { + var value = this.value, unit = this.unit.clone(), + i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; + + if (typeof conversions === 'string') { + for (i in unitConversions) { + if (unitConversions[i].hasOwnProperty(conversions)) { + derivedConversions = {}; + derivedConversions[i] = conversions; + } + } + conversions = derivedConversions; + } + applyUnit = function (atomicUnit, denominator) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit)) { + if (denominator) { + value = value / (group[atomicUnit] / group[targetUnit]); + } else { + value = value * (group[atomicUnit] / group[targetUnit]); + } + + return targetUnit; + } + + return atomicUnit; + }; + + for (groupName in conversions) { + if (conversions.hasOwnProperty(groupName)) { + targetUnit = conversions[groupName]; + group = unitConversions[groupName]; + + unit.map(applyUnit); + } + } + + unit.cancel(); + + return new Dimension(value, unit); +}; +module.exports = Dimension; + +},{"../data/unit-conversions":13,"./color":47,"./node":67,"./unit":76}],54:[function(require,module,exports){ +var Node = require("./node"), + Ruleset = require("./ruleset"); + +var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isReferenced) { + this.name = name; + this.value = value; + if (rules) { + this.rules = rules; + this.rules.allowImports = true; + } + this.index = index; + this.currentFileInfo = currentFileInfo; + this.debugInfo = debugInfo; + this.isReferenced = isReferenced; +}; + +Directive.prototype = new Node(); +Directive.prototype.type = "Directive"; +Directive.prototype.accept = function (visitor) { + var value = this.value, rules = this.rules; + if (rules) { + this.rules = visitor.visit(rules); + } + if (value) { + this.value = visitor.visit(value); + } +}; +Directive.prototype.isRulesetLike = function() { + return this.rules || !this.isCharset(); +}; +Directive.prototype.isCharset = function() { + return "@charset" === this.name; +}; +Directive.prototype.genCSS = function (context, output) { + var value = this.value, rules = this.rules; + output.add(this.name, this.currentFileInfo, this.index); + if (value) { + output.add(' '); + value.genCSS(context, output); + } + if (rules) { + if (rules.type === "Ruleset") { + rules = [rules]; + } + this.outputRuleset(context, output, rules); + } else { + output.add(';'); + } +}; +Directive.prototype.eval = function (context) { + var value = this.value, rules = this.rules; + if (value) { + value = value.eval(context); + } + if (rules) { + rules = rules.eval(context); + rules.root = true; + } + return new Directive(this.name, value, rules, + this.index, this.currentFileInfo, this.debugInfo, this.isReferenced); +}; +Directive.prototype.variable = function (name) { + if (this.rules) { + return Ruleset.prototype.variable.call(this.rules, name); + } +}; +Directive.prototype.find = function () { + if (this.rules) { + return Ruleset.prototype.find.apply(this.rules, arguments); + } +}; +Directive.prototype.rulesets = function () { + if (this.rules) { + return Ruleset.prototype.rulesets.apply(this.rules); + } +}; +Directive.prototype.markReferenced = function () { + var i, rules; + this.isReferenced = true; + if (this.rules) { + rules = this.rules.rules; + for (i = 0; i < rules.length; i++) { + if (rules[i].markReferenced) { + rules[i].markReferenced(); + } + } + } +}; +Directive.prototype.getIsReferenced = function () { + return !this.currentFileInfo || !this.currentFileInfo.reference || this.isReferenced; +}; +Directive.prototype.outputRuleset = function (context, output, rules) { + var ruleCnt = rules.length, i; + context.tabLevel = (context.tabLevel | 0) + 1; + + // Compressed + if (context.compress) { + output.add('{'); + for (i = 0; i < ruleCnt; i++) { + rules[i].genCSS(context, output); + } + output.add('}'); + context.tabLevel--; + return; + } + + // Non-compressed + var tabSetStr = '\n' + Array(context.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; + if (!ruleCnt) { + output.add(" {" + tabSetStr + '}'); + } else { + output.add(" {" + tabRuleStr); + rules[0].genCSS(context, output); + for (i = 1; i < ruleCnt; i++) { + output.add(tabRuleStr); + rules[i].genCSS(context, output); + } + output.add(tabSetStr + '}'); + } + + context.tabLevel--; +}; +module.exports = Directive; + +},{"./node":67,"./ruleset":73}],55:[function(require,module,exports){ +var Node = require("./node"), + Paren = require("./paren"), + Combinator = require("./combinator"); + +var Element = function (combinator, value, index, currentFileInfo) { + this.combinator = combinator instanceof Combinator ? + combinator : new Combinator(combinator); + + if (typeof value === 'string') { + this.value = value.trim(); + } else if (value) { + this.value = value; + } else { + this.value = ""; + } + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +Element.prototype = new Node(); +Element.prototype.type = "Element"; +Element.prototype.accept = function (visitor) { + var value = this.value; + this.combinator = visitor.visit(this.combinator); + if (typeof value === "object") { + this.value = visitor.visit(value); + } +}; +Element.prototype.eval = function (context) { + return new Element(this.combinator, + this.value.eval ? this.value.eval(context) : this.value, + this.index, + this.currentFileInfo); +}; +Element.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context), this.currentFileInfo, this.index); +}; +Element.prototype.toCSS = function (context) { + context = context || {}; + var value = this.value, firstSelector = context.firstSelector; + if (value instanceof Paren) { + // selector in parens should not be affected by outer selector + // flags (breaks only interpolated selectors - see #1973) + context.firstSelector = true; + } + value = value.toCSS ? value.toCSS(context) : value; + context.firstSelector = firstSelector; + if (value === '' && this.combinator.value.charAt(0) === '&') { + return ''; + } else { + return this.combinator.toCSS(context) + value; + } +}; +module.exports = Element; + +},{"./combinator":48,"./node":67,"./paren":69}],56:[function(require,module,exports){ +var Node = require("./node"), + Paren = require("./paren"), + Comment = require("./comment"); + +var Expression = function (value) { + this.value = value; + if (!value) { + throw new Error("Expression requires an array parameter"); + } +}; +Expression.prototype = new Node(); +Expression.prototype.type = "Expression"; +Expression.prototype.accept = function (visitor) { + this.value = visitor.visitArray(this.value); +}; +Expression.prototype.eval = function (context) { + var returnValue, + inParenthesis = this.parens && !this.parensInOp, + doubleParen = false; + if (inParenthesis) { + context.inParenthesis(); + } + if (this.value.length > 1) { + returnValue = new Expression(this.value.map(function (e) { + return e.eval(context); + })); + } else if (this.value.length === 1) { + if (this.value[0].parens && !this.value[0].parensInOp) { + doubleParen = true; + } + returnValue = this.value[0].eval(context); + } else { + returnValue = this; + } + if (inParenthesis) { + context.outOfParenthesis(); + } + if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) { + returnValue = new Paren(returnValue); + } + return returnValue; +}; +Expression.prototype.genCSS = function (context, output) { + for (var i = 0; i < this.value.length; i++) { + this.value[i].genCSS(context, output); + if (i + 1 < this.value.length) { + output.add(" "); + } + } +}; +Expression.prototype.throwAwayComments = function () { + this.value = this.value.filter(function(v) { + return !(v instanceof Comment); + }); +}; +module.exports = Expression; + +},{"./comment":49,"./node":67,"./paren":69}],57:[function(require,module,exports){ +var Node = require("./node"); + +var Extend = function Extend(selector, option, index) { + this.selector = selector; + this.option = option; + this.index = index; + this.object_id = Extend.next_id++; + this.parent_ids = [this.object_id]; + + switch(option) { + case "all": + this.allowBefore = true; + this.allowAfter = true; + break; + default: + this.allowBefore = false; + this.allowAfter = false; + break; + } +}; +Extend.next_id = 0; + +Extend.prototype = new Node(); +Extend.prototype.type = "Extend"; +Extend.prototype.accept = function (visitor) { + this.selector = visitor.visit(this.selector); +}; +Extend.prototype.eval = function (context) { + return new Extend(this.selector.eval(context), this.option, this.index); +}; +Extend.prototype.clone = function (context) { + return new Extend(this.selector, this.option, this.index); +}; +Extend.prototype.findSelfSelectors = function (selectors) { + var selfElements = [], + i, + selectorElements; + + for (i = 0; i < selectors.length; i++) { + selectorElements = selectors[i].elements; + // duplicate the logic in genCSS function inside the selector node. + // future TODO - move both logics into the selector joiner visitor + if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { + selectorElements[0].combinator.value = ' '; + } + selfElements = selfElements.concat(selectors[i].elements); + } + + this.selfSelectors = [{ elements: selfElements }]; +}; +module.exports = Extend; + +},{"./node":67}],58:[function(require,module,exports){ +var Node = require("./node"), + Media = require("./media"), + URL = require("./url"), + Quoted = require("./quoted"), + Ruleset = require("./ruleset"), + Anonymous = require("./anonymous"); + +// +// CSS @import node +// +// The general strategy here is that we don't want to wait +// for the parsing to be completed, before we start importing +// the file. That's because in the context of a browser, +// most of the time will be spent waiting for the server to respond. +// +// On creation, we push the import path to our import queue, though +// `import,push`, we also pass it a callback, which it'll call once +// the file has been fetched, and parsed. +// +var Import = function (path, features, options, index, currentFileInfo) { + this.options = options; + this.index = index; + this.path = path; + this.features = features; + this.currentFileInfo = currentFileInfo; + + if (this.options.less !== undefined || this.options.inline) { + this.css = !this.options.less || this.options.inline; + } else { + var pathValue = this.getPath(); + if (pathValue && /[#\.\&\?\/]css([\?;].*)?$/.test(pathValue)) { + this.css = true; + } + } +}; + +// +// The actual import node doesn't return anything, when converted to CSS. +// The reason is that it's used at the evaluation stage, so that the rules +// it imports can be treated like any other rules. +// +// In `eval`, we make sure all Import nodes get evaluated, recursively, so +// we end up with a flat structure, which can easily be imported in the parent +// ruleset. +// +Import.prototype = new Node(); +Import.prototype.type = "Import"; +Import.prototype.accept = function (visitor) { + if (this.features) { + this.features = visitor.visit(this.features); + } + this.path = visitor.visit(this.path); + if (!this.options.inline && this.root) { + this.root = visitor.visit(this.root); + } +}; +Import.prototype.genCSS = function (context, output) { + if (this.css) { + output.add("@import ", this.currentFileInfo, this.index); + this.path.genCSS(context, output); + if (this.features) { + output.add(" "); + this.features.genCSS(context, output); + } + output.add(';'); + } +}; +Import.prototype.getPath = function () { + if (this.path instanceof Quoted) { + return this.path.value; + } else if (this.path instanceof URL) { + return this.path.value.value; + } + return null; +}; +Import.prototype.isVariableImport = function () { + var path = this.path; + if (path instanceof URL) { + path = path.value; + } + if (path instanceof Quoted) { + return path.containsVariables(); + } + + return true; +}; +Import.prototype.evalForImport = function (context) { + var path = this.path; + if (path instanceof URL) { + path = path.value; + } + return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo); +}; +Import.prototype.evalPath = function (context) { + var path = this.path.eval(context); + var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; + + if (!(path instanceof URL)) { + if (rootpath) { + var pathValue = path.value; + // Add the base path if the import is relative + if (pathValue && context.isPathRelative(pathValue)) { + path.value = rootpath + pathValue; + } + } + path.value = context.normalizePath(path.value); + } + + return path; +}; +Import.prototype.eval = function (context) { + var ruleset, features = this.features && this.features.eval(context); + + if (this.skip) { + if (typeof this.skip === "function") { + this.skip = this.skip(); + } + if (this.skip) { + return []; + } + } + + if (this.options.inline) { + var contents = new Anonymous(this.root, 0, {filename: this.importedFilename}, true, true); + return this.features ? new Media([contents], this.features.value) : [contents]; + } else if (this.css) { + var newImport = new Import(this.evalPath(context), features, this.options, this.index); + if (!newImport.css && this.error) { + throw this.error; + } + return newImport; + } else { + ruleset = new Ruleset(null, this.root.rules.slice(0)); + + ruleset.evalImports(context); + + return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules; + } +}; +module.exports = Import; + +},{"./anonymous":43,"./media":63,"./node":67,"./quoted":70,"./ruleset":73,"./url":77}],59:[function(require,module,exports){ +var tree = {}; + +tree.Node = require('./node'); +tree.Alpha = require('./alpha'); +tree.Color = require('./color'); +tree.Directive = require('./directive'); +tree.DetachedRuleset = require('./detached-ruleset'); +tree.Operation = require('./operation'); +tree.Dimension = require('./dimension'); +tree.Unit = require('./unit'); +tree.Keyword = require('./keyword'); +tree.Variable = require('./variable'); +tree.Ruleset = require('./ruleset'); +tree.Element = require('./element'); +tree.Attribute = require('./attribute'); +tree.Combinator = require('./combinator'); +tree.Selector = require('./selector'); +tree.Quoted = require('./quoted'); +tree.Expression = require('./expression'); +tree.Rule = require('./rule'); +tree.Call = require('./call'); +tree.URL = require('./url'); +tree.Import = require('./import'); +tree.mixin = { + Call: require('./mixin-call'), + Definition: require('./mixin-definition') +}; +tree.Comment = require('./comment'); +tree.Anonymous = require('./anonymous'); +tree.Value = require('./value'); +tree.JavaScript = require('./javascript'); +tree.Assignment = require('./assignment'); +tree.Condition = require('./condition'); +tree.Paren = require('./paren'); +tree.Media = require('./media'); +tree.UnicodeDescriptor = require('./unicode-descriptor'); +tree.Negative = require('./negative'); +tree.Extend = require('./extend'); +tree.RulesetCall = require('./ruleset-call'); + +module.exports = tree; + +},{"./alpha":42,"./anonymous":43,"./assignment":44,"./attribute":45,"./call":46,"./color":47,"./combinator":48,"./comment":49,"./condition":50,"./detached-ruleset":52,"./dimension":53,"./directive":54,"./element":55,"./expression":56,"./extend":57,"./import":58,"./javascript":60,"./keyword":62,"./media":63,"./mixin-call":64,"./mixin-definition":65,"./negative":66,"./node":67,"./operation":68,"./paren":69,"./quoted":70,"./rule":71,"./ruleset":73,"./ruleset-call":72,"./selector":74,"./unicode-descriptor":75,"./unit":76,"./url":77,"./value":78,"./variable":79}],60:[function(require,module,exports){ +var JsEvalNode = require("./js-eval-node"), + Dimension = require("./dimension"), + Quoted = require("./quoted"), + Anonymous = require("./anonymous"); + +var JavaScript = function (string, escaped, index, currentFileInfo) { + this.escaped = escaped; + this.expression = string; + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +JavaScript.prototype = new JsEvalNode(); +JavaScript.prototype.type = "JavaScript"; +JavaScript.prototype.eval = function(context) { + var result = this.evaluateJavaScript(this.expression, context); + + if (typeof result === 'number') { + return new Dimension(result); + } else if (typeof result === 'string') { + return new Quoted('"' + result + '"', result, this.escaped, this.index); + } else if (Array.isArray(result)) { + return new Anonymous(result.join(', ')); + } else { + return new Anonymous(result); + } +}; + +module.exports = JavaScript; + +},{"./anonymous":43,"./dimension":53,"./js-eval-node":61,"./quoted":70}],61:[function(require,module,exports){ +var Node = require("./node"), + Variable = require("./variable"); + +var JsEvalNode = function() { +}; +JsEvalNode.prototype = new Node(); + +JsEvalNode.prototype.evaluateJavaScript = function (expression, context) { + var result, + that = this, + evalContext = {}; + + if (context.javascriptEnabled !== undefined && !context.javascriptEnabled) { + throw { message: "You are using JavaScript, which has been disabled.", + filename: this.currentFileInfo.filename, + index: this.index }; + } + + expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { + return that.jsify(new Variable('@' + name, that.index, that.currentFileInfo).eval(context)); + }); + + try { + expression = new Function('return (' + expression + ')'); + } catch (e) { + throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , + filename: this.currentFileInfo.filename, + index: this.index }; + } + + var variables = context.frames[0].variables(); + for (var k in variables) { + if (variables.hasOwnProperty(k)) { + /*jshint loopfunc:true */ + evalContext[k.slice(1)] = { + value: variables[k].value, + toJS: function () { + return this.value.eval(context).toCSS(); + } + }; + } + } + + try { + result = expression.call(evalContext); + } catch (e) { + throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , + filename: this.currentFileInfo.filename, + index: this.index }; + } + return result; +}; +JsEvalNode.prototype.jsify = function (obj) { + if (Array.isArray(obj.value) && (obj.value.length > 1)) { + return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; + } else { + return obj.toCSS(); + } +}; + +module.exports = JsEvalNode; + +},{"./node":67,"./variable":79}],62:[function(require,module,exports){ +var Node = require("./node"); + +var Keyword = function (value) { this.value = value; }; +Keyword.prototype = new Node(); +Keyword.prototype.type = "Keyword"; +Keyword.prototype.genCSS = function (context, output) { + if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } + output.add(this.value); +}; + +Keyword.True = new Keyword('true'); +Keyword.False = new Keyword('false'); + +module.exports = Keyword; + +},{"./node":67}],63:[function(require,module,exports){ +var Ruleset = require("./ruleset"), + Value = require("./value"), + Element = require("./element"), + Selector = require("./selector"), + Anonymous = require("./anonymous"), + Expression = require("./expression"), + Directive = require("./directive"); + +var Media = function (value, features, index, currentFileInfo) { + this.index = index; + this.currentFileInfo = currentFileInfo; + + var selectors = this.emptySelectors(); + + this.features = new Value(features); + this.rules = [new Ruleset(selectors, value)]; + this.rules[0].allowImports = true; +}; +Media.prototype = new Directive(); +Media.prototype.type = "Media"; +Media.prototype.isRulesetLike = true; +Media.prototype.accept = function (visitor) { + if (this.features) { + this.features = visitor.visit(this.features); + } + if (this.rules) { + this.rules = visitor.visitArray(this.rules); + } +}; +Media.prototype.genCSS = function (context, output) { + output.add('@media ', this.currentFileInfo, this.index); + this.features.genCSS(context, output); + this.outputRuleset(context, output, this.rules); +}; +Media.prototype.eval = function (context) { + if (!context.mediaBlocks) { + context.mediaBlocks = []; + context.mediaPath = []; + } + + var media = new Media(null, [], this.index, this.currentFileInfo); + if (this.debugInfo) { + this.rules[0].debugInfo = this.debugInfo; + media.debugInfo = this.debugInfo; + } + var strictMathBypass = false; + if (!context.strictMath) { + strictMathBypass = true; + context.strictMath = true; + } + try { + media.features = this.features.eval(context); + } + finally { + if (strictMathBypass) { + context.strictMath = false; + } + } + + context.mediaPath.push(media); + context.mediaBlocks.push(media); + + context.frames.unshift(this.rules[0]); + media.rules = [this.rules[0].eval(context)]; + context.frames.shift(); + + context.mediaPath.pop(); + + return context.mediaPath.length === 0 ? media.evalTop(context) : + media.evalNested(context); +}; +//TODO merge with directive +Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); }; +Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); }; +Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); }; +Media.prototype.emptySelectors = function() { + var el = new Element('', '&', this.index, this.currentFileInfo), + sels = [new Selector([el], null, null, this.index, this.currentFileInfo)]; + sels[0].mediaEmpty = true; + return sels; +}; +Media.prototype.markReferenced = function () { + var i, rules = this.rules[0].rules; + this.rules[0].markReferenced(); + this.isReferenced = true; + for (i = 0; i < rules.length; i++) { + if (rules[i].markReferenced) { + rules[i].markReferenced(); + } + } +}; +Media.prototype.evalTop = function (context) { + var result = this; + + // Render all dependent Media blocks. + if (context.mediaBlocks.length > 1) { + var selectors = this.emptySelectors(); + result = new Ruleset(selectors, context.mediaBlocks); + result.multiMedia = true; + } + + delete context.mediaBlocks; + delete context.mediaPath; + + return result; +}; +Media.prototype.evalNested = function (context) { + var i, value, + path = context.mediaPath.concat([this]); + + // Extract the media-query conditions separated with `,` (OR). + for (i = 0; i < path.length; i++) { + value = path[i].features instanceof Value ? + path[i].features.value : path[i].features; + path[i] = Array.isArray(value) ? value : [value]; + } + + // Trace all permutations to generate the resulting media-query. + // + // (a, b and c) with nested (d, e) -> + // a and d + // a and e + // b and c and d + // b and c and e + this.features = new Value(this.permute(path).map(function (path) { + path = path.map(function (fragment) { + return fragment.toCSS ? fragment : new Anonymous(fragment); + }); + + for (i = path.length - 1; i > 0; i--) { + path.splice(i, 0, new Anonymous("and")); + } + + return new Expression(path); + })); + + // Fake a tree-node that doesn't output anything. + return new Ruleset([], []); +}; +Media.prototype.permute = function (arr) { + if (arr.length === 0) { + return []; + } else if (arr.length === 1) { + return arr[0]; + } else { + var result = []; + var rest = this.permute(arr.slice(1)); + for (var i = 0; i < rest.length; i++) { + for (var j = 0; j < arr[0].length; j++) { + result.push([arr[0][j]].concat(rest[i])); + } + } + return result; + } +}; +Media.prototype.bubbleSelectors = function (selectors) { + if (!selectors) { + return; + } + this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])]; +}; +module.exports = Media; + +},{"./anonymous":43,"./directive":54,"./element":55,"./expression":56,"./ruleset":73,"./selector":74,"./value":78}],64:[function(require,module,exports){ +var Node = require("./node"), + Selector = require("./selector"), + MixinDefinition = require("./mixin-definition"), + defaultFunc = require("../functions/default"); + +var MixinCall = function (elements, args, index, currentFileInfo, important) { + this.selector = new Selector(elements); + this.arguments = (args && args.length) ? args : null; + this.index = index; + this.currentFileInfo = currentFileInfo; + this.important = important; +}; +MixinCall.prototype = new Node(); +MixinCall.prototype.type = "MixinCall"; +MixinCall.prototype.accept = function (visitor) { + if (this.selector) { + this.selector = visitor.visit(this.selector); + } + if (this.arguments) { + this.arguments = visitor.visitArray(this.arguments); + } +}; +MixinCall.prototype.eval = function (context) { + var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, + candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase = -1, + defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter; + + function calcDefGroup(mixin, mixinPath) { + var p, namespace; + + for (f = 0; f < 2; f++) { + conditionResult[f] = true; + defaultFunc.value(f); + for (p = 0; p < mixinPath.length && conditionResult[f]; p++) { + namespace = mixinPath[p]; + if (namespace.matchCondition) { + conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context); + } + } + if (mixin.matchCondition) { + conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context); + } + } + if (conditionResult[0] || conditionResult[1]) { + if (conditionResult[0] != conditionResult[1]) { + return conditionResult[1] ? + defTrue : defFalse; + } + + return defNone; + } + return defFalseEitherCase; + } + + args = this.arguments && this.arguments.map(function (a) { + return { name: a.name, value: a.value.eval(context) }; + }); + + noArgumentsFilter = function(rule) {return rule.matchArgs(null, context);}; + + for (i = 0; i < context.frames.length; i++) { + if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) { + isOneFound = true; + + // To make `default()` function independent of definition order we have two "subpasses" here. + // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`), + // and build candidate list with corresponding flags. Then, when we know all possible matches, + // we make a final decision. + + for (m = 0; m < mixins.length; m++) { + mixin = mixins[m].rule; + mixinPath = mixins[m].path; + isRecursive = false; + for (f = 0; f < context.frames.length; f++) { + if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) { + isRecursive = true; + break; + } + } + if (isRecursive) { + continue; + } + + if (mixin.matchArgs(args, context)) { + candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)}; + + if (candidate.group !== defFalseEitherCase) { + candidates.push(candidate); + } + + match = true; + } + } + + defaultFunc.reset(); + + count = [0, 0, 0]; + for (m = 0; m < candidates.length; m++) { + count[candidates[m].group]++; + } + + if (count[defNone] > 0) { + defaultResult = defFalse; + } else { + defaultResult = defTrue; + if ((count[defTrue] + count[defFalse]) > 1) { + throw { type: 'Runtime', + message: 'Ambiguous use of `default()` found when matching for `' + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } + } + + for (m = 0; m < candidates.length; m++) { + candidate = candidates[m].group; + if ((candidate === defNone) || (candidate === defaultResult)) { + try { + mixin = candidates[m].mixin; + if (!(mixin instanceof MixinDefinition)) { + originalRuleset = mixin.originalRuleset || mixin; + mixin = new MixinDefinition("", [], mixin.rules, null, false); + mixin.originalRuleset = originalRuleset; + } + Array.prototype.push.apply( + rules, mixin.evalCall(context, args, this.important).rules); + } catch (e) { + throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; + } + } + } + + if (match) { + if (!this.currentFileInfo || !this.currentFileInfo.reference) { + for (i = 0; i < rules.length; i++) { + rule = rules[i]; + if (rule.markReferenced) { + rule.markReferenced(); + } + } + } + return rules; + } + } + } + if (isOneFound) { + throw { type: 'Runtime', + message: 'No matching definition was found for `' + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } else { + throw { type: 'Name', + message: this.selector.toCSS().trim() + " is undefined", + index: this.index, filename: this.currentFileInfo.filename }; + } +}; +MixinCall.prototype.format = function (args) { + return this.selector.toCSS().trim() + '(' + + (args ? args.map(function (a) { + var argValue = ""; + if (a.name) { + argValue += a.name + ":"; + } + if (a.value.toCSS) { + argValue += a.value.toCSS(); + } else { + argValue += "???"; + } + return argValue; + }).join(', ') : "") + ")"; +}; +module.exports = MixinCall; + +},{"../functions/default":19,"./mixin-definition":65,"./node":67,"./selector":74}],65:[function(require,module,exports){ +var Selector = require("./selector"), + Element = require("./element"), + Ruleset = require("./ruleset"), + Rule = require("./rule"), + Expression = require("./expression"), + contexts = require("../contexts"); + +var Definition = function (name, params, rules, condition, variadic, frames) { + this.name = name; + this.selectors = [new Selector([new Element(null, name, this.index, this.currentFileInfo)])]; + this.params = params; + this.condition = condition; + this.variadic = variadic; + this.arity = params.length; + this.rules = rules; + this._lookups = {}; + this.required = params.reduce(function (count, p) { + if (!p.name || (p.name && !p.value)) { + return count + 1; + } + else { + return count; + } + }, 0); + this.frames = frames; +}; +Definition.prototype = new Ruleset(); +Definition.prototype.type = "MixinDefinition"; +Definition.prototype.evalFirst = true; +Definition.prototype.accept = function (visitor) { + if (this.params && this.params.length) { + this.params = visitor.visitArray(this.params); + } + this.rules = visitor.visitArray(this.rules); + if (this.condition) { + this.condition = visitor.visit(this.condition); + } +}; +Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) { + /*jshint boss:true */ + var frame = new Ruleset(null, null), + varargs, arg, + params = this.params.slice(0), + i, j, val, name, isNamedFound, argIndex, argsLength = 0; + + mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames)); + + if (args) { + args = args.slice(0); + argsLength = args.length; + + for (i = 0; i < argsLength; i++) { + arg = args[i]; + if (name = (arg && arg.name)) { + isNamedFound = false; + for (j = 0; j < params.length; j++) { + if (!evaldArguments[j] && name === params[j].name) { + evaldArguments[j] = arg.value.eval(context); + frame.prependRule(new Rule(name, arg.value.eval(context))); + isNamedFound = true; + break; + } + } + if (isNamedFound) { + args.splice(i, 1); + i--; + continue; + } else { + throw { type: 'Runtime', message: "Named argument for " + this.name + + ' ' + args[i].name + ' not found' }; + } + } + } + } + argIndex = 0; + for (i = 0; i < params.length; i++) { + if (evaldArguments[i]) { continue; } + + arg = args && args[argIndex]; + + if (name = params[i].name) { + if (params[i].variadic) { + varargs = []; + for (j = argIndex; j < argsLength; j++) { + varargs.push(args[j].value.eval(context)); + } + frame.prependRule(new Rule(name, new Expression(varargs).eval(context))); + } else { + val = arg && arg.value; + if (val) { + val = val.eval(context); + } else if (params[i].value) { + val = params[i].value.eval(mixinEnv); + frame.resetCache(); + } else { + throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + + ' (' + argsLength + ' for ' + this.arity + ')' }; + } + + frame.prependRule(new Rule(name, val)); + evaldArguments[i] = val; + } + } + + if (params[i].variadic && args) { + for (j = argIndex; j < argsLength; j++) { + evaldArguments[j] = args[j].value.eval(context); + } + } + argIndex++; + } + + return frame; +}; +Definition.prototype.makeImportant = function() { + var rules = !this.rules ? this.rules : this.rules.map(function (r) { + if (r.makeImportant) { + return r.makeImportant(true); + } else { + return r; + } + }); + var result = new Definition (this.name, this.params, rules, this.condition, this.variadic, this.frames); + return result; +}; +Definition.prototype.eval = function (context) { + return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || context.frames.slice(0)); +}; +Definition.prototype.evalCall = function (context, args, important) { + var _arguments = [], + mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames, + frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments), + rules, ruleset; + + frame.prependRule(new Rule('@arguments', new Expression(_arguments).eval(context))); + + rules = this.rules.slice(0); + + ruleset = new Ruleset(null, rules); + ruleset.originalRuleset = this; + ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames))); + if (important) { + ruleset = ruleset.makeImportant(); + } + return ruleset; +}; +Definition.prototype.matchCondition = function (args, context) { + if (this.condition && !this.condition.eval( + new contexts.Eval(context, + [this.evalParams(context, /* the parameter variables*/ + new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])] + .concat(this.frames) // the parent namespace/mixin frames + .concat(context.frames)))) { // the current environment frames + return false; + } + return true; +}; +Definition.prototype.matchArgs = function (args, context) { + var argsLength = (args && args.length) || 0, len; + + if (! this.variadic) { + if (argsLength < this.required) { + return false; + } + if (argsLength > this.params.length) { + return false; + } + } else { + if (argsLength < (this.required - 1)) { + return false; + } + } + + len = Math.min(argsLength, this.arity); + + for (var i = 0; i < len; i++) { + if (!this.params[i].name && !this.params[i].variadic) { + if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) { + return false; + } + } + } + return true; +}; +module.exports = Definition; + +},{"../contexts":10,"./element":55,"./expression":56,"./rule":71,"./ruleset":73,"./selector":74}],66:[function(require,module,exports){ +var Node = require("./node"), + Operation = require("./operation"), + Dimension = require("./dimension"); + +var Negative = function (node) { + this.value = node; +}; +Negative.prototype = new Node(); +Negative.prototype.type = "Negative"; +Negative.prototype.genCSS = function (context, output) { + output.add('-'); + this.value.genCSS(context, output); +}; +Negative.prototype.eval = function (context) { + if (context.isMathOn()) { + return (new Operation('*', [new Dimension(-1), this.value])).eval(context); + } + return new Negative(this.value.eval(context)); +}; +module.exports = Negative; + +},{"./dimension":53,"./node":67,"./operation":68}],67:[function(require,module,exports){ +var Node = function() { +}; +Node.prototype.toCSS = function (context) { + var strs = []; + this.genCSS(context, { + add: function(chunk, fileInfo, index) { + strs.push(chunk); + }, + isEmpty: function () { + return strs.length === 0; + } + }); + return strs.join(''); +}; +Node.prototype.genCSS = function (context, output) { + output.add(this.value); +}; +Node.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Node.prototype.eval = function () { return this; }; +Node.prototype._operate = function (context, op, a, b) { + switch (op) { + case '+': return a + b; + case '-': return a - b; + case '*': return a * b; + case '/': return a / b; + } +}; +Node.prototype.fround = function(context, value) { + var precision = context && context.numPrecision; + //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... + return (precision == null) ? value : Number((value + 2e-16).toFixed(precision)); +}; +Node.compare = function (a, b) { + /* returns: + -1: a < b + 0: a = b + 1: a > b + and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */ + + if ((a.compare) && + // for "symmetric results" force toCSS-based comparison + // of Quoted or Anonymous if either value is one of those + !(b.type === "Quoted" || b.type === "Anonymous")) { + return a.compare(b); + } else if (b.compare) { + return -b.compare(a); + } else if (a.type !== b.type) { + return undefined; + } + + a = a.value; + b = b.value; + if (!Array.isArray(a)) { + return a === b ? 0 : undefined; + } + if (a.length !== b.length) { + return undefined; + } + for (var i = 0; i < a.length; i++) { + if (Node.compare(a[i], b[i]) !== 0) { + return undefined; + } + } + return 0; +}; + +Node.numericCompare = function (a, b) { + return a < b ? -1 + : a === b ? 0 + : a > b ? 1 : undefined; +}; +module.exports = Node; + +},{}],68:[function(require,module,exports){ +var Node = require("./node"), + Color = require("./color"), + Dimension = require("./dimension"); + +var Operation = function (op, operands, isSpaced) { + this.op = op.trim(); + this.operands = operands; + this.isSpaced = isSpaced; +}; +Operation.prototype = new Node(); +Operation.prototype.type = "Operation"; +Operation.prototype.accept = function (visitor) { + this.operands = visitor.visit(this.operands); +}; +Operation.prototype.eval = function (context) { + var a = this.operands[0].eval(context), + b = this.operands[1].eval(context); + + if (context.isMathOn()) { + if (a instanceof Dimension && b instanceof Color) { + a = a.toColor(); + } + if (b instanceof Dimension && a instanceof Color) { + b = b.toColor(); + } + if (!a.operate) { + throw { type: "Operation", + message: "Operation on an invalid type" }; + } + + return a.operate(context, this.op, b); + } else { + return new Operation(this.op, [a, b], this.isSpaced); + } +}; +Operation.prototype.genCSS = function (context, output) { + this.operands[0].genCSS(context, output); + if (this.isSpaced) { + output.add(" "); + } + output.add(this.op); + if (this.isSpaced) { + output.add(" "); + } + this.operands[1].genCSS(context, output); +}; + +module.exports = Operation; + +},{"./color":47,"./dimension":53,"./node":67}],69:[function(require,module,exports){ +var Node = require("./node"); + +var Paren = function (node) { + this.value = node; +}; +Paren.prototype = new Node(); +Paren.prototype.type = "Paren"; +Paren.prototype.genCSS = function (context, output) { + output.add('('); + this.value.genCSS(context, output); + output.add(')'); +}; +Paren.prototype.eval = function (context) { + return new Paren(this.value.eval(context)); +}; +module.exports = Paren; + +},{"./node":67}],70:[function(require,module,exports){ +var Node = require("./node"), + JsEvalNode = require("./js-eval-node"), + Variable = require("./variable"); + +var Quoted = function (str, content, escaped, index, currentFileInfo) { + this.escaped = (escaped == null) ? true : escaped; + this.value = content || ''; + this.quote = str.charAt(0); + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +Quoted.prototype = new JsEvalNode(); +Quoted.prototype.type = "Quoted"; +Quoted.prototype.genCSS = function (context, output) { + if (!this.escaped) { + output.add(this.quote, this.currentFileInfo, this.index); + } + output.add(this.value); + if (!this.escaped) { + output.add(this.quote); + } +}; +Quoted.prototype.containsVariables = function() { + return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/); +}; +Quoted.prototype.eval = function (context) { + var that = this, value = this.value; + var javascriptReplacement = function (_, exp) { + return String(that.evaluateJavaScript(exp, context)); + }; + var interpolationReplacement = function (_, name) { + var v = new Variable('@' + name, that.index, that.currentFileInfo).eval(context, true); + return (v instanceof Quoted) ? v.value : v.toCSS(); + }; + function iterativeReplace(value, regexp, replacementFnc) { + var evaluatedValue = value; + do { + value = evaluatedValue; + evaluatedValue = value.replace(regexp, replacementFnc); + } while (value !== evaluatedValue); + return evaluatedValue; + } + value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement); + value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement); + return new Quoted(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); +}; +Quoted.prototype.compare = function (other) { + // when comparing quoted strings allow the quote to differ + if (other.type === "Quoted" && !this.escaped && !other.escaped) { + return Node.numericCompare(this.value, other.value); + } else { + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; + } +}; +module.exports = Quoted; + +},{"./js-eval-node":61,"./node":67,"./variable":79}],71:[function(require,module,exports){ +var Node = require("./node"), + Value = require("./value"), + Keyword = require("./keyword"); + +var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) { + this.name = name; + this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ?? + this.important = important ? ' ' + important.trim() : ''; + this.merge = merge; + this.index = index; + this.currentFileInfo = currentFileInfo; + this.inline = inline || false; + this.variable = (variable !== undefined) ? variable + : (name.charAt && (name.charAt(0) === '@')); +}; + +function evalName(context, name) { + var value = "", i, n = name.length, + output = {add: function (s) {value += s;}}; + for (i = 0; i < n; i++) { + name[i].eval(context).genCSS(context, output); + } + return value; +} + +Rule.prototype = new Node(); +Rule.prototype.type = "Rule"; +Rule.prototype.genCSS = function (context, output) { + output.add(this.name + (context.compress ? ':' : ': '), this.currentFileInfo, this.index); + try { + this.value.genCSS(context, output); + } + catch(e) { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + throw e; + } + output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this.currentFileInfo, this.index); +}; +Rule.prototype.eval = function (context) { + var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable; + if (typeof name !== "string") { + // expand 'primitive' name directly to get + // things faster (~10% for benchmark.less): + name = (name.length === 1) && (name[0] instanceof Keyword) ? + name[0].value : evalName(context, name); + variable = false; // never treat expanded interpolation as new variable name + } + if (name === "font" && !context.strictMath) { + strictMathBypass = true; + context.strictMath = true; + } + try { + context.importantScope.push({}); + evaldValue = this.value.eval(context); + + if (!this.variable && evaldValue.type === "DetachedRuleset") { + throw { message: "Rulesets cannot be evaluated on a property.", + index: this.index, filename: this.currentFileInfo.filename }; + } + var important = this.important, + importantResult = context.importantScope.pop(); + if (!important && importantResult.important) { + important = importantResult.important; + } + + return new Rule(name, + evaldValue, + important, + this.merge, + this.index, this.currentFileInfo, this.inline, + variable); + } + catch(e) { + if (typeof e.index !== 'number') { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + } + throw e; + } + finally { + if (strictMathBypass) { + context.strictMath = false; + } + } +}; +Rule.prototype.makeImportant = function () { + return new Rule(this.name, + this.value, + "!important", + this.merge, + this.index, this.currentFileInfo, this.inline); +}; + +module.exports = Rule; + +},{"./keyword":62,"./node":67,"./value":78}],72:[function(require,module,exports){ +var Node = require("./node"), + Variable = require("./variable"); + +var RulesetCall = function (variable) { + this.variable = variable; +}; +RulesetCall.prototype = new Node(); +RulesetCall.prototype.type = "RulesetCall"; +RulesetCall.prototype.eval = function (context) { + var detachedRuleset = new Variable(this.variable).eval(context); + return detachedRuleset.callEval(context); +}; +module.exports = RulesetCall; + +},{"./node":67,"./variable":79}],73:[function(require,module,exports){ +var Node = require("./node"), + Rule = require("./rule"), + Selector = require("./selector"), + Element = require("./element"), + Paren = require("./paren"), + contexts = require("../contexts"), + defaultFunc = require("../functions/default"), + getDebugInfo = require("./debug-info"); + +var Ruleset = function (selectors, rules, strictImports) { + this.selectors = selectors; + this.rules = rules; + this._lookups = {}; + this.strictImports = strictImports; +}; +Ruleset.prototype = new Node(); +Ruleset.prototype.type = "Ruleset"; +Ruleset.prototype.isRuleset = true; +Ruleset.prototype.isRulesetLike = true; +Ruleset.prototype.accept = function (visitor) { + if (this.paths) { + visitor.visitArray(this.paths, true); + } else if (this.selectors) { + this.selectors = visitor.visitArray(this.selectors); + } + if (this.rules && this.rules.length) { + this.rules = visitor.visitArray(this.rules); + } +}; +Ruleset.prototype.eval = function (context) { + var thisSelectors = this.selectors, selectors, + selCnt, selector, i, hasOnePassingSelector = false; + + if (thisSelectors && (selCnt = thisSelectors.length)) { + selectors = []; + defaultFunc.error({ + type: "Syntax", + message: "it is currently only allowed in parametric mixin guards," + }); + for (i = 0; i < selCnt; i++) { + selector = thisSelectors[i].eval(context); + selectors.push(selector); + if (selector.evaldCondition) { + hasOnePassingSelector = true; + } + } + defaultFunc.reset(); + } else { + hasOnePassingSelector = true; + } + + var rules = this.rules ? this.rules.slice(0) : null, + ruleset = new Ruleset(selectors, rules, this.strictImports), + rule, subRule; + + ruleset.originalRuleset = this; + ruleset.root = this.root; + ruleset.firstRoot = this.firstRoot; + ruleset.allowImports = this.allowImports; + + if (this.debugInfo) { + ruleset.debugInfo = this.debugInfo; + } + + if (!hasOnePassingSelector) { + rules.length = 0; + } + + // push the current ruleset to the frames stack + var ctxFrames = context.frames; + ctxFrames.unshift(ruleset); + + // currrent selectors + var ctxSelectors = context.selectors; + if (!ctxSelectors) { + context.selectors = ctxSelectors = []; + } + ctxSelectors.unshift(this.selectors); + + // Evaluate imports + if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { + ruleset.evalImports(context); + } + + // Store the frames around mixin definitions, + // so they can be evaluated like closures when the time comes. + var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; + for (i = 0; i < rsRuleCnt; i++) { + if (rsRules[i].evalFirst) { + rsRules[i] = rsRules[i].eval(context); + } + } + + var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0; + + // Evaluate mixin calls. + for (i = 0; i < rsRuleCnt; i++) { + if (rsRules[i].type === "MixinCall") { + /*jshint loopfunc:true */ + rules = rsRules[i].eval(context).filter(function(r) { + if ((r instanceof Rule) && r.variable) { + // do not pollute the scope if the variable is + // already there. consider returning false here + // but we need a way to "return" variable from mixins + return !(ruleset.variable(r.name)); + } + return true; + }); + rsRules.splice.apply(rsRules, [i, 1].concat(rules)); + rsRuleCnt += rules.length - 1; + i += rules.length - 1; + ruleset.resetCache(); + } else if (rsRules[i].type === "RulesetCall") { + /*jshint loopfunc:true */ + rules = rsRules[i].eval(context).rules.filter(function(r) { + if ((r instanceof Rule) && r.variable) { + // do not pollute the scope at all + return false; + } + return true; + }); + rsRules.splice.apply(rsRules, [i, 1].concat(rules)); + rsRuleCnt += rules.length - 1; + i += rules.length - 1; + ruleset.resetCache(); + } + } + + // Evaluate everything else + for (i = 0; i < rsRules.length; i++) { + rule = rsRules[i]; + if (!rule.evalFirst) { + rsRules[i] = rule = rule.eval ? rule.eval(context) : rule; + } + } + + // Evaluate everything else + for (i = 0; i < rsRules.length; i++) { + rule = rsRules[i]; + // for rulesets, check if it is a css guard and can be removed + if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) { + // check if it can be folded in (e.g. & where) + if (rule.selectors[0].isJustParentSelector()) { + rsRules.splice(i--, 1); + + for (var j = 0; j < rule.rules.length; j++) { + subRule = rule.rules[j]; + if (!(subRule instanceof Rule) || !subRule.variable) { + rsRules.splice(++i, 0, subRule); + } + } + } + } + } + + // Pop the stack + ctxFrames.shift(); + ctxSelectors.shift(); + + if (context.mediaBlocks) { + for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) { + context.mediaBlocks[i].bubbleSelectors(selectors); + } + } + + return ruleset; +}; +Ruleset.prototype.evalImports = function(context) { + var rules = this.rules, i, importRules; + if (!rules) { return; } + + for (i = 0; i < rules.length; i++) { + if (rules[i].type === "Import") { + importRules = rules[i].eval(context); + if (importRules && importRules.length) { + rules.splice.apply(rules, [i, 1].concat(importRules)); + i+= importRules.length - 1; + } else { + rules.splice(i, 1, importRules); + } + this.resetCache(); + } + } +}; +Ruleset.prototype.makeImportant = function() { + var result = new Ruleset(this.selectors, this.rules.map(function (r) { + if (r.makeImportant) { + return r.makeImportant(); + } else { + return r; + } + }), this.strictImports); + + return result; +}; +Ruleset.prototype.matchArgs = function (args) { + return !args || args.length === 0; +}; +// lets you call a css selector with a guard +Ruleset.prototype.matchCondition = function (args, context) { + var lastSelector = this.selectors[this.selectors.length - 1]; + if (!lastSelector.evaldCondition) { + return false; + } + if (lastSelector.condition && + !lastSelector.condition.eval( + new contexts.Eval(context, + context.frames))) { + return false; + } + return true; +}; +Ruleset.prototype.resetCache = function () { + this._rulesets = null; + this._variables = null; + this._lookups = {}; +}; +Ruleset.prototype.variables = function () { + if (!this._variables) { + this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { + if (r instanceof Rule && r.variable === true) { + hash[r.name] = r; + } + // when evaluating variables in an import statement, imports have not been eval'd + // so we need to go inside import statements. + // guard against root being a string (in the case of inlined less) + if (r.type === "Import" && r.root && r.root.variables) { + var vars = r.root.variables(); + for (var name in vars) { + if (vars.hasOwnProperty(name)) { + hash[name] = vars[name]; + } + } + } + return hash; + }, {}); + } + return this._variables; +}; +Ruleset.prototype.variable = function (name) { + return this.variables()[name]; +}; +Ruleset.prototype.rulesets = function () { + if (!this.rules) { return null; } + + var filtRules = [], rules = this.rules, cnt = rules.length, + i, rule; + + for (i = 0; i < cnt; i++) { + rule = rules[i]; + if (rule.isRuleset) { + filtRules.push(rule); + } + } + + return filtRules; +}; +Ruleset.prototype.prependRule = function (rule) { + var rules = this.rules; + if (rules) { + rules.unshift(rule); + } else { + this.rules = [ rule ]; + } +}; +Ruleset.prototype.find = function (selector, self, filter) { + self = self || this; + var rules = [], match, foundMixins, + key = selector.toCSS(); + + if (key in this._lookups) { return this._lookups[key]; } + + this.rulesets().forEach(function (rule) { + if (rule !== self) { + for (var j = 0; j < rule.selectors.length; j++) { + match = selector.match(rule.selectors[j]); + if (match) { + if (selector.elements.length > match) { + if (!filter || filter(rule)) { + foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter); + for (var i = 0; i < foundMixins.length; ++i) { + foundMixins[i].path.push(rule); + } + Array.prototype.push.apply(rules, foundMixins); + } + } else { + rules.push({ rule: rule, path: []}); + } + break; + } + } + } + }); + this._lookups[key] = rules; + return rules; +}; +Ruleset.prototype.genCSS = function (context, output) { + var i, j, + charsetRuleNodes = [], + ruleNodes = [], + rulesetNodes = [], + rulesetNodeCnt, + debugInfo, // Line number debugging + rule, + path; + + context.tabLevel = (context.tabLevel || 0); + + if (!this.root) { + context.tabLevel++; + } + + var tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(" "), + tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "), + sep; + + function isRulesetLikeNode(rule, root) { + // if it has nested rules, then it should be treated like a ruleset + // medias and comments do not have nested rules, but should be treated like rulesets anyway + // some directives and anonymous nodes are ruleset like, others are not + if (typeof rule.isRulesetLike === "boolean") { + return rule.isRulesetLike; + } else if (typeof rule.isRulesetLike === "function") { + return rule.isRulesetLike(root); + } + + //anything else is assumed to be a rule + return false; + } + + for (i = 0; i < this.rules.length; i++) { + rule = this.rules[i]; + if (isRulesetLikeNode(rule, this.root)) { + rulesetNodes.push(rule); + } else { + //charsets should float on top of everything + if (rule.isCharset && rule.isCharset()) { + charsetRuleNodes.push(rule); + } else { + ruleNodes.push(rule); + } + } + } + ruleNodes = charsetRuleNodes.concat(ruleNodes); + + // If this is the root node, we don't render + // a selector, or {}. + if (!this.root) { + debugInfo = getDebugInfo(context, this, tabSetStr); + + if (debugInfo) { + output.add(debugInfo); + output.add(tabSetStr); + } + + var paths = this.paths, pathCnt = paths.length, + pathSubCnt; + + sep = context.compress ? ',' : (',\n' + tabSetStr); + + for (i = 0; i < pathCnt; i++) { + path = paths[i]; + if (!(pathSubCnt = path.length)) { continue; } + if (i > 0) { output.add(sep); } + + context.firstSelector = true; + path[0].genCSS(context, output); + + context.firstSelector = false; + for (j = 1; j < pathSubCnt; j++) { + path[j].genCSS(context, output); + } + } + + output.add((context.compress ? '{' : ' {\n') + tabRuleStr); + } + + // Compile rules and rulesets + for (i = 0; i < ruleNodes.length; i++) { + rule = ruleNodes[i]; + + // @page{ directive ends up with root elements inside it, a mix of rules and rulesets + // In this instance we do not know whether it is the last property + if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { + context.lastRule = true; + } + + if (rule.genCSS) { + rule.genCSS(context, output); + } else if (rule.value) { + output.add(rule.value.toString()); + } + + if (!context.lastRule) { + output.add(context.compress ? '' : ('\n' + tabRuleStr)); + } else { + context.lastRule = false; + } + } + + if (!this.root) { + output.add((context.compress ? '}' : '\n' + tabSetStr + '}')); + context.tabLevel--; + } + + sep = (context.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); + rulesetNodeCnt = rulesetNodes.length; + if (rulesetNodeCnt) { + if (ruleNodes.length && sep) { output.add(sep); } + rulesetNodes[0].genCSS(context, output); + for (i = 1; i < rulesetNodeCnt; i++) { + if (sep) { output.add(sep); } + rulesetNodes[i].genCSS(context, output); + } + } + + if (!output.isEmpty() && !context.compress && this.firstRoot) { + output.add('\n'); + } +}; +Ruleset.prototype.markReferenced = function () { + var s; + if (this.selectors) { + for (s = 0; s < this.selectors.length; s++) { + this.selectors[s].markReferenced(); + } + } + + if (this.rules) { + for (s = 0; s < this.rules.length; s++) { + if (this.rules[s].markReferenced) { + this.rules[s].markReferenced(); + } + } + } +}; +Ruleset.prototype.getIsReferenced = function() { + var i, j, path, selector; + + if (this.paths) { + for (i = 0; i < this.paths.length; i++) { + path = this.paths[i]; + for (j = 0; j < path.length; j++) { + if (path[j].getIsReferenced && path[j].getIsReferenced()) { + return true; + } + } + } + } + + if (this.selectors) { + for (i = 0; i < this.selectors.length; i++) { + selector = this.selectors[i]; + if (selector.getIsReferenced && selector.getIsReferenced()) { + return true; + } + } + } + return false; +}; + +Ruleset.prototype.joinSelectors = function (paths, context, selectors) { + for (var s = 0; s < selectors.length; s++) { + this.joinSelector(paths, context, selectors[s]); + } +}; + +Ruleset.prototype.joinSelector = function (paths, context, selector) { + + function createParenthesis(elementsToPak, originalElement) { + var replacementParen, j; + if (elementsToPak.length === 0) { + replacementParen = new Paren(elementsToPak[0]); + } else { + var insideParent = []; + for (j = 0; j < elementsToPak.length; j++) { + insideParent.push(new Element(null, elementsToPak[j], originalElement.index, originalElement.currentFileInfo)); + } + replacementParen = new Paren(new Selector(insideParent)); + } + return replacementParen; + } + + function createSelector(containedElement, originalElement) { + var element, selector; + element = new Element(null, containedElement, originalElement.index, originalElement.currentFileInfo); + selector = new Selector([element]); + return selector; + } + + // replace all parent selectors inside `inSelector` by content of `context` array + // resulting selectors are returned inside `paths` array + // returns true if `inSelector` contained at least one parent selector + function replaceParentSelector(paths, context, inSelector) { + // The paths are [[Selector]] + // The first list is a list of comma separated selectors + // The inner list is a list of inheritance separated selectors + // e.g. + // .a, .b { + // .c { + // } + // } + // == [[.a] [.c]] [[.b] [.c]] + // + var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector; + function findNestedSelector(element) { + var maybeSelector; + if (element.value.type !== 'Paren') { + return null; + } + + maybeSelector = element.value.value; + if (maybeSelector.type !== 'Selector') { + return null; + } + + return maybeSelector; + } + + // the elements from the current selector so far + currentElements = []; + // the current list of new selectors to add to the path. + // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors + // by the parents + newSelectors = [ + [] + ]; + + for (i = 0; i < inSelector.elements.length; i++) { + el = inSelector.elements[i]; + // non parent reference elements just get added + if (el.value !== "&") { + var nestedSelector = findNestedSelector(el); + if (nestedSelector != null) { + // merge the current list of non parent selector elements + // on to the current list of selectors to add + mergeElementsOnToSelectors(currentElements, newSelectors); + + var nestedPaths = [], replaced, replacedNewSelectors = []; + replaced = replaceParentSelector(nestedPaths, context, nestedSelector); + hadParentSelector = hadParentSelector || replaced; + //the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors + for (k = 0; k < nestedPaths.length; k++) { + var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el); + addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors); + } + newSelectors = replacedNewSelectors; + currentElements = []; + + } else { + currentElements.push(el); + } + + } else { + hadParentSelector = true; + // the new list of selectors to add + selectorsMultiplied = []; + + // merge the current list of non parent selector elements + // on to the current list of selectors to add + mergeElementsOnToSelectors(currentElements, newSelectors); + + // loop through our current selectors + for (j = 0; j < newSelectors.length; j++) { + sel = newSelectors[j]; + // if we don't have any parent paths, the & might be in a mixin so that it can be used + // whether there are parents or not + if (context.length === 0) { + // the combinator used on el should now be applied to the next element instead so that + // it is not lost + if (sel.length > 0) { + sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo)); + } + selectorsMultiplied.push(sel); + } + else { + // and the parent selectors + for (k = 0; k < context.length; k++) { + // We need to put the current selectors + // then join the last selector's elements on to the parents selectors + var newSelectorPath = addReplacementIntoPath(sel, context[k], el, inSelector); + // add that to our new set of selectors + selectorsMultiplied.push(newSelectorPath); + } + } + } + + // our new selectors has been multiplied, so reset the state + newSelectors = selectorsMultiplied; + currentElements = []; + } + } + + // if we have any elements left over (e.g. .a& .b == .b) + // add them on to all the current selectors + mergeElementsOnToSelectors(currentElements, newSelectors); + + for (i = 0; i < newSelectors.length; i++) { + length = newSelectors[i].length; + if (length > 0) { + paths.push(newSelectors[i]); + lastSelector = newSelectors[i][length - 1]; + newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList); + } + } + + return hadParentSelector; + } + + // joins selector path from `beginningPath` with selector path in `addPath` + // `replacedElement` contains element that is being replaced by `addPath` + // returns concatenated path + function addReplacementIntoPath(beginningPath, addPath, replacedElement, originalSelector) { + var newSelectorPath, lastSelector, newJoinedSelector; + // our new selector path + newSelectorPath = []; + + //construct the joined selector - if & is the first thing this will be empty, + // if not newJoinedSelector will be the last set of elements in the selector + if (beginningPath.length > 0) { + newSelectorPath = beginningPath.slice(0); + lastSelector = newSelectorPath.pop(); + newJoinedSelector = originalSelector.createDerived(lastSelector.elements.slice(0)); + } + else { + newJoinedSelector = originalSelector.createDerived([]); + } + + if (addPath.length > 0) { + // /deep/ is a combinator that is valid without anything in front of it + // so if the & does not have a combinator that is "" or " " then + // and there is a combinator on the parent, then grab that. + // this also allows + a { & .b { .a & { ... though not sure why you would want to do that + var combinator = replacedElement.combinator, parentEl = addPath[0].elements[0]; + if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) { + combinator = parentEl.combinator; + } + // join the elements so far with the first part of the parent + newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement.index, replacedElement.currentFileInfo)); + newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1)); + } + + // now add the joined selector - but only if it is not empty + if (newJoinedSelector.elements.length !== 0) { + newSelectorPath.push(newJoinedSelector); + } + + //put together the parent selectors after the join (e.g. the rest of the parent) + if (addPath.length > 1) { + newSelectorPath = newSelectorPath.concat(addPath.slice(1)); + } + return newSelectorPath; + } + + // joins selector path from `beginningPath` with every selector path in `addPaths` array + // `replacedElement` contains element that is being replaced by `addPath` + // returns array with all concatenated paths + function addAllReplacementsIntoPath( beginningPath, addPaths, replacedElement, originalSelector, result) { + var j; + for (j = 0; j < beginningPath.length; j++) { + var newSelectorPath = addReplacementIntoPath(beginningPath[j], addPaths, replacedElement, originalSelector); + result.push(newSelectorPath); + } + return result; + } + + function mergeElementsOnToSelectors(elements, selectors) { + var i, sel; + + if (elements.length === 0) { + return ; + } + if (selectors.length === 0) { + selectors.push([ new Selector(elements) ]); + return; + } + + for (i = 0; i < selectors.length; i++) { + sel = selectors[i]; + + // if the previous thing in sel is a parent this needs to join on to it + if (sel.length > 0) { + sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); + } + else { + sel.push(new Selector(elements)); + } + } + } + + // joinSelector code follows + var i, newPaths, hadParentSelector; + + newPaths = []; + hadParentSelector = replaceParentSelector(newPaths, context, selector); + + if (!hadParentSelector) { + if (context.length > 0) { + newPaths = []; + for (i = 0; i < context.length; i++) { + newPaths.push(context[i].concat(selector)); + } + } + else { + newPaths = [[selector]]; + } + } + + for (i = 0; i < newPaths.length; i++) { + paths.push(newPaths[i]); + } + +}; +module.exports = Ruleset; + +},{"../contexts":10,"../functions/default":19,"./debug-info":51,"./element":55,"./node":67,"./paren":69,"./rule":71,"./selector":74}],74:[function(require,module,exports){ +var Node = require("./node"); + +var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { + this.elements = elements; + this.extendList = extendList; + this.condition = condition; + this.currentFileInfo = currentFileInfo || {}; + this.isReferenced = isReferenced; + if (!condition) { + this.evaldCondition = true; + } +}; +Selector.prototype = new Node(); +Selector.prototype.type = "Selector"; +Selector.prototype.accept = function (visitor) { + if (this.elements) { + this.elements = visitor.visitArray(this.elements); + } + if (this.extendList) { + this.extendList = visitor.visitArray(this.extendList); + } + if (this.condition) { + this.condition = visitor.visit(this.condition); + } +}; +Selector.prototype.createDerived = function(elements, extendList, evaldCondition) { + evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; + var newSelector = new Selector(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); + newSelector.evaldCondition = evaldCondition; + newSelector.mediaEmpty = this.mediaEmpty; + return newSelector; +}; +Selector.prototype.match = function (other) { + var elements = this.elements, + len = elements.length, + olen, i; + + other.CacheElements(); + + olen = other._elements.length; + if (olen === 0 || len < olen) { + return 0; + } else { + for (i = 0; i < olen; i++) { + if (elements[i].value !== other._elements[i]) { + return 0; + } + } + } + + return olen; // return number of matched elements +}; +Selector.prototype.CacheElements = function() { + if (this._elements) { + return; + } + + var elements = this.elements.map( function(v) { + return v.combinator.value + (v.value.value || v.value); + }).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); + + if (elements) { + if (elements[0] === "&") { + elements.shift(); + } + } else { + elements = []; + } + + this._elements = elements; +}; +Selector.prototype.isJustParentSelector = function() { + return !this.mediaEmpty && + this.elements.length === 1 && + this.elements[0].value === '&' && + (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); +}; +Selector.prototype.eval = function (context) { + var evaldCondition = this.condition && this.condition.eval(context), + elements = this.elements, extendList = this.extendList; + + elements = elements && elements.map(function (e) { return e.eval(context); }); + extendList = extendList && extendList.map(function(extend) { return extend.eval(context); }); + + return this.createDerived(elements, extendList, evaldCondition); +}; +Selector.prototype.genCSS = function (context, output) { + var i, element; + if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") { + output.add(' ', this.currentFileInfo, this.index); + } + if (!this._css) { + //TODO caching? speed comparison? + for (i = 0; i < this.elements.length; i++) { + element = this.elements[i]; + element.genCSS(context, output); + } + } +}; +Selector.prototype.markReferenced = function () { + this.isReferenced = true; +}; +Selector.prototype.getIsReferenced = function() { + return !this.currentFileInfo.reference || this.isReferenced; +}; +Selector.prototype.getIsOutput = function() { + return this.evaldCondition; +}; +module.exports = Selector; + +},{"./node":67}],75:[function(require,module,exports){ +var Node = require("./node"); + +var UnicodeDescriptor = function (value) { + this.value = value; +}; +UnicodeDescriptor.prototype = new Node(); +UnicodeDescriptor.prototype.type = "UnicodeDescriptor"; + +module.exports = UnicodeDescriptor; + +},{"./node":67}],76:[function(require,module,exports){ +var Node = require("./node"), + unitConversions = require("../data/unit-conversions"); + +var Unit = function (numerator, denominator, backupUnit) { + this.numerator = numerator ? numerator.slice(0).sort() : []; + this.denominator = denominator ? denominator.slice(0).sort() : []; + if (backupUnit) { + this.backupUnit = backupUnit; + } else if (numerator && numerator.length) { + this.backupUnit = numerator[0]; + } +}; + +Unit.prototype = new Node(); +Unit.prototype.type = "Unit"; +Unit.prototype.clone = function () { + return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); +}; +Unit.prototype.genCSS = function (context, output) { + // Dimension checks the unit is singular and throws an error if in strict math mode. + var strictUnits = context && context.strictUnits; + if (this.numerator.length === 1) { + output.add(this.numerator[0]); // the ideal situation + } else if (!strictUnits && this.backupUnit) { + output.add(this.backupUnit); + } else if (!strictUnits && this.denominator.length) { + output.add(this.denominator[0]); + } +}; +Unit.prototype.toString = function () { + var i, returnStr = this.numerator.join("*"); + for (i = 0; i < this.denominator.length; i++) { + returnStr += "/" + this.denominator[i]; + } + return returnStr; +}; +Unit.prototype.compare = function (other) { + return this.is(other.toString()) ? 0 : undefined; +}; +Unit.prototype.is = function (unitString) { + return this.toString().toUpperCase() === unitString.toUpperCase(); +}; +Unit.prototype.isLength = function () { + return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); +}; +Unit.prototype.isEmpty = function () { + return this.numerator.length === 0 && this.denominator.length === 0; +}; +Unit.prototype.isSingular = function() { + return this.numerator.length <= 1 && this.denominator.length === 0; +}; +Unit.prototype.map = function(callback) { + var i; + + for (i = 0; i < this.numerator.length; i++) { + this.numerator[i] = callback(this.numerator[i], false); + } + + for (i = 0; i < this.denominator.length; i++) { + this.denominator[i] = callback(this.denominator[i], true); + } +}; +Unit.prototype.usedUnits = function() { + var group, result = {}, mapUnit; + + mapUnit = function (atomicUnit) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { + result[groupName] = atomicUnit; + } + + return atomicUnit; + }; + + for (var groupName in unitConversions) { + if (unitConversions.hasOwnProperty(groupName)) { + group = unitConversions[groupName]; + + this.map(mapUnit); + } + } + + return result; +}; +Unit.prototype.cancel = function () { + var counter = {}, atomicUnit, i; + + for (i = 0; i < this.numerator.length; i++) { + atomicUnit = this.numerator[i]; + counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; + } + + for (i = 0; i < this.denominator.length; i++) { + atomicUnit = this.denominator[i]; + counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; + } + + this.numerator = []; + this.denominator = []; + + for (atomicUnit in counter) { + if (counter.hasOwnProperty(atomicUnit)) { + var count = counter[atomicUnit]; + + if (count > 0) { + for (i = 0; i < count; i++) { + this.numerator.push(atomicUnit); + } + } else if (count < 0) { + for (i = 0; i < -count; i++) { + this.denominator.push(atomicUnit); + } + } + } + } + + this.numerator.sort(); + this.denominator.sort(); +}; +module.exports = Unit; + +},{"../data/unit-conversions":13,"./node":67}],77:[function(require,module,exports){ +var Node = require("./node"); + +var URL = function (val, index, currentFileInfo, isEvald) { + this.value = val; + this.currentFileInfo = currentFileInfo; + this.index = index; + this.isEvald = isEvald; +}; +URL.prototype = new Node(); +URL.prototype.type = "Url"; +URL.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +URL.prototype.genCSS = function (context, output) { + output.add("url("); + this.value.genCSS(context, output); + output.add(")"); +}; +URL.prototype.eval = function (context) { + var val = this.value.eval(context), + rootpath; + + if (!this.isEvald) { + // Add the base path if the URL is relative + rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; + if (rootpath && + typeof val.value === "string" && + context.isPathRelative(val.value)) { + + if (!val.quote) { + rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\" + match; }); + } + val.value = rootpath + val.value; + } + + val.value = context.normalizePath(val.value); + + // Add url args if enabled + if (context.urlArgs) { + if (!val.value.match(/^\s*data:/)) { + var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; + var urlArgs = delimiter + context.urlArgs; + if (val.value.indexOf('#') !== -1) { + val.value = val.value.replace('#', urlArgs + '#'); + } else { + val.value += urlArgs; + } + } + } + } + + return new URL(val, this.index, this.currentFileInfo, true); +}; +module.exports = URL; + +},{"./node":67}],78:[function(require,module,exports){ +var Node = require("./node"); + +var Value = function (value) { + this.value = value; + if (!value) { + throw new Error("Value requires an array argument"); + } +}; +Value.prototype = new Node(); +Value.prototype.type = "Value"; +Value.prototype.accept = function (visitor) { + if (this.value) { + this.value = visitor.visitArray(this.value); + } +}; +Value.prototype.eval = function (context) { + if (this.value.length === 1) { + return this.value[0].eval(context); + } else { + return new Value(this.value.map(function (v) { + return v.eval(context); + })); + } +}; +Value.prototype.genCSS = function (context, output) { + var i; + for (i = 0; i < this.value.length; i++) { + this.value[i].genCSS(context, output); + if (i + 1 < this.value.length) { + output.add((context && context.compress) ? ',' : ', '); + } + } +}; +module.exports = Value; + +},{"./node":67}],79:[function(require,module,exports){ +var Node = require("./node"); + +var Variable = function (name, index, currentFileInfo) { + this.name = name; + this.index = index; + this.currentFileInfo = currentFileInfo || {}; +}; +Variable.prototype = new Node(); +Variable.prototype.type = "Variable"; +Variable.prototype.eval = function (context) { + var variable, name = this.name; + + if (name.indexOf('@@') === 0) { + name = '@' + new Variable(name.slice(1), this.index, this.currentFileInfo).eval(context).value; + } + + if (this.evaluating) { + throw { type: 'Name', + message: "Recursive variable definition for " + name, + filename: this.currentFileInfo.filename, + index: this.index }; + } + + this.evaluating = true; + + variable = this.find(context.frames, function (frame) { + var v = frame.variable(name); + if (v) { + if (v.important) { + var importantScope = context.importantScope[context.importantScope.length - 1]; + importantScope.important = v.important; + } + return v.value.eval(context); + } + }); + if (variable) { + this.evaluating = false; + return variable; + } else { + throw { type: 'Name', + message: "variable " + name + " is undefined", + filename: this.currentFileInfo.filename, + index: this.index }; + } +}; +Variable.prototype.find = function (obj, fun) { + for (var i = 0, r; i < obj.length; i++) { + r = fun.call(obj, obj[i]); + if (r) { return r; } + } + return null; +}; +module.exports = Variable; + +},{"./node":67}],80:[function(require,module,exports){ +module.exports = { + getLocation: function(index, inputStream) { + var n = index + 1, + line = null, + column = -1; + + while (--n >= 0 && inputStream.charAt(n) !== '\n') { + column++; + } + + if (typeof index === 'number') { + line = (inputStream.slice(0, index).match(/\n/g) || "").length; + } + + return { + line: line, + column: column + }; + } +}; + +},{}],81:[function(require,module,exports){ +var tree = require("../tree"), + Visitor = require("./visitor"), + logger = require("../logger"); + +/*jshint loopfunc:true */ + +var ExtendFinderVisitor = function() { + this._visitor = new Visitor(this); + this.contexts = []; + this.allExtendsStack = [[]]; +}; + +ExtendFinderVisitor.prototype = { + run: function (root) { + root = this._visitor.visit(root); + root.allExtends = this.allExtendsStack[0]; + return root; + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitRuleset: function (rulesetNode, visitArgs) { + if (rulesetNode.root) { + return; + } + + var i, j, extend, allSelectorsExtendList = [], extendList; + + // get &:extend(.a); rules which apply to all selectors in this ruleset + var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; + for (i = 0; i < ruleCnt; i++) { + if (rulesetNode.rules[i] instanceof tree.Extend) { + allSelectorsExtendList.push(rules[i]); + rulesetNode.extendOnEveryPath = true; + } + } + + // now find every selector and apply the extends that apply to all extends + // and the ones which apply to an individual extend + var paths = rulesetNode.paths; + for (i = 0; i < paths.length; i++) { + var selectorPath = paths[i], + selector = selectorPath[selectorPath.length - 1], + selExtendList = selector.extendList; + + extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) + : allSelectorsExtendList; + + if (extendList) { + extendList = extendList.map(function(allSelectorsExtend) { + return allSelectorsExtend.clone(); + }); + } + + for (j = 0; j < extendList.length; j++) { + this.foundExtends = true; + extend = extendList[j]; + extend.findSelfSelectors(selectorPath); + extend.ruleset = rulesetNode; + if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } + this.allExtendsStack[this.allExtendsStack.length - 1].push(extend); + } + } + + this.contexts.push(rulesetNode.selectors); + }, + visitRulesetOut: function (rulesetNode) { + if (!rulesetNode.root) { + this.contexts.length = this.contexts.length - 1; + } + }, + visitMedia: function (mediaNode, visitArgs) { + mediaNode.allExtends = []; + this.allExtendsStack.push(mediaNode.allExtends); + }, + visitMediaOut: function (mediaNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + }, + visitDirective: function (directiveNode, visitArgs) { + directiveNode.allExtends = []; + this.allExtendsStack.push(directiveNode.allExtends); + }, + visitDirectiveOut: function (directiveNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + } +}; + +var ProcessExtendsVisitor = function() { + this._visitor = new Visitor(this); +}; + +ProcessExtendsVisitor.prototype = { + run: function(root) { + var extendFinder = new ExtendFinderVisitor(); + this.extendIndicies = {}; + extendFinder.run(root); + if (!extendFinder.foundExtends) { return root; } + root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); + this.allExtendsStack = [root.allExtends]; + var newRoot = this._visitor.visit(root); + this.checkExtendsForNonMatched(root.allExtends); + return newRoot; + }, + checkExtendsForNonMatched: function(extendList) { + var indicies = this.extendIndicies; + extendList.filter(function(extend) { + return !extend.hasFoundMatches && extend.parent_ids.length == 1; + }).forEach(function(extend) { + var selector = "_unknown_"; + try { + selector = extend.selector.toCSS({}); + } + catch(_) {} + + if (!indicies[extend.index + ' ' + selector]) { + indicies[extend.index + ' ' + selector] = true; + logger.warn("extend '" + selector + "' has no matches"); + } + }); + }, + doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { + // + // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering + // and pasting the selector we would do normally, but we are also adding an extend with the same target selector + // this means this new extend can then go and alter other extends + // + // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors + // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already + // processed if we look at each selector at a time, as is done in visitRuleset + + var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, + extend, targetExtend, newExtend; + + iterationCount = iterationCount || 0; + + //loop through comparing every extend with every target extend. + // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place + // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one + // and the second is the target. + // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the + // case when processing media queries + for (extendIndex = 0; extendIndex < extendsList.length; extendIndex++) { + for (targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++) { + + extend = extendsList[extendIndex]; + targetExtend = extendsListTarget[targetExtendIndex]; + + // look for circular references + if ( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ) { continue; } + + // find a match in the target extends self selector (the bit before :extend) + selectorPath = [targetExtend.selfSelectors[0]]; + matches = extendVisitor.findMatch(extend, selectorPath); + + if (matches.length) { + + extend.hasFoundMatches = true; + + // we found a match, so for each self selector.. + extend.selfSelectors.forEach(function(selfSelector) { + + // process the extend as usual + newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); + + // but now we create a new extend from it + newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); + newExtend.selfSelectors = newSelector; + + // add the extend onto the list of extends for that selector + newSelector[newSelector.length - 1].extendList = [newExtend]; + + // record that we need to add it. + extendsToAdd.push(newExtend); + newExtend.ruleset = targetExtend.ruleset; + + //remember its parents for circular references + newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids); + + // only process the selector once.. if we have :extend(.a,.b) then multiple + // extends will look at the same selector path, so when extending + // we know that any others will be duplicates in terms of what is added to the css + if (targetExtend.firstExtendOnThisSelectorPath) { + newExtend.firstExtendOnThisSelectorPath = true; + targetExtend.ruleset.paths.push(newSelector); + } + }); + } + } + } + + if (extendsToAdd.length) { + // try to detect circular references to stop a stack overflow. + // may no longer be needed. + this.extendChainCount++; + if (iterationCount > 100) { + var selectorOne = "{unable to calculate}"; + var selectorTwo = "{unable to calculate}"; + try + { + selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); + selectorTwo = extendsToAdd[0].selector.toCSS(); + } + catch(e) {} + throw { message: "extend circular reference detected. One of the circular extends is currently:" + + selectorOne + ":extend(" + selectorTwo + ")"}; + } + + // now process the new extends on the existing rules so that we can handle a extending b extending c extending + // d extending e... + return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount + 1)); + } else { + return extendsToAdd; + } + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitSelector: function (selectorNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitRuleset: function (rulesetNode, visitArgs) { + if (rulesetNode.root) { + return; + } + var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length - 1], + selectorsToAdd = [], extendVisitor = this, selectorPath; + + // look at each selector path in the ruleset, find any extend matches and then copy, find and replace + + for (extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { + for (pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { + selectorPath = rulesetNode.paths[pathIndex]; + + // extending extends happens initially, before the main pass + if (rulesetNode.extendOnEveryPath) { continue; } + var extendList = selectorPath[selectorPath.length - 1].extendList; + if (extendList && extendList.length) { continue; } + + matches = this.findMatch(allExtends[extendIndex], selectorPath); + + if (matches.length) { + allExtends[extendIndex].hasFoundMatches = true; + + allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { + selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); + }); + } + } + } + rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); + }, + findMatch: function (extend, haystackSelectorPath) { + // + // look through the haystack selector path to try and find the needle - extend.selector + // returns an array of selector matches that can then be replaced + // + var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, + targetCombinator, i, + extendVisitor = this, + needleElements = extend.selector.elements, + potentialMatches = [], potentialMatch, matches = []; + + // loop through the haystack elements + for (haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { + hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; + + for (hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) { + + haystackElement = hackstackSelector.elements[hackstackElementIndex]; + + // if we allow elements before our match we can add a potential match every time. otherwise only at the first element. + if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) { + potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, + initialCombinator: haystackElement.combinator}); + } + + for (i = 0; i < potentialMatches.length; i++) { + potentialMatch = potentialMatches[i]; + + // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't + // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to + // work out what the resulting combinator will be + targetCombinator = haystackElement.combinator.value; + if (targetCombinator === '' && hackstackElementIndex === 0) { + targetCombinator = ' '; + } + + // if we don't match, null our match to indicate failure + if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || + (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { + potentialMatch = null; + } else { + potentialMatch.matched++; + } + + // if we are still valid and have finished, test whether we have elements after and whether these are allowed + if (potentialMatch) { + potentialMatch.finished = potentialMatch.matched === needleElements.length; + if (potentialMatch.finished && + (!extend.allowAfter && + (hackstackElementIndex + 1 < hackstackSelector.elements.length || haystackSelectorIndex + 1 < haystackSelectorPath.length))) { + potentialMatch = null; + } + } + // if null we remove, if not, we are still valid, so either push as a valid match or continue + if (potentialMatch) { + if (potentialMatch.finished) { + potentialMatch.length = needleElements.length; + potentialMatch.endPathIndex = haystackSelectorIndex; + potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match + potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again + matches.push(potentialMatch); + } + } else { + potentialMatches.splice(i, 1); + i--; + } + } + } + } + return matches; + }, + isElementValuesEqual: function(elementValue1, elementValue2) { + if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { + return elementValue1 === elementValue2; + } + if (elementValue1 instanceof tree.Attribute) { + if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) { + return false; + } + if (!elementValue1.value || !elementValue2.value) { + if (elementValue1.value || elementValue2.value) { + return false; + } + return true; + } + elementValue1 = elementValue1.value.value || elementValue1.value; + elementValue2 = elementValue2.value.value || elementValue2.value; + return elementValue1 === elementValue2; + } + elementValue1 = elementValue1.value; + elementValue2 = elementValue2.value; + if (elementValue1 instanceof tree.Selector) { + if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { + return false; + } + for (var i = 0; i < elementValue1.elements.length; i++) { + if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) { + if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) { + return false; + } + } + if (!this.isElementValuesEqual(elementValue1.elements[i].value, elementValue2.elements[i].value)) { + return false; + } + } + return true; + } + return false; + }, + extendSelector:function (matches, selectorPath, replacementSelector) { + + //for a set of matches, replace each match with the replacement selector + + var currentSelectorPathIndex = 0, + currentSelectorPathElementIndex = 0, + path = [], + matchIndex, + selector, + firstElement, + match, + newElements; + + for (matchIndex = 0; matchIndex < matches.length; matchIndex++) { + match = matches[matchIndex]; + selector = selectorPath[match.pathIndex]; + firstElement = new tree.Element( + match.initialCombinator, + replacementSelector.elements[0].value, + replacementSelector.elements[0].index, + replacementSelector.elements[0].currentFileInfo + ); + + if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { + path[path.length - 1].elements = path[path.length - 1] + .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathElementIndex = 0; + currentSelectorPathIndex++; + } + + newElements = selector.elements + .slice(currentSelectorPathElementIndex, match.index) + .concat([firstElement]) + .concat(replacementSelector.elements.slice(1)); + + if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { + path[path.length - 1].elements = + path[path.length - 1].elements.concat(newElements); + } else { + path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); + + path.push(new tree.Selector( + newElements + )); + } + currentSelectorPathIndex = match.endPathIndex; + currentSelectorPathElementIndex = match.endPathElementIndex; + if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { + currentSelectorPathElementIndex = 0; + currentSelectorPathIndex++; + } + } + + if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { + path[path.length - 1].elements = path[path.length - 1] + .elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathIndex++; + } + + path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); + + return path; + }, + visitRulesetOut: function (rulesetNode) { + }, + visitMedia: function (mediaNode, visitArgs) { + var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]); + newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); + this.allExtendsStack.push(newAllExtends); + }, + visitMediaOut: function (mediaNode) { + var lastIndex = this.allExtendsStack.length - 1; + this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]); + this.allExtendsStack.length = lastIndex; + }, + visitDirective: function (directiveNode, visitArgs) { + var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]); + newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); + this.allExtendsStack.push(newAllExtends); + }, + visitDirectiveOut: function (directiveNode) { + var lastIndex = this.allExtendsStack.length - 1; + this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]); + this.allExtendsStack.length = lastIndex; + } +}; + +module.exports = ProcessExtendsVisitor; + +},{"../logger":31,"../tree":59,"./visitor":87}],82:[function(require,module,exports){ +function ImportSequencer(onSequencerEmpty) { + this.imports = []; + this.variableImports = []; + this._onSequencerEmpty = onSequencerEmpty; + this._currentDepth = 0; +} + +ImportSequencer.prototype.addImport = function(callback) { + var importSequencer = this, + importItem = { + callback: callback, + args: null, + isReady: false + }; + this.imports.push(importItem); + return function() { + importItem.args = Array.prototype.slice.call(arguments, 0); + importItem.isReady = true; + importSequencer.tryRun(); + }; +}; + +ImportSequencer.prototype.addVariableImport = function(callback) { + this.variableImports.push(callback); +}; + +ImportSequencer.prototype.tryRun = function() { + this._currentDepth++; + try { + while (true) { + while (this.imports.length > 0) { + var importItem = this.imports[0]; + if (!importItem.isReady) { + return; + } + this.imports = this.imports.slice(1); + importItem.callback.apply(null, importItem.args); + } + if (this.variableImports.length === 0) { + break; + } + var variableImport = this.variableImports[0]; + this.variableImports = this.variableImports.slice(1); + variableImport(); + } + } finally { + this._currentDepth--; + } + if (this._currentDepth === 0 && this._onSequencerEmpty) { + this._onSequencerEmpty(); + } +}; + +module.exports = ImportSequencer; + +},{}],83:[function(require,module,exports){ +var contexts = require("../contexts"), + Visitor = require("./visitor"), + ImportSequencer = require("./import-sequencer"); + +var ImportVisitor = function(importer, finish) { + + this._visitor = new Visitor(this); + this._importer = importer; + this._finish = finish; + this.context = new contexts.Eval(); + this.importCount = 0; + this.onceFileDetectionMap = {}; + this.recursionDetector = {}; + this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this)); +}; + +ImportVisitor.prototype = { + isReplacing: false, + run: function (root) { + try { + // process the contents + this._visitor.visit(root); + } + catch(e) { + this.error = e; + } + + this.isFinished = true; + this._sequencer.tryRun(); + }, + _onSequencerEmpty: function() { + if (!this.isFinished) { + return; + } + this._finish(this.error); + }, + visitImport: function (importNode, visitArgs) { + var inlineCSS = importNode.options.inline; + + if (!importNode.css || inlineCSS) { + + var context = new contexts.Eval(this.context, this.context.frames.slice(0)); + var importParent = context.frames[0]; + + this.importCount++; + if (importNode.isVariableImport()) { + this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent)); + } else { + this.processImportNode(importNode, context, importParent); + } + } + visitArgs.visitDeeper = false; + }, + processImportNode: function(importNode, context, importParent) { + var evaldImportNode, + inlineCSS = importNode.options.inline; + + try { + evaldImportNode = importNode.evalForImport(context); + } catch(e) { + if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } + // attempt to eval properly and treat as css + importNode.css = true; + // if that fails, this error will be thrown + importNode.error = e; + } + + if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { + + if (evaldImportNode.options.multiple) { + context.importMultiple = true; + } + + // try appending if we haven't determined if it is css or not + var tryAppendLessExtension = evaldImportNode.css === undefined; + + for (var i = 0; i < importParent.rules.length; i++) { + if (importParent.rules[i] === importNode) { + importParent.rules[i] = evaldImportNode; + break; + } + } + + var onImported = this.onImported.bind(this, evaldImportNode, context), + sequencedOnImported = this._sequencer.addImport(onImported); + + this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo, + evaldImportNode.options, sequencedOnImported); + } else { + this.importCount--; + if (this.isFinished) { + this._sequencer.tryRun(); + } + } + }, + onImported: function (importNode, context, e, root, importedAtRoot, fullPath) { + if (e) { + if (!e.filename) { + e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; + } + this.error = e; + } + + var importVisitor = this, + inlineCSS = importNode.options.inline, + duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; + + if (!context.importMultiple) { + if (duplicateImport) { + importNode.skip = true; + } else { + importNode.skip = function() { + if (fullPath in importVisitor.onceFileDetectionMap) { + return true; + } + importVisitor.onceFileDetectionMap[fullPath] = true; + return false; + }; + } + } + + if (root) { + importNode.root = root; + importNode.importedFilename = fullPath; + + if (!inlineCSS && (context.importMultiple || !duplicateImport)) { + importVisitor.recursionDetector[fullPath] = true; + + var oldContext = this.context; + this.context = context; + try { + this._visitor.visit(root); + } catch (e) { + this.error = e; + } + this.context = oldContext; + } + } + + importVisitor.importCount--; + + if (importVisitor.isFinished) { + importVisitor._sequencer.tryRun(); + } + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitDirective: function (directiveNode, visitArgs) { + this.context.frames.unshift(directiveNode); + }, + visitDirectiveOut: function (directiveNode) { + this.context.frames.shift(); + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + this.context.frames.unshift(mixinDefinitionNode); + }, + visitMixinDefinitionOut: function (mixinDefinitionNode) { + this.context.frames.shift(); + }, + visitRuleset: function (rulesetNode, visitArgs) { + this.context.frames.unshift(rulesetNode); + }, + visitRulesetOut: function (rulesetNode) { + this.context.frames.shift(); + }, + visitMedia: function (mediaNode, visitArgs) { + this.context.frames.unshift(mediaNode.rules[0]); + }, + visitMediaOut: function (mediaNode) { + this.context.frames.shift(); + } +}; +module.exports = ImportVisitor; + +},{"../contexts":10,"./import-sequencer":82,"./visitor":87}],84:[function(require,module,exports){ +var visitors = { + Visitor: require("./visitor"), + ImportVisitor: require('./import-visitor'), + ExtendVisitor: require('./extend-visitor'), + JoinSelectorVisitor: require('./join-selector-visitor'), + ToCSSVisitor: require('./to-css-visitor') +}; + +module.exports = visitors; + +},{"./extend-visitor":81,"./import-visitor":83,"./join-selector-visitor":85,"./to-css-visitor":86,"./visitor":87}],85:[function(require,module,exports){ +var Visitor = require("./visitor"); + +var JoinSelectorVisitor = function() { + this.contexts = [[]]; + this._visitor = new Visitor(this); +}; + +JoinSelectorVisitor.prototype = { + run: function (root) { + return this._visitor.visit(root); + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + + visitRuleset: function (rulesetNode, visitArgs) { + var context = this.contexts[this.contexts.length - 1], + paths = [], selectors; + + this.contexts.push(paths); + + if (! rulesetNode.root) { + selectors = rulesetNode.selectors; + if (selectors) { + selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); + rulesetNode.selectors = selectors.length ? selectors : (selectors = null); + if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } + } + if (!selectors) { rulesetNode.rules = null; } + rulesetNode.paths = paths; + } + }, + visitRulesetOut: function (rulesetNode) { + this.contexts.length = this.contexts.length - 1; + }, + visitMedia: function (mediaNode, visitArgs) { + var context = this.contexts[this.contexts.length - 1]; + mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); + } +}; + +module.exports = JoinSelectorVisitor; + +},{"./visitor":87}],86:[function(require,module,exports){ +var tree = require("../tree"), + Visitor = require("./visitor"); + +var ToCSSVisitor = function(context) { + this._visitor = new Visitor(this); + this._context = context; +}; + +ToCSSVisitor.prototype = { + isReplacing: true, + run: function (root) { + return this._visitor.visit(root); + }, + + visitRule: function (ruleNode, visitArgs) { + if (ruleNode.variable) { + return; + } + return ruleNode; + }, + + visitMixinDefinition: function (mixinNode, visitArgs) { + // mixin definitions do not get eval'd - this means they keep state + // so we have to clear that state here so it isn't used if toCSS is called twice + mixinNode.frames = []; + }, + + visitExtend: function (extendNode, visitArgs) { + }, + + visitComment: function (commentNode, visitArgs) { + if (commentNode.isSilent(this._context)) { + return; + } + return commentNode; + }, + + visitMedia: function(mediaNode, visitArgs) { + mediaNode.accept(this._visitor); + visitArgs.visitDeeper = false; + + if (!mediaNode.rules.length) { + return; + } + return mediaNode; + }, + + visitDirective: function(directiveNode, visitArgs) { + if (directiveNode.name === "@charset") { + if (!directiveNode.getIsReferenced()) { + return; + } + // Only output the debug info together with subsequent @charset definitions + // a comment (or @media statement) before the actual @charset directive would + // be considered illegal css as it has to be on the first line + if (this.charset) { + if (directiveNode.debugInfo) { + var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "") + " */\n"); + comment.debugInfo = directiveNode.debugInfo; + return this._visitor.visit(comment); + } + return; + } + this.charset = true; + } + if (directiveNode.rules && directiveNode.rules.rules) { + this._mergeRules(directiveNode.rules.rules); + //process childs + directiveNode.accept(this._visitor); + visitArgs.visitDeeper = false; + + // the directive was directly referenced and therefore needs to be shown in the output + if (directiveNode.getIsReferenced()) { + return directiveNode; + } + + if (!directiveNode.rules.rules) { + return ; + } + + //the directive was not directly referenced + for (var r = 0; r < directiveNode.rules.rules.length; r++) { + var rule = directiveNode.rules.rules[r]; + if (rule.getIsReferenced && rule.getIsReferenced()) { + //the directive contains something that was referenced (likely by extend) + //therefore it needs to be shown in output too + + //marking as referenced in case the directive is stored inside another directive + directiveNode.markReferenced(); + return directiveNode; + } + } + //The directive was not directly referenced and does not contain anything that + //was referenced. Therefore it must not be shown in output. + return ; + } else { + if (!directiveNode.getIsReferenced()) { + return; + } + } + return directiveNode; + }, + + checkPropertiesInRoot: function(rules) { + var ruleNode; + for (var i = 0; i < rules.length; i++) { + ruleNode = rules[i]; + if (ruleNode instanceof tree.Rule && !ruleNode.variable) { + throw { message: "properties must be inside selector blocks, they cannot be in the root.", + index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; + } + } + }, + + visitRuleset: function (rulesetNode, visitArgs) { + var rule, rulesets = []; + if (rulesetNode.firstRoot) { + this.checkPropertiesInRoot(rulesetNode.rules); + } + if (! rulesetNode.root) { + if (rulesetNode.paths) { + rulesetNode.paths = rulesetNode.paths + .filter(function(p) { + var i; + if (p[0].elements[0].combinator.value === ' ') { + p[0].elements[0].combinator = new(tree.Combinator)(''); + } + for (i = 0; i < p.length; i++) { + if (p[i].getIsReferenced() && p[i].getIsOutput()) { + return true; + } + } + return false; + }); + } + + // Compile rules and rulesets + var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; + for (var i = 0; i < nodeRuleCnt; ) { + rule = nodeRules[i]; + if (rule && rule.rules) { + // visit because we are moving them out from being a child + rulesets.push(this._visitor.visit(rule)); + nodeRules.splice(i, 1); + nodeRuleCnt--; + continue; + } + i++; + } + // accept the visitor to remove rules and refactor itself + // then we can decide now whether we want it or not + if (nodeRuleCnt > 0) { + rulesetNode.accept(this._visitor); + } else { + rulesetNode.rules = null; + } + visitArgs.visitDeeper = false; + + nodeRules = rulesetNode.rules; + if (nodeRules) { + this._mergeRules(nodeRules); + nodeRules = rulesetNode.rules; + } + if (nodeRules) { + this._removeDuplicateRules(nodeRules); + nodeRules = rulesetNode.rules; + } + + // now decide whether we keep the ruleset + if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { + rulesets.splice(0, 0, rulesetNode); + } + } else { + rulesetNode.accept(this._visitor); + visitArgs.visitDeeper = false; + if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { + rulesets.splice(0, 0, rulesetNode); + } + } + if (rulesets.length === 1) { + return rulesets[0]; + } + return rulesets; + }, + + _removeDuplicateRules: function(rules) { + if (!rules) { return; } + + // remove duplicates + var ruleCache = {}, + ruleList, rule, i; + + for (i = rules.length - 1; i >= 0 ; i--) { + rule = rules[i]; + if (rule instanceof tree.Rule) { + if (!ruleCache[rule.name]) { + ruleCache[rule.name] = rule; + } else { + ruleList = ruleCache[rule.name]; + if (ruleList instanceof tree.Rule) { + ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)]; + } + var ruleCSS = rule.toCSS(this._context); + if (ruleList.indexOf(ruleCSS) !== -1) { + rules.splice(i, 1); + } else { + ruleList.push(ruleCSS); + } + } + } + } + }, + + _mergeRules: function (rules) { + if (!rules) { return; } + + var groups = {}, + parts, + rule, + key; + + for (var i = 0; i < rules.length; i++) { + rule = rules[i]; + + if ((rule instanceof tree.Rule) && rule.merge) { + key = [rule.name, + rule.important ? "!" : ""].join(","); + + if (!groups[key]) { + groups[key] = []; + } else { + rules.splice(i--, 1); + } + + groups[key].push(rule); + } + } + + Object.keys(groups).map(function (k) { + + function toExpression(values) { + return new (tree.Expression)(values.map(function (p) { + return p.value; + })); + } + + function toValue(values) { + return new (tree.Value)(values.map(function (p) { + return p; + })); + } + + parts = groups[k]; + + if (parts.length > 1) { + rule = parts[0]; + var spacedGroups = []; + var lastSpacedGroup = []; + parts.map(function (p) { + if (p.merge === "+") { + if (lastSpacedGroup.length > 0) { + spacedGroups.push(toExpression(lastSpacedGroup)); + } + lastSpacedGroup = []; + } + lastSpacedGroup.push(p); + }); + spacedGroups.push(toExpression(lastSpacedGroup)); + rule.value = toValue(spacedGroups); + } + }); + } +}; + +module.exports = ToCSSVisitor; + +},{"../tree":59,"./visitor":87}],87:[function(require,module,exports){ +var tree = require("../tree"); + +var _visitArgs = { visitDeeper: true }, + _hasIndexed = false; + +function _noop(node) { + return node; +} + +function indexNodeTypes(parent, ticker) { + // add .typeIndex to tree node types for lookup table + var key, child; + for (key in parent) { + if (parent.hasOwnProperty(key)) { + child = parent[key]; + switch (typeof child) { + case "function": + // ignore bound functions directly on tree which do not have a prototype + // or aren't nodes + if (child.prototype && child.prototype.type) { + child.prototype.typeIndex = ticker++; + } + break; + case "object": + ticker = indexNodeTypes(child, ticker); + break; + } + } + } + return ticker; +} + +var Visitor = function(implementation) { + this._implementation = implementation; + this._visitFnCache = []; + + if (!_hasIndexed) { + indexNodeTypes(tree, 1); + _hasIndexed = true; + } +}; + +Visitor.prototype = { + visit: function(node) { + if (!node) { + return node; + } + + var nodeTypeIndex = node.typeIndex; + if (!nodeTypeIndex) { + return node; + } + + var visitFnCache = this._visitFnCache, + impl = this._implementation, + aryIndx = nodeTypeIndex << 1, + outAryIndex = aryIndx | 1, + func = visitFnCache[aryIndx], + funcOut = visitFnCache[outAryIndex], + visitArgs = _visitArgs, + fnName; + + visitArgs.visitDeeper = true; + + if (!func) { + fnName = "visit" + node.type; + func = impl[fnName] || _noop; + funcOut = impl[fnName + "Out"] || _noop; + visitFnCache[aryIndx] = func; + visitFnCache[outAryIndex] = funcOut; + } + + if (func !== _noop) { + var newNode = func.call(impl, node, visitArgs); + if (impl.isReplacing) { + node = newNode; + } + } + + if (visitArgs.visitDeeper && node && node.accept) { + node.accept(this); + } + + if (funcOut != _noop) { + funcOut.call(impl, node); + } + + return node; + }, + visitArray: function(nodes, nonReplacing) { + if (!nodes) { + return nodes; + } + + var cnt = nodes.length, i; + + // Non-replacing + if (nonReplacing || !this._implementation.isReplacing) { + for (i = 0; i < cnt; i++) { + this.visit(nodes[i]); + } + return nodes; + } + + // Replacing + var out = []; + for (i = 0; i < cnt; i++) { + var evald = this.visit(nodes[i]); + if (evald === undefined) { continue; } + if (!evald.splice) { + out.push(evald); + } else if (evald.length) { + this.flatten(evald, out); + } + } + return out; + }, + flatten: function(arr, out) { + if (!out) { + out = []; + } + + var cnt, i, item, + nestedCnt, j, nestedItem; + + for (i = 0, cnt = arr.length; i < cnt; i++) { + item = arr[i]; + if (item === undefined) { + continue; + } + if (!item.splice) { + out.push(item); + continue; + } + + for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { + nestedItem = item[j]; + if (nestedItem === undefined) { + continue; + } + if (!nestedItem.splice) { + out.push(nestedItem); + } else if (nestedItem.length) { + this.flatten(nestedItem, out); + } + } + } + + return out; + } +}; +module.exports = Visitor; + +},{"../tree":59}],88:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; +var queue = []; +var draining = false; + +function drainQueue() { + if (draining) { + return; + } + draining = true; + var currentQueue; + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + var i = -1; + while (++i < len) { + currentQueue[i](); + } + len = queue.length; + } + draining = false; +} +process.nextTick = function (fun) { + queue.push(fun); + if (!draining) { + setTimeout(drainQueue, 0); + } +}; + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],89:[function(require,module,exports){ +'use strict'; + +var asap = require('asap') + +module.exports = Promise; +function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new') + if (typeof fn !== 'function') throw new TypeError('not a function') + var state = null + var value = null + var deferreds = [] + var self = this + + this.then = function(onFulfilled, onRejected) { + return new self.constructor(function(resolve, reject) { + handle(new Handler(onFulfilled, onRejected, resolve, reject)) + }) + } + + function handle(deferred) { + if (state === null) { + deferreds.push(deferred) + return + } + asap(function() { + var cb = state ? deferred.onFulfilled : deferred.onRejected + if (cb === null) { + (state ? deferred.resolve : deferred.reject)(value) + return + } + var ret + try { + ret = cb(value) + } + catch (e) { + deferred.reject(e) + return + } + deferred.resolve(ret) + }) + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.') + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then + if (typeof then === 'function') { + doResolve(then.bind(newValue), resolve, reject) + return + } + } + state = true + value = newValue + finale() + } catch (e) { reject(e) } + } + + function reject(newValue) { + state = false + value = newValue + finale() + } + + function finale() { + for (var i = 0, len = deferreds.length; i < len; i++) + handle(deferreds[i]) + deferreds = null + } + + doResolve(fn, resolve, reject) +} + + +function Handler(onFulfilled, onRejected, resolve, reject){ + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null + this.onRejected = typeof onRejected === 'function' ? onRejected : null + this.resolve = resolve + this.reject = reject +} + +/** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ +function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function (value) { + if (done) return + done = true + onFulfilled(value) + }, function (reason) { + if (done) return + done = true + onRejected(reason) + }) + } catch (ex) { + if (done) return + done = true + onRejected(ex) + } +} + +},{"asap":91}],90:[function(require,module,exports){ +'use strict'; + +//This file contains the ES6 extensions to the core Promises/A+ API + +var Promise = require('./core.js') +var asap = require('asap') + +module.exports = Promise + +/* Static Functions */ + +function ValuePromise(value) { + this.then = function (onFulfilled) { + if (typeof onFulfilled !== 'function') return this + return new Promise(function (resolve, reject) { + asap(function () { + try { + resolve(onFulfilled(value)) + } catch (ex) { + reject(ex); + } + }) + }) + } +} +ValuePromise.prototype = Promise.prototype + +var TRUE = new ValuePromise(true) +var FALSE = new ValuePromise(false) +var NULL = new ValuePromise(null) +var UNDEFINED = new ValuePromise(undefined) +var ZERO = new ValuePromise(0) +var EMPTYSTRING = new ValuePromise('') + +Promise.resolve = function (value) { + if (value instanceof Promise) return value + + if (value === null) return NULL + if (value === undefined) return UNDEFINED + if (value === true) return TRUE + if (value === false) return FALSE + if (value === 0) return ZERO + if (value === '') return EMPTYSTRING + + if (typeof value === 'object' || typeof value === 'function') { + try { + var then = value.then + if (typeof then === 'function') { + return new Promise(then.bind(value)) + } + } catch (ex) { + return new Promise(function (resolve, reject) { + reject(ex) + }) + } + } + + return new ValuePromise(value) +} + +Promise.all = function (arr) { + var args = Array.prototype.slice.call(arr) + + return new Promise(function (resolve, reject) { + if (args.length === 0) return resolve([]) + var remaining = args.length + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then + if (typeof then === 'function') { + then.call(val, function (val) { res(i, val) }, reject) + return + } + } + args[i] = val + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex) + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]) + } + }) +} + +Promise.reject = function (value) { + return new Promise(function (resolve, reject) { + reject(value); + }); +} + +Promise.race = function (values) { + return new Promise(function (resolve, reject) { + values.forEach(function(value){ + Promise.resolve(value).then(resolve, reject); + }) + }); +} + +/* Prototype Methods */ + +Promise.prototype['catch'] = function (onRejected) { + return this.then(null, onRejected); +} + +},{"./core.js":89,"asap":91}],91:[function(require,module,exports){ +(function (process){ + +// Use the fastest possible means to execute a task in a future turn +// of the event loop. + +// linked list of tasks (single, with head node) +var head = {task: void 0, next: null}; +var tail = head; +var flushing = false; +var requestFlush = void 0; +var isNodeJS = false; + +function flush() { + /* jshint loopfunc: true */ + + while (head.next) { + head = head.next; + var task = head.task; + head.task = void 0; + var domain = head.domain; + + if (domain) { + head.domain = void 0; + domain.enter(); + } + + try { + task(); + + } catch (e) { + if (isNodeJS) { + // In node, uncaught exceptions are considered fatal errors. + // Re-throw them synchronously to interrupt flushing! + + // Ensure continuation if the uncaught exception is suppressed + // listening "uncaughtException" events (as domains does). + // Continue in next event to avoid tick recursion. + if (domain) { + domain.exit(); + } + setTimeout(flush, 0); + if (domain) { + domain.enter(); + } + + throw e; + + } else { + // In browsers, uncaught exceptions are not fatal. + // Re-throw them asynchronously to avoid slow-downs. + setTimeout(function() { + throw e; + }, 0); + } + } + + if (domain) { + domain.exit(); + } + } + + flushing = false; +} + +if (typeof process !== "undefined" && process.nextTick) { + // Node.js before 0.9. Note that some fake-Node environments, like the + // Mocha test runner, introduce a `process` global without a `nextTick`. + isNodeJS = true; + + requestFlush = function () { + process.nextTick(flush); + }; + +} else if (typeof setImmediate === "function") { + // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate + if (typeof window !== "undefined") { + requestFlush = setImmediate.bind(window, flush); + } else { + requestFlush = function () { + setImmediate(flush); + }; + } + +} else if (typeof MessageChannel !== "undefined") { + // modern browsers + // http://www.nonblocking.io/2011/06/windownexttick.html + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + requestFlush = function () { + channel.port2.postMessage(0); + }; + +} else { + // old browsers + requestFlush = function () { + setTimeout(flush, 0); + }; +} + +function asap(task) { + tail = tail.next = { + task: task, + domain: isNodeJS && process.domain, + next: null + }; + + if (!flushing) { + flushing = true; + requestFlush(); + } +}; + +module.exports = asap; + + +}).call(this,require('_process')) +},{"_process":88}],92:[function(require,module,exports){ +// should work in any browser without browserify + +if (typeof Promise.prototype.done !== 'function') { + Promise.prototype.done = function (onFulfilled, onRejected) { + var self = arguments.length ? this.then.apply(this, arguments) : this + self.then(null, function (err) { + setTimeout(function () { + throw err + }, 0) + }) + } +} +},{}],"promise/polyfill.js":[function(require,module,exports){ +// not "use strict" so we can declare global "Promise" + +var asap = require('asap'); + +if (typeof Promise === 'undefined') { + Promise = require('./lib/core.js') + require('./lib/es6-extensions.js') +} + +require('./polyfill-done.js'); + +},{"./lib/core.js":89,"./lib/es6-extensions.js":90,"./polyfill-done.js":92,"asap":91}]},{},[2])(2) +}); \ No newline at end of file diff --git a/lib/require.js b/lib/require.js new file mode 100644 index 0000000..bc43457 --- /dev/null +++ b/lib/require.js @@ -0,0 +1,2083 @@ +/** vim: et:ts=4:sw=4:sts=4 + * @license RequireJS 2.1.16 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/requirejs for details + */ +//Not using strict: uneven strict support in browsers, #392, and causes +//problems with requirejs.exec()/transpiler plugins that may not be strict. +/*jslint regexp: true, nomen: true, sloppy: true */ +/*global window, navigator, document, importScripts, setTimeout, opera */ + +var requirejs, require, define; +(function (global) { + var req, s, head, baseElement, dataMain, src, + interactiveScript, currentlyAddingScript, mainScript, subPath, + version = '2.1.16', + commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, + cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, + jsSuffixRegExp = /\.js$/, + currDirRegExp = /^\.\//, + op = Object.prototype, + ostring = op.toString, + hasOwn = op.hasOwnProperty, + ap = Array.prototype, + apsp = ap.splice, + isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), + isWebWorker = !isBrowser && typeof importScripts !== 'undefined', + //PS3 indicates loaded and complete, but need to wait for complete + //specifically. Sequence is 'loading', 'loaded', execution, + // then 'complete'. The UA check is unfortunate, but not sure how + //to feature test w/o causing perf issues. + readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? + /^complete$/ : /^(complete|loaded)$/, + defContextName = '_', + //Oh the tragedy, detecting opera. See the usage of isOpera for reason. + isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', + contexts = {}, + cfg = {}, + globalDefQueue = [], + useInteractive = false; + + function isFunction(it) { + return ostring.call(it) === '[object Function]'; + } + + function isArray(it) { + return ostring.call(it) === '[object Array]'; + } + + /** + * Helper function for iterating over an array. If the func returns + * a true value, it will break out of the loop. + */ + function each(ary, func) { + if (ary) { + var i; + for (i = 0; i < ary.length; i += 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + /** + * Helper function for iterating over an array backwards. If the func + * returns a true value, it will break out of the loop. + */ + function eachReverse(ary, func) { + if (ary) { + var i; + for (i = ary.length - 1; i > -1; i -= 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + function getOwn(obj, prop) { + return hasProp(obj, prop) && obj[prop]; + } + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (hasProp(obj, prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + } + + /** + * Simple function to mix in properties from source into target, + * but only if target does not already have a property of the same name. + */ + function mixin(target, source, force, deepStringMixin) { + if (source) { + eachProp(source, function (value, prop) { + if (force || !hasProp(target, prop)) { + if (deepStringMixin && typeof value === 'object' && value && + !isArray(value) && !isFunction(value) && + !(value instanceof RegExp)) { + + if (!target[prop]) { + target[prop] = {}; + } + mixin(target[prop], value, force, deepStringMixin); + } else { + target[prop] = value; + } + } + }); + } + return target; + } + + //Similar to Function.prototype.bind, but the 'this' object is specified + //first, since it is easier to read/figure out what 'this' will be. + function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + } + + function scripts() { + return document.getElementsByTagName('script'); + } + + function defaultOnError(err) { + throw err; + } + + //Allow getting a global that is expressed in + //dot notation, like 'a.b.c'. + function getGlobal(value) { + if (!value) { + return value; + } + var g = global; + each(value.split('.'), function (part) { + g = g[part]; + }); + return g; + } + + /** + * Constructs an error with a pointer to an URL with more information. + * @param {String} id the error ID that maps to an ID on a web page. + * @param {String} message human readable error. + * @param {Error} [err] the original error, if there is one. + * + * @returns {Error} + */ + function makeError(id, msg, err, requireModules) { + var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + e.requireType = id; + e.requireModules = requireModules; + if (err) { + e.originalError = err; + } + return e; + } + + if (typeof define !== 'undefined') { + //If a define is already in play via another AMD loader, + //do not overwrite. + return; + } + + if (typeof requirejs !== 'undefined') { + if (isFunction(requirejs)) { + //Do not overwrite an existing requirejs instance. + return; + } + cfg = requirejs; + requirejs = undefined; + } + + //Allow for a require config object + if (typeof require !== 'undefined' && !isFunction(require)) { + //assume it is a config object. + cfg = require; + require = undefined; + } + + function newContext(contextName) { + var inCheckLoaded, Module, context, handlers, + checkLoadedTimeoutId, + config = { + //Defaults. Do not set a default for map + //config to speed up normalize(), which + //will run faster if there is no default. + waitSeconds: 7, + baseUrl: './', + paths: {}, + bundles: {}, + pkgs: {}, + shim: {}, + config: {} + }, + registry = {}, + //registry of just enabled modules, to speed + //cycle breaking code when lots of modules + //are registered, but not activated. + enabledRegistry = {}, + undefEvents = {}, + defQueue = [], + defined = {}, + urlFetched = {}, + bundlesMap = {}, + requireCounter = 1, + unnormalizedCounter = 1; + + /** + * Trims the . and .. from an array of path segments. + * It will keep a leading path segment if a .. will become + * the first path segment, to help with module name lookups, + * which act like paths, but can be remapped. But the end result, + * all paths that use this function should look normalized. + * NOTE: this method MODIFIES the input array. + * @param {Array} ary the array of path segments. + */ + function trimDots(ary) { + var i, part; + for (i = 0; i < ary.length; i++) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') { + continue; + } else if (i > 0) { + ary.splice(i - 1, 2); + i -= 2; + } + } + } + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @param {Boolean} applyMap apply the map config to the value. Should + * only be done if this normalization is for a dependency ID. + * @returns {String} normalized name + */ + function normalize(name, baseName, applyMap) { + var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, + foundMap, foundI, foundStarMap, starI, normalizedBaseParts, + baseParts = (baseName && baseName.split('/')), + map = config.map, + starMap = map && map['*']; + + //Adjust any relative paths. + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; + + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); + } + + trimDots(name); + name = name.join('/'); + } + + //Apply map config if available. + if (applyMap && map && (baseParts || starMap)) { + nameParts = name.split('/'); + + outerLoop: for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join('/'); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = getOwn(map, baseParts.slice(0, j).join('/')); + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = getOwn(mapValue, nameSegment); + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break outerLoop; + } + } + } + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { + foundStarMap = getOwn(starMap, nameSegment); + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + // If the name points to a package's name, use + // the package main instead. + pkgMain = getOwn(config.pkgs, name); + + return pkgMain ? pkgMain : name; + } + + function removeScript(name) { + if (isBrowser) { + each(scripts(), function (scriptNode) { + if (scriptNode.getAttribute('data-requiremodule') === name && + scriptNode.getAttribute('data-requirecontext') === context.contextName) { + scriptNode.parentNode.removeChild(scriptNode); + return true; + } + }); + } + } + + function hasPathFallback(id) { + var pathConfig = getOwn(config.paths, id); + if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { + //Pop off the first array value, since it failed, and + //retry + pathConfig.shift(); + context.require.undef(id); + + //Custom require that does not do map translation, since + //ID is "absolute", already mapped/resolved. + context.makeRequire(null, { + skipMap: true + })([id]); + + return true; + } + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Creates a module mapping that includes plugin prefix, module + * name, and path. If parentModuleMap is provided it will + * also normalize the name via require.normalize() + * + * @param {String} name the module name + * @param {String} [parentModuleMap] parent module map + * for the module name, used to resolve relative names. + * @param {Boolean} isNormalized: is the ID already normalized. + * This is true if this call is done for a define() module ID. + * @param {Boolean} applyMap: apply the map config to the ID. + * Should only be true if this map is for a dependency. + * + * @returns {Object} + */ + function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { + var url, pluginModule, suffix, nameParts, + prefix = null, + parentName = parentModuleMap ? parentModuleMap.name : null, + originalName = name, + isDefine = true, + normalizedName = ''; + + //If no name, then it means it is a require call, generate an + //internal name. + if (!name) { + isDefine = false; + name = '_@r' + (requireCounter += 1); + } + + nameParts = splitPrefix(name); + prefix = nameParts[0]; + name = nameParts[1]; + + if (prefix) { + prefix = normalize(prefix, parentName, applyMap); + pluginModule = getOwn(defined, prefix); + } + + //Account for relative paths if there is a base name. + if (name) { + if (prefix) { + if (pluginModule && pluginModule.normalize) { + //Plugin is loaded, use its normalize method. + normalizedName = pluginModule.normalize(name, function (name) { + return normalize(name, parentName, applyMap); + }); + } else { + // If nested plugin references, then do not try to + // normalize, as it will not normalize correctly. This + // places a restriction on resourceIds, and the longer + // term solution is not to normalize until plugins are + // loaded and all normalizations to allow for async + // loading of a loader plugin. But for now, fixes the + // common uses. Details in #1131 + normalizedName = name.indexOf('!') === -1 ? + normalize(name, parentName, applyMap) : + name; + } + } else { + //A regular module. + normalizedName = normalize(name, parentName, applyMap); + + //Normalized name may be a plugin ID due to map config + //application in normalize. The map config values must + //already be normalized, so do not need to redo that part. + nameParts = splitPrefix(normalizedName); + prefix = nameParts[0]; + normalizedName = nameParts[1]; + isNormalized = true; + + url = context.nameToUrl(normalizedName); + } + } + + //If the id is a plugin id that cannot be determined if it needs + //normalization, stamp it with a unique ID so two matching relative + //ids that may conflict can be separate. + suffix = prefix && !pluginModule && !isNormalized ? + '_unnormalized' + (unnormalizedCounter += 1) : + ''; + + return { + prefix: prefix, + name: normalizedName, + parentMap: parentModuleMap, + unnormalized: !!suffix, + url: url, + originalName: originalName, + isDefine: isDefine, + id: (prefix ? + prefix + '!' + normalizedName : + normalizedName) + suffix + }; + } + + function getModule(depMap) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (!mod) { + mod = registry[id] = new context.Module(depMap); + } + + return mod; + } + + function on(depMap, name, fn) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (hasProp(defined, id) && + (!mod || mod.defineEmitComplete)) { + if (name === 'defined') { + fn(defined[id]); + } + } else { + mod = getModule(depMap); + if (mod.error && name === 'error') { + fn(mod.error); + } else { + mod.on(name, fn); + } + } + } + + function onError(err, errback) { + var ids = err.requireModules, + notified = false; + + if (errback) { + errback(err); + } else { + each(ids, function (id) { + var mod = getOwn(registry, id); + if (mod) { + //Set error on module, so it skips timeout checks. + mod.error = err; + if (mod.events.error) { + notified = true; + mod.emit('error', err); + } + } + }); + + if (!notified) { + req.onError(err); + } + } + } + + /** + * Internal method to transfer globalQueue items to this context's + * defQueue. + */ + function takeGlobalQueue() { + //Push all the globalDefQueue items into the context's defQueue + if (globalDefQueue.length) { + //Array splice in the values since the context code has a + //local var ref to defQueue, so cannot just reassign the one + //on context. + apsp.apply(defQueue, + [defQueue.length, 0].concat(globalDefQueue)); + globalDefQueue = []; + } + } + + handlers = { + 'require': function (mod) { + if (mod.require) { + return mod.require; + } else { + return (mod.require = context.makeRequire(mod.map)); + } + }, + 'exports': function (mod) { + mod.usingExports = true; + if (mod.map.isDefine) { + if (mod.exports) { + return (defined[mod.map.id] = mod.exports); + } else { + return (mod.exports = defined[mod.map.id] = {}); + } + } + }, + 'module': function (mod) { + if (mod.module) { + return mod.module; + } else { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return getOwn(config.config, mod.map.id) || {}; + }, + exports: mod.exports || (mod.exports = {}) + }); + } + } + }; + + function cleanRegistry(id) { + //Clean up machinery used for waiting modules. + delete registry[id]; + delete enabledRegistry[id]; + } + + function breakCycle(mod, traced, processed) { + var id = mod.map.id; + + if (mod.error) { + mod.emit('error', mod.error); + } else { + traced[id] = true; + each(mod.depMaps, function (depMap, i) { + var depId = depMap.id, + dep = getOwn(registry, depId); + + //Only force things that have not completed + //being defined, so still in the registry, + //and only if it has not been matched up + //in the module already. + if (dep && !mod.depMatched[i] && !processed[depId]) { + if (getOwn(traced, depId)) { + mod.defineDep(i, defined[depId]); + mod.check(); //pass false? + } else { + breakCycle(dep, traced, processed); + } + } + }); + processed[id] = true; + } + } + + function checkLoaded() { + var err, usingPathFallback, + waitInterval = config.waitSeconds * 1000, + //It is possible to disable the wait interval by using waitSeconds of 0. + expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), + noLoads = [], + reqCalls = [], + stillLoading = false, + needCycleCheck = true; + + //Do not bother if this call was a result of a cycle break. + if (inCheckLoaded) { + return; + } + + inCheckLoaded = true; + + //Figure out the state of all the modules. + eachProp(enabledRegistry, function (mod) { + var map = mod.map, + modId = map.id; + + //Skip things that are not enabled or in error state. + if (!mod.enabled) { + return; + } + + if (!map.isDefine) { + reqCalls.push(mod); + } + + if (!mod.error) { + //If the module should be executed, and it has not + //been inited and time is up, remember it. + if (!mod.inited && expired) { + if (hasPathFallback(modId)) { + usingPathFallback = true; + stillLoading = true; + } else { + noLoads.push(modId); + removeScript(modId); + } + } else if (!mod.inited && mod.fetched && map.isDefine) { + stillLoading = true; + if (!map.prefix) { + //No reason to keep looking for unfinished + //loading. If the only stillLoading is a + //plugin resource though, keep going, + //because it may be that a plugin resource + //is waiting on a non-plugin cycle. + return (needCycleCheck = false); + } + } + } + }); + + if (expired && noLoads.length) { + //If wait time expired, throw error of unloaded modules. + err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); + err.contextName = context.contextName; + return onError(err); + } + + //Not expired, check for a cycle. + if (needCycleCheck) { + each(reqCalls, function (mod) { + breakCycle(mod, {}, {}); + }); + } + + //If still waiting on loads, and the waiting load is something + //other than a plugin resource, or there are still outstanding + //scripts, then just try back later. + if ((!expired || usingPathFallback) && stillLoading) { + //Something is still waiting to load. Wait for it, but only + //if a timeout is not already in effect. + if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { + checkLoadedTimeoutId = setTimeout(function () { + checkLoadedTimeoutId = 0; + checkLoaded(); + }, 50); + } + } + + inCheckLoaded = false; + } + + Module = function (map) { + this.events = getOwn(undefEvents, map.id) || {}; + this.map = map; + this.shim = getOwn(config.shim, map.id); + this.depExports = []; + this.depMaps = []; + this.depMatched = []; + this.pluginMaps = {}; + this.depCount = 0; + + /* this.exports this.factory + this.depMaps = [], + this.enabled, this.fetched + */ + }; + + Module.prototype = { + init: function (depMaps, factory, errback, options) { + options = options || {}; + + //Do not do more inits if already done. Can happen if there + //are multiple define calls for the same module. That is not + //a normal, common case, but it is also not unexpected. + if (this.inited) { + return; + } + + this.factory = factory; + + if (errback) { + //Register for errors on this module. + this.on('error', errback); + } else if (this.events.error) { + //If no errback already, but there are error listeners + //on this module, set up an errback to pass to the deps. + errback = bind(this, function (err) { + this.emit('error', err); + }); + } + + //Do a copy of the dependency array, so that + //source inputs are not modified. For example + //"shim" deps are passed in here directly, and + //doing a direct modification of the depMaps array + //would affect that config. + this.depMaps = depMaps && depMaps.slice(0); + + this.errback = errback; + + //Indicate this module has be initialized + this.inited = true; + + this.ignore = options.ignore; + + //Could have option to init this module in enabled mode, + //or could have been previously marked as enabled. However, + //the dependencies are not known until init is called. So + //if enabled previously, now trigger dependencies as enabled. + if (options.enabled || this.enabled) { + //Enable this module and dependencies. + //Will call this.check() + this.enable(); + } else { + this.check(); + } + }, + + defineDep: function (i, depExports) { + //Because of cycles, defined callback for a given + //export can be called more than once. + if (!this.depMatched[i]) { + this.depMatched[i] = true; + this.depCount -= 1; + this.depExports[i] = depExports; + } + }, + + fetch: function () { + if (this.fetched) { + return; + } + this.fetched = true; + + context.startTime = (new Date()).getTime(); + + var map = this.map; + + //If the manager is for a plugin managed resource, + //ask the plugin to load it now. + if (this.shim) { + context.makeRequire(this.map, { + enableBuildCallback: true + })(this.shim.deps || [], bind(this, function () { + return map.prefix ? this.callPlugin() : this.load(); + })); + } else { + //Regular dependency. + return map.prefix ? this.callPlugin() : this.load(); + } + }, + + load: function () { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); + } + }, + + /** + * Checks if the module is ready to define itself, and if so, + * define it. + */ + check: function () { + if (!this.enabled || this.enabling) { + return; + } + + var err, cjsModule, + id = this.map.id, + depExports = this.depExports, + exports = this.exports, + factory = this.factory; + + if (!this.inited) { + this.fetch(); + } else if (this.error) { + this.emit('error', this.error); + } else if (!this.defining) { + //The factory could trigger another require call + //that would result in checking this module to + //define itself again. If already in the process + //of doing that, skip this work. + this.defining = true; + + if (this.depCount < 1 && !this.defined) { + if (isFunction(factory)) { + //If there is an error listener, favor passing + //to that instead of throwing an error. However, + //only do it for define()'d modules. require + //errbacks should not be called for failures in + //their callbacks (#699). However if a global + //onError is set, use that. + if ((this.events.error && this.map.isDefine) || + req.onError !== defaultOnError) { + try { + exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; + } + } else { + exports = context.execCb(id, factory, depExports, exports); + } + + // Favor return value over exports. If node/cjs in play, + // then will not have a return value anyway. Favor + // module.exports assignment over exports object. + if (this.map.isDefine && exports === undefined) { + cjsModule = this.module; + if (cjsModule) { + exports = cjsModule.exports; + } else if (this.usingExports) { + //exports already set the defined value. + exports = this.exports; + } + } + + if (err) { + err.requireMap = this.map; + err.requireModules = this.map.isDefine ? [this.map.id] : null; + err.requireType = this.map.isDefine ? 'define' : 'require'; + return onError((this.error = err)); + } + + } else { + //Just a literal value + exports = factory; + } + + this.exports = exports; + + if (this.map.isDefine && !this.ignore) { + defined[id] = exports; + + if (req.onResourceLoad) { + req.onResourceLoad(context, this.map, this.depMaps); + } + } + + //Clean up + cleanRegistry(id); + + this.defined = true; + } + + //Finished the define stage. Allow calling check again + //to allow define notifications below in the case of a + //cycle. + this.defining = false; + + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; + } + + } + }, + + callPlugin: function () { + var map = this.map, + id = map.id, + //Map already normalized the prefix. + pluginMap = makeModuleMap(map.prefix); + + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(pluginMap); + + on(pluginMap, 'defined', bind(this, function (plugin) { + var load, normalizedMap, normalizedMod, + bundleId = getOwn(bundlesMap, this.map.id), + name = this.map.name, + parentName = this.map.parentMap ? this.map.parentMap.name : null, + localRequire = context.makeRequire(map.parentMap, { + enableBuildCallback: true + }); + + //If current map is not normalized, wait for that + //normalized name to load instead of continuing. + if (this.map.unnormalized) { + //Normalize the ID if the plugin allows it. + if (plugin.normalize) { + name = plugin.normalize(name, function (name) { + return normalize(name, parentName, true); + }) || ''; + } + + //prefix and name should already be normalized, no need + //for applying map config again either. + normalizedMap = makeModuleMap(map.prefix + '!' + name, + this.map.parentMap); + on(normalizedMap, + 'defined', bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true, + ignore: true + }); + })); + + normalizedMod = getOwn(registry, normalizedMap.id); + if (normalizedMod) { + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(normalizedMap); + + if (this.events.error) { + normalizedMod.on('error', bind(this, function (err) { + this.emit('error', err); + })); + } + normalizedMod.enable(); + } + + return; + } + + //If a paths config, then just load that file instead to + //resolve the plugin, as it is built into that paths layer. + if (bundleId) { + this.map.url = context.nameToUrl(bundleId); + this.load(); + return; + } + + load = bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true + }); + }); + + load.error = bind(this, function (err) { + this.inited = true; + this.error = err; + err.requireModules = [id]; + + //Remove temp unnormalized modules for this module, + //since they will never be resolved otherwise now. + eachProp(registry, function (mod) { + if (mod.map.id.indexOf(id + '_unnormalized') === 0) { + cleanRegistry(mod.map.id); + } + }); + + onError(err); + }); + + //Allow plugins to load other code without having to know the + //context or how to 'complete' the load. + load.fromText = bind(this, function (text, textAlt) { + /*jslint evil: true */ + var moduleName = map.name, + moduleMap = makeModuleMap(moduleName), + hasInteractive = useInteractive; + + //As of 2.1.0, support just passing the text, to reinforce + //fromText only being called once per resource. Still + //support old style of passing moduleName but discard + //that moduleName in favor of the internal ref. + if (textAlt) { + text = textAlt; + } + + //Turn off interactive script matching for IE for any define + //calls in the text, then turn it back on at the end. + if (hasInteractive) { + useInteractive = false; + } + + //Prime the system by creating a module instance for + //it. + getModule(moduleMap); + + //Transfer any config to this other module. + if (hasProp(config.config, id)) { + config.config[moduleName] = config.config[id]; + } + + try { + req.exec(text); + } catch (e) { + return onError(makeError('fromtexteval', + 'fromText eval for ' + id + + ' failed: ' + e, + e, + [id])); + } + + if (hasInteractive) { + useInteractive = true; + } + + //Mark this as a dependency for the plugin + //resource + this.depMaps.push(moduleMap); + + //Support anonymous modules. + context.completeLoad(moduleName); + + //Bind the value of that module to the value for this + //resource ID. + localRequire([moduleName], load); + }); + + //Use parentName here since the plugin's name is not reliable, + //could be some weird string with no path that actually wants to + //reference the parentName's path. + plugin.load(map.name, localRequire, load, config); + })); + + context.enable(pluginMap, this); + this.pluginMaps[pluginMap.id] = pluginMap; + }, + + enable: function () { + enabledRegistry[this.map.id] = this; + this.enabled = true; + + //Set flag mentioning that the module is enabling, + //so that immediate calls to the defined callbacks + //for dependencies do not trigger inadvertent load + //with the depCount still being zero. + this.enabling = true; + + //Enable each dependency + each(this.depMaps, bind(this, function (depMap, i) { + var id, mod, handler; + + if (typeof depMap === 'string') { + //Dependency needs to be converted to a depMap + //and wired up to this module. + depMap = makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap), + false, + !this.skipMap); + this.depMaps[i] = depMap; + + handler = getOwn(handlers, depMap.id); + + if (handler) { + this.depExports[i] = handler(this); + return; + } + + this.depCount += 1; + + on(depMap, 'defined', bind(this, function (depExports) { + this.defineDep(i, depExports); + this.check(); + })); + + if (this.errback) { + on(depMap, 'error', bind(this, this.errback)); + } else if (this.events.error) { + // No direct errback on this module, but something + // else is listening for errors, so be sure to + // propagate the error correctly. + on(depMap, 'error', bind(this, function(err) { + this.emit('error', err); + })); + } + } + + id = depMap.id; + mod = registry[id]; + + //Skip special modules like 'require', 'exports', 'module' + //Also, don't call enable if it is already enabled, + //important in circular dependency cases. + if (!hasProp(handlers, id) && mod && !mod.enabled) { + context.enable(depMap, this); + } + })); + + //Enable each plugin that is used in + //a dependency + eachProp(this.pluginMaps, bind(this, function (pluginMap) { + var mod = getOwn(registry, pluginMap.id); + if (mod && !mod.enabled) { + context.enable(pluginMap, this); + } + })); + + this.enabling = false; + + this.check(); + }, + + on: function (name, cb) { + var cbs = this.events[name]; + if (!cbs) { + cbs = this.events[name] = []; + } + cbs.push(cb); + }, + + emit: function (name, evt) { + each(this.events[name], function (cb) { + cb(evt); + }); + if (name === 'error') { + //Now that the error handler was triggered, remove + //the listeners, since this broken Module instance + //can stay around for a while in the registry. + delete this.events[name]; + } + } + }; + + function callGetModule(args) { + //Skip modules already defined. + if (!hasProp(defined, args[0])) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } + } + + function removeListener(node, func, name, ieName) { + //Favor detachEvent because of IE9 + //issue, see attachEvent/addEventListener comment elsewhere + //in this file. + if (node.detachEvent && !isOpera) { + //Probably IE. If not it will throw an error, which will be + //useful to know. + if (ieName) { + node.detachEvent(ieName, func); + } + } else { + node.removeEventListener(name, func, false); + } + } + + /** + * Given an event from a script node, get the requirejs info from it, + * and then removes the event listeners on the node. + * @param {Event} evt + * @returns {Object} + */ + function getScriptData(evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + var node = evt.currentTarget || evt.srcElement; + + //Remove the listeners once here. + removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); + removeListener(node, context.onScriptError, 'error'); + + return { + node: node, + id: node && node.getAttribute('data-requiremodule') + }; + } + + function intakeDefines() { + var args; + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //define() function. + callGetModule(args); + } + } + } + + context = { + config: config, + contextName: contextName, + registry: registry, + defined: defined, + urlFetched: urlFetched, + defQueue: defQueue, + Module: Module, + makeModuleMap: makeModuleMap, + nextTick: req.nextTick, + onError: onError, + + /** + * Set a configuration for the context. + * @param {Object} cfg config object to integrate. + */ + configure: function (cfg) { + //Make sure the baseUrl ends in a slash. + if (cfg.baseUrl) { + if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { + cfg.baseUrl += '/'; + } + } + + //Save off the paths since they require special processing, + //they are additive. + var shim = config.shim, + objs = { + paths: true, + bundles: true, + config: true, + map: true + }; + + eachProp(cfg, function (value, prop) { + if (objs[prop]) { + if (!config[prop]) { + config[prop] = {}; + } + mixin(config[prop], value, true, true); + } else { + config[prop] = value; + } + }); + + //Reverse map the bundles + if (cfg.bundles) { + eachProp(cfg.bundles, function (value, prop) { + each(value, function (v) { + if (v !== prop) { + bundlesMap[v] = prop; + } + }); + }); + } + + //Merge shim + if (cfg.shim) { + eachProp(cfg.shim, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if ((value.exports || value.init) && !value.exportsFn) { + value.exportsFn = context.makeShimExports(value); + } + shim[id] = value; + }); + config.shim = shim; + } + + //Adjust packages if necessary. + if (cfg.packages) { + each(cfg.packages, function (pkgObj) { + var location, name; + + pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + + name = pkgObj.name; + location = pkgObj.location; + if (location) { + config.paths[name] = pkgObj.location; + } + + //Save pointer to main module ID for pkg name. + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, ''); + }); + } + + //If there are any "waiting to execute" modules in the registry, + //update the maps for them, since their info, like URLs to load, + //may have changed. + eachProp(registry, function (mod, id) { + //If module already has init called, since it is too + //late to modify them, and ignore unnormalized ones + //since they are transient. + if (!mod.inited && !mod.map.unnormalized) { + mod.map = makeModuleMap(id); + } + }); + + //If a deps array or a config callback is specified, then call + //require with those args. This is useful when require is defined as a + //config object before require.js is loaded. + if (cfg.deps || cfg.callback) { + context.require(cfg.deps || [], cfg.callback); + } + }, + + makeShimExports: function (value) { + function fn() { + var ret; + if (value.init) { + ret = value.init.apply(global, arguments); + } + return ret || (value.exports && getGlobal(value.exports)); + } + return fn; + }, + + makeRequire: function (relMap, options) { + options = options || {}; + + function localRequire(deps, callback, errback) { + var id, map, requireMod; + + if (options.enableBuildCallback && callback && isFunction(callback)) { + callback.__requireJsBuild = true; + } + + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid require call'), errback); + } + + //If require|exports|module are requested, get the + //value for them from the special handlers. Caveat: + //this only works while module is being defined. + if (relMap && hasProp(handlers, deps)) { + return handlers[deps](registry[relMap.id]); + } + + //Synchronous access to one module. If require.get is + //available (as in the Node adapter), prefer that. + if (req.get) { + return req.get(context, deps, relMap, localRequire); + } + + //Normalize module name, if it contains . or .. + map = makeModuleMap(deps, relMap, false, true); + id = map.id; + + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName + + (relMap ? '' : '. Use require([])'))); + } + return defined[id]; + } + + //Grab defines waiting in the global queue. + intakeDefines(); + + //Mark all the dependencies as needing to be loaded. + context.nextTick(function () { + //Some defines could have been added since the + //require call, collect them. + intakeDefines(); + + requireMod = getModule(makeModuleMap(null, relMap)); + + //Store if map config should be applied to this require + //call for dependencies. + requireMod.skipMap = options.skipMap; + + requireMod.init(deps, callback, errback, { + enabled: true + }); + + checkLoaded(); + }); + + return localRequire; + } + + mixin(localRequire, { + isBrowser: isBrowser, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt) { + var ext, + index = moduleNamePlusExt.lastIndexOf('.'), + segment = moduleNamePlusExt.split('/')[0], + isRelative = segment === '.' || segment === '..'; + + //Have a file extension alias, and it is not the + //dots from a relative path. + if (index !== -1 && (!isRelative || index > 1)) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } + + return context.nameToUrl(normalize(moduleNamePlusExt, + relMap && relMap.id, true), ext, true); + }, + + defined: function (id) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, + + specified: function (id) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); + } + }); + + //Only allow undef on top level require calls + if (!relMap) { + localRequire.undef = function (id) { + //Bind any waiting define() calls to this context, + //fix for #408 + takeGlobalQueue(); + + var map = makeModuleMap(id, relMap, true), + mod = getOwn(registry, id); + + removeScript(id); + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + //Clean queued defines too. Go backwards + //in array so that the splices do not + //mess up the iteration. + eachReverse(defQueue, function(args, i) { + if(args[0] === id) { + defQueue.splice(i, 1); + } + }); + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } + + cleanRegistry(id); + } + }; + } + + return localRequire; + }, + + /** + * Called to enable a module if it is still in the registry + * awaiting enablement. A second arg, parent, the parent module, + * is passed in for context, when this method is overridden by + * the optimizer. Not shown here to keep code compact. + */ + enable: function (depMap) { + var mod = getOwn(registry, depMap.id); + if (mod) { + getModule(depMap).enable(); + } + }, + + /** + * Internal method used by environment adapters to complete a load event. + * A load event could be a script load or just a load pass from a synchronous + * load call. + * @param {String} moduleName the name of the module to potentially complete. + */ + completeLoad: function (moduleName) { + var found, args, mod, + shim = getOwn(config.shim, moduleName) || {}, + shExports = shim.exports; + + takeGlobalQueue(); + + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + args[0] = moduleName; + //If already found an anonymous module and bound it + //to this name, then this is some other anon module + //waiting for its completeLoad to fire. + if (found) { + break; + } + found = true; + } else if (args[0] === moduleName) { + //Found matching define call for this script! + found = true; + } + + callGetModule(args); + } + + //Do this after the cycle of callGetModule in case the result + //of those calls/init calls changes the registry. + mod = getOwn(registry, moduleName); + + if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { + if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { + if (hasPathFallback(moduleName)) { + return; + } else { + return onError(makeError('nodefine', + 'No define call for ' + moduleName, + null, + [moduleName])); + } + } else { + //A script that does not call define(), so just simulate + //the call for it. + callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); + } + } + + checkLoaded(); + }, + + /** + * Converts a module name to a file path. Supports cases where + * moduleName may actually be just an URL. + * Note that it **does not** call normalize on the moduleName, + * it is assumed to have already been normalized. This is an + * internal API, not a public one. Use toUrl for the public API. + */ + nameToUrl: function (moduleName, ext, skipExt) { + var paths, syms, i, parentModule, url, + parentPath, bundleId, + pkgMain = getOwn(config.pkgs, moduleName); + + if (pkgMain) { + moduleName = pkgMain; + } + + bundleId = getOwn(bundlesMap, moduleName); + + if (bundleId) { + return context.nameToUrl(bundleId, ext, skipExt); + } + + //If a colon is in the URL, it indicates a protocol is used and it is just + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. + //The slash is important for protocol-less URLs as well as full paths. + if (req.jsExtRegExp.test(moduleName)) { + //Just a plain path, not module name lookup, so just return it. + //Add extension if it is included. This is a bit wonky, only non-.js things pass + //an extension, this method probably needs to be reworked. + url = moduleName + (ext || ''); + } else { + //A module that needs to be converted to a path. + paths = config.paths; + + syms = moduleName.split('/'); + //For each module name segment, see if there is a path + //registered for it. Start with most specific name + //and work up from it. + for (i = syms.length; i > 0; i -= 1) { + parentModule = syms.slice(0, i).join('/'); + + parentPath = getOwn(paths, parentModule); + if (parentPath) { + //If an array, it means there are a few choices, + //Choose the one that is desired + if (isArray(parentPath)) { + parentPath = parentPath[0]; + } + syms.splice(0, i, parentPath); + break; + } + } + + //Join the path parts together, then figure out if baseUrl is needed. + url = syms.join('/'); + url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; + } + + return config.urlArgs ? url + + ((url.indexOf('?') === -1 ? '?' : '&') + + config.urlArgs) : url; + }, + + //Delegates to req.load. Broken out as a separate function to + //allow overriding in the optimizer. + load: function (id, url) { + req.load(context, id, url); + }, + + /** + * Executes a module callback function. Broken out as a separate function + * solely to allow the build system to sequence the files in the built + * layer in the right sequence. + * + * @private + */ + execCb: function (name, callback, args, exports) { + return callback.apply(exports, args); + }, + + /** + * callback for script loads, used to check status of loading. + * + * @param {Event} evt the event from the browser for the script + * that was loaded. + */ + onScriptLoad: function (evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + if (evt.type === 'load' || + (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { + //Reset interactive script so a script node is not held onto for + //to long. + interactiveScript = null; + + //Pull out the name of the module and the context. + var data = getScriptData(evt); + context.completeLoad(data.id); + } + }, + + /** + * Callback for script errors. + */ + onScriptError: function (evt) { + var data = getScriptData(evt); + if (!hasPathFallback(data.id)) { + return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); + } + } + }; + + context.require = context.makeRequire(); + return context; + } + + /** + * Main entry point. + * + * If the only argument to require is a string, then the module that + * is represented by that string is fetched for the appropriate context. + * + * If the first argument is an array, then it will be treated as an array + * of dependency string names to fetch. An optional function callback can + * be specified to execute when all of those dependencies are available. + * + * Make a local req variable to help Caja compliance (it assumes things + * on a require that are not standardized), and to give a short + * name for minification/local scope use. + */ + req = requirejs = function (deps, callback, errback, optional) { + + //Find the right context, use default + var context, config, + contextName = defContextName; + + // Determine if have config object in the call. + if (!isArray(deps) && typeof deps !== 'string') { + // deps is a config object + config = deps; + if (isArray(callback)) { + // Adjust args if there are dependencies + deps = callback; + callback = errback; + errback = optional; + } else { + deps = []; + } + } + + if (config && config.context) { + contextName = config.context; + } + + context = getOwn(contexts, contextName); + if (!context) { + context = contexts[contextName] = req.s.newContext(contextName); + } + + if (config) { + context.configure(config); + } + + return context.require(deps, callback, errback); + }; + + /** + * Support require.config() to make it easier to cooperate with other + * AMD loaders on globally agreed names. + */ + req.config = function (config) { + return req(config); + }; + + /** + * Execute something after the current tick + * of the event loop. Override for other envs + * that have a better solution than setTimeout. + * @param {Function} fn function to execute later. + */ + req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { + setTimeout(fn, 4); + } : function (fn) { fn(); }; + + /** + * Export require as a global, but only if it does not already exist. + */ + if (!require) { + require = req; + } + + req.version = version; + + //Used to filter out dependencies that are already paths. + req.jsExtRegExp = /^\/|:|\?|\.js$/; + req.isBrowser = isBrowser; + s = req.s = { + contexts: contexts, + newContext: newContext + }; + + //Create default context. + req({}); + + //Exports some context-sensitive methods on global require. + each([ + 'toUrl', + 'undef', + 'defined', + 'specified' + ], function (prop) { + //Reference from contexts instead of early binding to default context, + //so that during builds, the latest instance of the default context + //with its config gets used. + req[prop] = function () { + var ctx = contexts[defContextName]; + return ctx.require[prop].apply(ctx, arguments); + }; + }); + + if (isBrowser) { + head = s.head = document.getElementsByTagName('head')[0]; + //If BASE tag is in play, using appendChild is a problem for IE6. + //When that browser dies, this can be removed. Details in this jQuery bug: + //http://dev.jquery.com/ticket/2709 + baseElement = document.getElementsByTagName('base')[0]; + if (baseElement) { + head = s.head = baseElement.parentNode; + } + } + + /** + * Any errors that require explicitly generates will be passed to this + * function. Intercept/override it if you want custom error handling. + * @param {Error} err the error object. + */ + req.onError = defaultOnError; + + /** + * Creates the node for the load command. Only used in browser envs. + */ + req.createNode = function (config, moduleName, url) { + var node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + node.async = true; + return node; + }; + + /** + * Does the request to load a module for the browser case. + * Make this a separate function to allow other environments + * to override it. + * + * @param {Object} context the require context to find state. + * @param {String} moduleName the name of the module. + * @param {Object} url the URL to the module. + */ + req.load = function (context, moduleName, url) { + var config = (context && context.config) || {}, + node; + if (isBrowser) { + //In the browser so use a script tag + node = req.createNode(config, moduleName, url); + + node.setAttribute('data-requirecontext', context.contextName); + node.setAttribute('data-requiremodule', moduleName); + + //Set up load listener. Test attachEvent first because IE9 has + //a subtle issue in its addEventListener and script onload firings + //that do not match the behavior of all other browsers with + //addEventListener support, which fire the onload event for a + //script right after the script execution. See: + //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution + //UNFORTUNATELY Opera implements attachEvent but does not follow the script + //script execution mode. + if (node.attachEvent && + //Check if node.attachEvent is artificially added by custom script or + //natively supported by browser + //read https://github.com/jrburke/requirejs/issues/187 + //if we can NOT find [native code] then it must NOT natively supported. + //in IE8, node.attachEvent does not have toString() + //Note the test for "[native code" with no closing brace, see: + //https://github.com/jrburke/requirejs/issues/273 + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && + !isOpera) { + //Probably IE. IE (at least 6-8) do not fire + //script onload right after executing the script, so + //we cannot tie the anonymous define call to a name. + //However, IE reports the script as being in 'interactive' + //readyState at the time of the define call. + useInteractive = true; + + node.attachEvent('onreadystatechange', context.onScriptLoad); + //It would be great to add an error handler here to catch + //404s in IE9+. However, onreadystatechange will fire before + //the error handler, so that does not help. If addEventListener + //is used, then IE will fire error before load, but we cannot + //use that pathway given the connect.microsoft.com issue + //mentioned above about not doing the 'script execute, + //then fire the script load event listener before execute + //next script' that other browsers do. + //Best hope: IE10 fixes the issues, + //and then destroys all installs of IE 6-9. + //node.attachEvent('onerror', context.onScriptError); + } else { + node.addEventListener('load', context.onScriptLoad, false); + node.addEventListener('error', context.onScriptError, false); + } + node.src = url; + + //For some cache cases in IE 6-8, the script executes before the end + //of the appendChild execution, so to tie an anonymous define + //call to the module name (which is stored on the node), hold on + //to a reference to this node, but clear after the DOM insertion. + currentlyAddingScript = node; + if (baseElement) { + head.insertBefore(node, baseElement); + } else { + head.appendChild(node); + } + currentlyAddingScript = null; + + return node; + } else if (isWebWorker) { + try { + //In a web worker, use importScripts. This is not a very + //efficient use of importScripts, importScripts will block until + //its script is downloaded and evaluated. However, if web workers + //are in play, the expectation that a build has been done so that + //only one script needs to be loaded anyway. This may need to be + //reevaluated if other use cases become common. + importScripts(url); + + //Account for anonymous modules + context.completeLoad(moduleName); + } catch (e) { + context.onError(makeError('importscripts', + 'importScripts failed for ' + + moduleName + ' at ' + url, + e, + [moduleName])); + } + } + }; + + function getInteractiveScript() { + if (interactiveScript && interactiveScript.readyState === 'interactive') { + return interactiveScript; + } + + eachReverse(scripts(), function (script) { + if (script.readyState === 'interactive') { + return (interactiveScript = script); + } + }); + return interactiveScript; + } + + //Look for a data-main script attribute, which could also adjust the baseUrl. + if (isBrowser && !cfg.skipDataMain) { + //Figure out baseUrl. Get it from the script tag with require.js in it. + eachReverse(scripts(), function (script) { + //Set the 'head' where we can append children by + //using the script's parent. + if (!head) { + head = script.parentNode; + } + + //Look for a data-main attribute to set main script for the page + //to load. If it is there, the path to data main becomes the + //baseUrl, if it is not already set. + dataMain = script.getAttribute('data-main'); + if (dataMain) { + //Preserve dataMain in case it is a path (i.e. contains '?') + mainScript = dataMain; + + //Set final baseUrl if there is not already an explicit one. + if (!cfg.baseUrl) { + //Pull off the directory of data-main for use as the + //baseUrl. + src = mainScript.split('/'); + mainScript = src.pop(); + subPath = src.length ? src.join('/') + '/' : './'; + + cfg.baseUrl = subPath; + } + + //Strip off any trailing .js since mainScript is now + //like a module name. + mainScript = mainScript.replace(jsSuffixRegExp, ''); + + //If mainScript is still a path, fall back to dataMain + if (req.jsExtRegExp.test(mainScript)) { + mainScript = dataMain; + } + + //Put the data-main script in the files to load. + cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; + + return true; + } + }); + } + + /** + * The function that handles definitions of modules. Differs from + * require() in that a string for the module should be the first argument, + * and the function to execute after dependencies are loaded should + * return a value to define the module corresponding to the first argument's + * name. + */ + define = function (name, deps, callback) { + var node, context; + + //Allow for anonymous modules + if (typeof name !== 'string') { + //Adjust args appropriately + callback = deps; + deps = name; + name = null; + } + + //This module may not have dependencies + if (!isArray(deps)) { + callback = deps; + deps = null; + } + + //If no name, and callback is a function, then figure out if it a + //CommonJS thing with dependencies. + if (!deps && isFunction(callback)) { + deps = []; + //Remove comments from the callback string, + //look for require calls, and pull them into the dependencies, + //but only if there are function args. + if (callback.length) { + callback + .toString() + .replace(commentRegExp, '') + .replace(cjsRequireRegExp, function (match, dep) { + deps.push(dep); + }); + + //May be a CommonJS thing even without require calls, but still + //could use exports, and module. Avoid doing exports and module + //work though if it just needs require. + //REQUIRES the function to expect the CommonJS variables in the + //order listed below. + deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); + } + } + + //If in IE 6-8 and hit an anonymous define() call, do the interactive + //work. + if (useInteractive) { + node = currentlyAddingScript || getInteractiveScript(); + if (node) { + if (!name) { + name = node.getAttribute('data-requiremodule'); + } + context = contexts[node.getAttribute('data-requirecontext')]; + } + } + + //Always save off evaluating the def call until the script onload handler. + //This allows multiple modules to be in a file without prematurely + //tracing dependencies, and allows for anonymous module support, + //where the module name is not known until the script onload event + //occurs. If no context, use the global queue, and get it processed + //in the onscript load callback. + (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); + }; + + define.amd = { + jQuery: true + }; + + + /** + * Executes the text. Normally just uses eval, but can be modified + * to use a better, environment-specific call. Only used for transpiling + * loader plugins, not for plain JS modules. + * @param {String} text the text to execute/evaluate. + */ + req.exec = function (text) { + /*jslint evil: true */ + return eval(text); + }; + + //Set up with config info. + req(cfg); +}(this)); -- cgit v1.2.1