/* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ /** * Define a module along with a payload. * @param {string} moduleName Name for the payload * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec * @param {function} payload Function with (require, exports, module) params */ function define(moduleName, deps, payload) { if (typeof moduleName != "string") { throw new TypeError('Expected string, got: ' + moduleName); } if (arguments.length == 2) { payload = deps; } if (moduleName in define.modules) { throw new Error("Module already defined: " + moduleName); } define.modules[moduleName] = payload; }; /** * The global store of un-instantiated modules */ define.modules = {}; /** * We invoke require() in the context of a Domain so we can have multiple * sets of modules running separate from each other. * This contrasts with JSMs which are singletons, Domains allows us to * optionally load a CommonJS module twice with separate data each time. * Perhaps you want 2 command lines with a different set of commands in each, * for example. */ function Domain() { this.modules = {}; this._currentModule = null; } (function () { /** * Lookup module names and resolve them by calling the definition function if * needed. * There are 2 ways to call this, either with an array of dependencies and a * callback to call when the dependencies are found (which can happen * asynchronously in an in-page context) or with a single string an no callback * where the dependency is resolved synchronously and returned. * The API is designed to be compatible with the CommonJS AMD spec and * RequireJS. * @param {string[]|string} deps A name, or names for the payload * @param {function|undefined} callback Function to call when the dependencies * are resolved * @return {undefined|object} The module required or undefined for * array/callback method */ Domain.prototype.require = function(deps, callback) { if (Array.isArray(deps)) { var params = deps.map(function(dep) { return this.lookup(dep); }, this); if (callback) { callback.apply(null, params); } return undefined; } else { return this.lookup(deps); } }; function normalize(path) { var bits = path.split('/'); var i = 1; while (i < bits.length) { if (bits[i] === '..') { bits.splice(i-1, 1); } else if (bits[i] === '.') { bits.splice(i, 1); } else { i++; } } return bits.join('/'); } function join(a, b) { a = a.trim(); b = b.trim(); if (/^\//.test(b)) { return b; } else { return a.replace(/\/*$/, '/') + b; } } function dirname(path) { var bits = path.split('/'); bits.pop(); return bits.join('/'); } /** * Lookup module names and resolve them by calling the definition function if * needed. * @param {string} moduleName A name for the payload to lookup * @return {object} The module specified by aModuleName or null if not found. */ Domain.prototype.lookup = function(moduleName) { if (/^\./.test(moduleName)) { moduleName = normalize(join(dirname(this._currentModule), moduleName)); } if (moduleName in this.modules) { var module = this.modules[moduleName]; return module; } if (!(moduleName in define.modules)) { throw new Error("Module not defined: " + moduleName); } var module = define.modules[moduleName]; if (typeof module == "function") { var exports = {}; var previousModule = this._currentModule; this._currentModule = moduleName; module(this.require.bind(this), exports, { id: moduleName, uri: "" }); this._currentModule = previousModule; module = exports; } // cache the resulting module object for next time this.modules[moduleName] = module; return module; }; }()); define.Domain = Domain; define.globalDomain = new Domain(); var require = define.globalDomain.require.bind(define.globalDomain); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { var base64VLQ = require('./base64-vlq'); var util = require('./util'); var ArraySet = require('./array-set').ArraySet; /** * An instance of the SourceMapGenerator represents a source map which is * being built incrementally. To create a new one, you must pass an object * with the following properties: * * - file: The filename of the generated source. * - sourceRoot: An optional root for all URLs in this source map. */ function SourceMapGenerator(aArgs) { this._file = util.getArg(aArgs, 'file'); this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); this._sources = new ArraySet(); this._names = new ArraySet(); this._mappings = []; this._sourcesContents = null; } SourceMapGenerator.prototype._version = 3; /** * Creates a new SourceMapGenerator based on a SourceMapConsumer * * @param aSourceMapConsumer The SourceMap. */ SourceMapGenerator.fromSourceMap = function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { var sourceRoot = aSourceMapConsumer.sourceRoot; var generator = new SourceMapGenerator({ file: aSourceMapConsumer.file, sourceRoot: sourceRoot }); aSourceMapConsumer.eachMapping(function (mapping) { var newMapping = { generated: { line: mapping.generatedLine, column: mapping.generatedColumn } }; if (mapping.source) { newMapping.source = mapping.source; if (sourceRoot) { newMapping.source = util.relative(sourceRoot, newMapping.source); } newMapping.original = { line: mapping.originalLine, column: mapping.originalColumn }; if (mapping.name) { newMapping.name = mapping.name; } } generator.addMapping(newMapping); }); aSourceMapConsumer.sources.forEach(function (sourceFile) { var content = aSourceMapConsumer.sourceContentFor(sourceFile); if (content) { generator.setSourceContent(sourceFile, content); } }); return generator; }; /** * Add a single mapping from original source line and column to the generated * source's line and column for this source map being created. The mapping * object should have the following properties: * * - generated: An object with the generated line and column positions. * - original: An object with the original line and column positions. * - source: The original source file (relative to the sourceRoot). * - name: An optional original token name for this mapping. */ SourceMapGenerator.prototype.addMapping = function SourceMapGenerator_addMapping(aArgs) { var generated = util.getArg(aArgs, 'generated'); var original = util.getArg(aArgs, 'original', null); var source = util.getArg(aArgs, 'source', null); var name = util.getArg(aArgs, 'name', null); this._validateMapping(generated, original, source, name); if (source && !this._sources.has(source)) { this._sources.add(source); } if (name && !this._names.has(name)) { this._names.add(name); } this._mappings.push({ generatedLine: generated.line, generatedColumn: generated.column, originalLine: original != null && original.line, originalColumn: original != null && original.column, source: source, name: name }); }; /** * Set the source content for a source file. */ SourceMapGenerator.prototype.setSourceContent = function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { var source = aSourceFile; if (this._sourceRoot) { source = util.relative(this._sourceRoot, source); } if (aSourceContent !== null) { // Add the source content to the _sourcesContents map. // Create a new _sourcesContents map if the property is null. if (!this._sourcesContents) { this._sourcesContents = {}; } this._sourcesContents[util.toSetString(source)] = aSourceContent; } else { // Remove the source file from the _sourcesContents map. // If the _sourcesContents map is empty, set the property to null. delete this._sourcesContents[util.toSetString(source)]; if (Object.keys(this._sourcesContents).length === 0) { this._sourcesContents = null; } } }; /** * Applies the mappings of a sub-source-map for a specific source file to the * source map being generated. Each mapping to the supplied source file is * rewritten using the supplied source map. Note: The resolution for the * resulting mappings is the minimium of this map and the supplied map. * * @param aSourceMapConsumer The source map to be applied. * @param aSourceFile Optional. The filename of the source file. * If omitted, SourceMapConsumer's file property will be used. */ SourceMapGenerator.prototype.applySourceMap = function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { // If aSourceFile is omitted, we will use the file property of the SourceMap if (!aSourceFile) { aSourceFile = aSourceMapConsumer.file; } var sourceRoot = this._sourceRoot; // Make "aSourceFile" relative if an absolute Url is passed. if (sourceRoot) { aSourceFile = util.relative(sourceRoot, aSourceFile); } // Applying the SourceMap can add and remove items from the sources and // the names array. var newSources = new ArraySet(); var newNames = new ArraySet(); // Find mappings for the "aSourceFile" this._mappings.forEach(function (mapping) { if (mapping.source === aSourceFile && mapping.originalLine) { // Check if it can be mapped by the source map, then update the mapping. var original = aSourceMapConsumer.originalPositionFor({ line: mapping.originalLine, column: mapping.originalColumn }); if (original.source !== null) { // Copy mapping if (sourceRoot) { mapping.source = util.relative(sourceRoot, original.source); } else { mapping.source = original.source; } mapping.originalLine = original.line; mapping.originalColumn = original.column; if (original.name !== null && mapping.name !== null) { // Only use the identifier name if it's an identifier // in both SourceMaps mapping.name = original.name; } } } var source = mapping.source; if (source && !newSources.has(source)) { newSources.add(source); } var name = mapping.name; if (name && !newNames.has(name)) { newNames.add(name); } }, this); this._sources = newSources; this._names = newNames; // Copy sourcesContents of applied map. aSourceMapConsumer.sources.forEach(function (sourceFile) { var content = aSourceMapConsumer.sourceContentFor(sourceFile); if (content) { if (sourceRoot) { sourceFile = util.relative(sourceRoot, sourceFile); } this.setSourceContent(sourceFile, content); } }, this); }; /** * A mapping can have one of the three levels of data: * * 1. Just the generated position. * 2. The Generated position, original position, and original source. * 3. Generated and original position, original source, as well as a name * token. * * To maintain consistency, we validate that any new mapping being added falls * in to one of these categories. */ SourceMapGenerator.prototype._validateMapping = function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, aName) { if (aGenerated && 'line' in aGenerated && 'column' in aGenerated && aGenerated.line > 0 && aGenerated.column >= 0 && !aOriginal && !aSource && !aName) { // Case 1. return; } else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated && aOriginal && 'line' in aOriginal && 'column' in aOriginal && aGenerated.line > 0 && aGenerated.column >= 0 && aOriginal.line > 0 && aOriginal.column >= 0 && aSource) { // Cases 2 and 3. return; } else { throw new Error('Invalid mapping: ' + JSON.stringify({ generated: aGenerated, source: aSource, orginal: aOriginal, name: aName })); } }; /** * Serialize the accumulated mappings in to the stream of base 64 VLQs * specified by the source map format. */ SourceMapGenerator.prototype._serializeMappings = function SourceMapGenerator_serializeMappings() { var previousGeneratedColumn = 0; var previousGeneratedLine = 1; var previousOriginalColumn = 0; var previousOriginalLine = 0; var previousName = 0; var previousSource = 0; var result = ''; var mapping; // The mappings must be guaranteed to be in sorted order before we start // serializing them or else the generated line numbers (which are defined // via the ';' separators) will be all messed up. Note: it might be more // performant to maintain the sorting as we insert them, rather than as we // serialize them, but the big O is the same either way. this._mappings.sort(util.compareByGeneratedPositions); for (var i = 0, len = this._mappings.length; i < len; i++) { mapping = this._mappings[i]; if (mapping.generatedLine !== previousGeneratedLine) { previousGeneratedColumn = 0; while (mapping.generatedLine !== previousGeneratedLine) { result += ';'; previousGeneratedLine++; } } else { if (i > 0) { if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { continue; } result += ','; } } result += base64VLQ.encode(mapping.generatedColumn - previousGeneratedColumn); previousGeneratedColumn = mapping.generatedColumn; if (mapping.source) { result += base64VLQ.encode(this._sources.indexOf(mapping.source) - previousSource); previousSource = this._sources.indexOf(mapping.source); // lines are stored 0-based in SourceMap spec version 3 result += base64VLQ.encode(mapping.originalLine - 1 - previousOriginalLine); previousOriginalLine = mapping.originalLine - 1; result += base64VLQ.encode(mapping.originalColumn - previousOriginalColumn); previousOriginalColumn = mapping.originalColumn; if (mapping.name) { result += base64VLQ.encode(this._names.indexOf(mapping.name) - previousName); previousName = this._names.indexOf(mapping.name); } } } return result; }; SourceMapGenerator.prototype._generateSourcesContent = function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { return aSources.map(function (source) { if (!this._sourcesContents) { return null; } if (aSourceRoot) { source = util.relative(aSourceRoot, source); } var key = util.toSetString(source); return Object.prototype.hasOwnProperty.call(this._sourcesContents, key) ? this._sourcesContents[key] : null; }, this); }; /** * Externalize the source map. */ SourceMapGenerator.prototype.toJSON = function SourceMapGenerator_toJSON() { var map = { version: this._version, file: this._file, sources: this._sources.toArray(), names: this._names.toArray(), mappings: this._serializeMappings() }; if (this._sourceRoot) { map.sourceRoot = this._sourceRoot; } if (this._sourcesContents) { map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); } return map; }; /** * Render the source map being generated to a string. */ SourceMapGenerator.prototype.toString = function SourceMapGenerator_toString() { return JSON.stringify(this); }; exports.SourceMapGenerator = SourceMapGenerator; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause * * Based on the Base 64 VLQ implementation in Closure Compiler: * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java * * Copyright 2011 The Closure Compiler Authors. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { var base64 = require('./base64'); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, // the next four bits are the actual value, and the 6th bit is the // continuation bit. The continuation bit tells us whether there are more // digits in this value following this digit. // // Continuation // | Sign // | | // V V // 101011 var VLQ_BASE_SHIFT = 5; // binary: 100000 var VLQ_BASE = 1 << VLQ_BASE_SHIFT; // binary: 011111 var VLQ_BASE_MASK = VLQ_BASE - 1; // binary: 100000 var VLQ_CONTINUATION_BIT = VLQ_BASE; /** * Converts from a two-complement value to a value where the sign bit is * is placed in the least significant bit. For example, as decimals: * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) */ function toVLQSigned(aValue) { return aValue < 0 ? ((-aValue) << 1) + 1 : (aValue << 1) + 0; } /** * Converts to a two-complement value from a value where the sign bit is * is placed in the least significant bit. For example, as decimals: * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 */ function fromVLQSigned(aValue) { var isNegative = (aValue & 1) === 1; var shifted = aValue >> 1; return isNegative ? -shifted : shifted; } /** * Returns the base 64 VLQ encoded value. */ exports.encode = function base64VLQ_encode(aValue) { var encoded = ""; var digit; var vlq = toVLQSigned(aValue); do { digit = vlq & VLQ_BASE_MASK; vlq >>>= VLQ_BASE_SHIFT; if (vlq > 0) { // There are still more digits in this value, so we must make sure the // continuation bit is marked. digit |= VLQ_CONTINUATION_BIT; } encoded += base64.encode(digit); } while (vlq > 0); return encoded; }; /** * Decodes the next base 64 VLQ value from the given string and returns the * value and the rest of the string. */ exports.decode = function base64VLQ_decode(aStr) { var i = 0; var strLen = aStr.length; var result = 0; var shift = 0; var continuation, digit; do { if (i >= strLen) { throw new Error("Expected more digits in base 64 VLQ value."); } digit = base64.decode(aStr.charAt(i++)); continuation = !!(digit & VLQ_CONTINUATION_BIT); digit &= VLQ_BASE_MASK; result = result + (digit << shift); shift += VLQ_BASE_SHIFT; } while (continuation); return { value: fromVLQSigned(result), rest: aStr.slice(i) }; }; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { var charToIntMap = {}; var intToCharMap = {}; 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' .split('') .forEach(function (ch, index) { charToIntMap[ch] = index; intToCharMap[index] = ch; }); /** * Encode an integer in the range of 0 to 63 to a single base 64 digit. */ exports.encode = function base64_encode(aNumber) { if (aNumber in intToCharMap) { return intToCharMap[aNumber]; } throw new TypeError("Must be between 0 and 63: " + aNumber); }; /** * Decode a single base 64 digit to an integer. */ exports.decode = function base64_decode(aChar) { if (aChar in charToIntMap) { return charToIntMap[aChar]; } throw new TypeError("Not a valid base 64 digit: " + aChar); }; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { /** * This is a helper function for getting values from parameter/options * objects. * * @param args The object we are extracting values from * @param name The name of the property we are getting. * @param defaultValue An optional value to return if the property is missing * from the object. If this is not specified and the property is missing, an * error will be thrown. */ function getArg(aArgs, aName, aDefaultValue) { if (aName in aArgs) { return aArgs[aName]; } else if (arguments.length === 3) { return aDefaultValue; } else { throw new Error('"' + aName + '" is a required argument.'); } } exports.getArg = getArg; var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; var dataUrlRegexp = /^data:.+\,.+/; function urlParse(aUrl) { var match = aUrl.match(urlRegexp); if (!match) { return null; } return { scheme: match[1], auth: match[3], host: match[4], port: match[6], path: match[7] }; } exports.urlParse = urlParse; function urlGenerate(aParsedUrl) { var url = aParsedUrl.scheme + "://"; if (aParsedUrl.auth) { url += aParsedUrl.auth + "@" } if (aParsedUrl.host) { url += aParsedUrl.host; } if (aParsedUrl.port) { url += ":" + aParsedUrl.port } if (aParsedUrl.path) { url += aParsedUrl.path; } return url; } exports.urlGenerate = urlGenerate; function join(aRoot, aPath) { var url; if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { return aPath; } if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { url.path = aPath; return urlGenerate(url); } return aRoot.replace(/\/$/, '') + '/' + aPath; } exports.join = join; /** * Because behavior goes wacky when you set `__proto__` on objects, we * have to prefix all the strings in our set with an arbitrary character. * * See https://github.com/mozilla/source-map/pull/31 and * https://github.com/mozilla/source-map/issues/30 * * @param String aStr */ function toSetString(aStr) { return '$' + aStr; } exports.toSetString = toSetString; function fromSetString(aStr) { return aStr.substr(1); } exports.fromSetString = fromSetString; function relative(aRoot, aPath) { aRoot = aRoot.replace(/\/$/, ''); var url = urlParse(aRoot); if (aPath.charAt(0) == "/" && url && url.path == "/") { return aPath.slice(1); } return aPath.indexOf(aRoot + '/') === 0 ? aPath.substr(aRoot.length + 1) : aPath; } exports.relative = relative; function strcmp(aStr1, aStr2) { var s1 = aStr1 || ""; var s2 = aStr2 || ""; return (s1 > s2) - (s1 < s2); } /** * Comparator between two mappings where the original positions are compared. * * Optionally pass in `true` as `onlyCompareGenerated` to consider two * mappings with the same original source/line/column, but different generated * line and column the same. Useful when searching for a mapping with a * stubbed out mapping. */ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { var cmp; cmp = strcmp(mappingA.source, mappingB.source); if (cmp) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp || onlyCompareOriginal) { return cmp; } cmp = strcmp(mappingA.name, mappingB.name); if (cmp) { return cmp; } cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp) { return cmp; } return mappingA.generatedColumn - mappingB.generatedColumn; }; exports.compareByOriginalPositions = compareByOriginalPositions; /** * Comparator between two mappings where the generated positions are * compared. * * Optionally pass in `true` as `onlyCompareGenerated` to consider two * mappings with the same generated line and column, but different * source/name/original line and column the same. Useful when searching for a * mapping with a stubbed out mapping. */ function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { var cmp; cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp || onlyCompareGenerated) { return cmp; } cmp = strcmp(mappingA.source, mappingB.source); if (cmp) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp) { return cmp; } return strcmp(mappingA.name, mappingB.name); }; exports.compareByGeneratedPositions = compareByGeneratedPositions; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { var util = require('./util'); /** * A data structure which is a combination of an array and a set. Adding a new * member is O(1), testing for membership is O(1), and finding the index of an * element is O(1). Removing elements from the set is not supported. Only * strings are supported for membership. */ function ArraySet() { this._array = []; this._set = {}; } /** * Static method for creating ArraySet instances from an existing array. */ ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { var set = new ArraySet(); for (var i = 0, len = aArray.length; i < len; i++) { set.add(aArray[i], aAllowDuplicates); } return set; }; /** * Add the given string to this set. * * @param String aStr */ ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { var isDuplicate = this.has(aStr); var idx = this._array.length; if (!isDuplicate || aAllowDuplicates) { this._array.push(aStr); } if (!isDuplicate) { this._set[util.toSetString(aStr)] = idx; } }; /** * Is the given string a member of this set? * * @param String aStr */ ArraySet.prototype.has = function ArraySet_has(aStr) { return Object.prototype.hasOwnProperty.call(this._set, util.toSetString(aStr)); }; /** * What is the index of the given string in the array? * * @param String aStr */ ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { if (this.has(aStr)) { return this._set[util.toSetString(aStr)]; } throw new Error('"' + aStr + '" is not in the set.'); }; /** * What is the element at the given index? * * @param Number aIdx */ ArraySet.prototype.at = function ArraySet_at(aIdx) { if (aIdx >= 0 && aIdx < this._array.length) { return this._array[aIdx]; } throw new Error('No element indexed by ' + aIdx); }; /** * Returns the array representation of this set (which has the proper indices * indicated by indexOf). Note that this is a copy of the internal array used * for storing the members so that no one can mess with internal state. */ ArraySet.prototype.toArray = function ArraySet_toArray() { return this._array.slice(); }; exports.ArraySet = ArraySet; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { var util = require('./util'); var binarySearch = require('./binary-search'); var ArraySet = require('./array-set').ArraySet; var base64VLQ = require('./base64-vlq'); /** * A SourceMapConsumer instance represents a parsed source map which we can * query for information about the original file positions by giving it a file * position in the generated source. * * The only parameter is the raw source map (either as a JSON string, or * already parsed to an object). According to the spec, source maps have the * following attributes: * * - version: Which version of the source map spec this map is following. * - sources: An array of URLs to the original source files. * - names: An array of identifiers which can be referrenced by individual mappings. * - sourceRoot: Optional. The URL root from which all sources are relative. * - sourcesContent: Optional. An array of contents of the original source files. * - mappings: A string of base64 VLQs which contain the actual mappings. * - file: The generated file this source map is associated with. * * Here is an example source map, taken from the source map spec[0]: * * { * version : 3, * file: "out.js", * sourceRoot : "", * sources: ["foo.js", "bar.js"], * names: ["src", "maps", "are", "fun"], * mappings: "AA,AB;;ABCDE;" * } * * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# */ function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; if (typeof aSourceMap === 'string') { sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } var version = util.getArg(sourceMap, 'version'); var sources = util.getArg(sourceMap, 'sources'); var names = util.getArg(sourceMap, 'names'); var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); var mappings = util.getArg(sourceMap, 'mappings'); var file = util.getArg(sourceMap, 'file', null); if (version !== this._version) { throw new Error('Unsupported version: ' + version); } // Pass `true` below to allow duplicate names and sources. While source maps // are intended to be compressed and deduplicated, the TypeScript compiler // sometimes generates source maps with duplicates in them. See Github issue // #72 and bugzil.la/889492. this._names = ArraySet.fromArray(names, true); this._sources = ArraySet.fromArray(sources, true); this.sourceRoot = sourceRoot; this.sourcesContent = sourcesContent; this._mappings = mappings; this.file = file; } /** * Create a SourceMapConsumer from a SourceMapGenerator. * * @param SourceMapGenerator aSourceMap * The source map that will be consumed. * @returns SourceMapConsumer */ SourceMapConsumer.fromSourceMap = function SourceMapConsumer_fromSourceMap(aSourceMap) { var smc = Object.create(SourceMapConsumer.prototype); smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); smc.sourceRoot = aSourceMap._sourceRoot; smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), smc.sourceRoot); smc.file = aSourceMap._file; smc.__generatedMappings = aSourceMap._mappings.slice() .sort(util.compareByGeneratedPositions); smc.__originalMappings = aSourceMap._mappings.slice() .sort(util.compareByOriginalPositions); return smc; }; /** * The version of the source mapping spec that we are consuming. */ SourceMapConsumer.prototype._version = 3; /** * The list of original sources. */ Object.defineProperty(SourceMapConsumer.prototype, 'sources', { get: function () { return this._sources.toArray().map(function (s) { return this.sourceRoot ? util.join(this.sourceRoot, s) : s; }, this); } }); // `__generatedMappings` and `__originalMappings` are arrays that hold the // parsed mapping coordinates from the source map's "mappings" attribute. They // are lazily instantiated, accessed via the `_generatedMappings` and // `_originalMappings` getters respectively, and we only parse the mappings // and create these arrays once queried for a source location. We jump through // these hoops because there can be many thousands of mappings, and parsing // them is expensive, so we only want to do it if we must. // // Each object in the arrays is of the form: // // { // generatedLine: The line number in the generated code, // generatedColumn: The column number in the generated code, // source: The path to the original source file that generated this // chunk of code, // originalLine: The line number in the original source that // corresponds to this chunk of generated code, // originalColumn: The column number in the original source that // corresponds to this chunk of generated code, // name: The name of the original symbol which generated this chunk of // code. // } // // All properties except for `generatedLine` and `generatedColumn` can be // `null`. // // `_generatedMappings` is ordered by the generated positions. // // `_originalMappings` is ordered by the original positions. SourceMapConsumer.prototype.__generatedMappings = null; Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { get: function () { if (!this.__generatedMappings) { this.__generatedMappings = []; this.__originalMappings = []; this._parseMappings(this._mappings, this.sourceRoot); } return this.__generatedMappings; } }); SourceMapConsumer.prototype.__originalMappings = null; Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { get: function () { if (!this.__originalMappings) { this.__generatedMappings = []; this.__originalMappings = []; this._parseMappings(this._mappings, this.sourceRoot); } return this.__originalMappings; } }); /** * Parse the mappings in a string in to a data structure which we can easily * query (the ordered arrays in the `this.__generatedMappings` and * `this.__originalMappings` properties). */ SourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { var generatedLine = 1; var previousGeneratedColumn = 0; var previousOriginalLine = 0; var previousOriginalColumn = 0; var previousSource = 0; var previousName = 0; var mappingSeparator = /^[,;]/; var str = aStr; var mapping; var temp; while (str.length > 0) { if (str.charAt(0) === ';') { generatedLine++; str = str.slice(1); previousGeneratedColumn = 0; } else if (str.charAt(0) === ',') { str = str.slice(1); } else { mapping = {}; mapping.generatedLine = generatedLine; // Generated column. temp = base64VLQ.decode(str); mapping.generatedColumn = previousGeneratedColumn + temp.value; previousGeneratedColumn = mapping.generatedColumn; str = temp.rest; if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { // Original source. temp = base64VLQ.decode(str); mapping.source = this._sources.at(previousSource + temp.value); previousSource += temp.value; str = temp.rest; if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { throw new Error('Found a source, but no line and column'); } // Original line. temp = base64VLQ.decode(str); mapping.originalLine = previousOriginalLine + temp.value; previousOriginalLine = mapping.originalLine; // Lines are stored 0-based mapping.originalLine += 1; str = temp.rest; if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { throw new Error('Found a source and line, but no column'); } // Original column. temp = base64VLQ.decode(str); mapping.originalColumn = previousOriginalColumn + temp.value; previousOriginalColumn = mapping.originalColumn; str = temp.rest; if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { // Original name. temp = base64VLQ.decode(str); mapping.name = this._names.at(previousName + temp.value); previousName += temp.value; str = temp.rest; } } this.__generatedMappings.push(mapping); if (typeof mapping.originalLine === 'number') { this.__originalMappings.push(mapping); } } } this.__originalMappings.sort(util.compareByOriginalPositions); }; /** * Find the mapping that best matches the hypothetical "needle" mapping that * we are searching for in the given "haystack" of mappings. */ SourceMapConsumer.prototype._findMapping = function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, aColumnName, aComparator) { // To return the position we are searching for, we must first find the // mapping for the given position and then return the opposite position it // points to. Because the mappings are sorted, we can use binary search to // find the best mapping. if (aNeedle[aLineName] <= 0) { throw new TypeError('Line must be greater than or equal to 1, got ' + aNeedle[aLineName]); } if (aNeedle[aColumnName] < 0) { throw new TypeError('Column must be greater than or equal to 0, got ' + aNeedle[aColumnName]); } return binarySearch.search(aNeedle, aMappings, aComparator); }; /** * Returns the original source, line, and column information for the generated * source's line and column positions provided. The only argument is an object * with the following properties: * * - line: The line number in the generated source. * - column: The column number in the generated source. * * and an object is returned with the following properties: * * - source: The original source file, or null. * - line: The line number in the original source, or null. * - column: The column number in the original source, or null. * - name: The original identifier, or null. */ SourceMapConsumer.prototype.originalPositionFor = function SourceMapConsumer_originalPositionFor(aArgs) { var needle = { generatedLine: util.getArg(aArgs, 'line'), generatedColumn: util.getArg(aArgs, 'column') }; var mapping = this._findMapping(needle, this._generatedMappings, "generatedLine", "generatedColumn", util.compareByGeneratedPositions); if (mapping) { var source = util.getArg(mapping, 'source', null); if (source && this.sourceRoot) { source = util.join(this.sourceRoot, source); } return { source: source, line: util.getArg(mapping, 'originalLine', null), column: util.getArg(mapping, 'originalColumn', null), name: util.getArg(mapping, 'name', null) }; } return { source: null, line: null, column: null, name: null }; }; /** * Returns the original source content. The only argument is the url of the * original source file. Returns null if no original source content is * availible. */ SourceMapConsumer.prototype.sourceContentFor = function SourceMapConsumer_sourceContentFor(aSource) { if (!this.sourcesContent) { return null; } if (this.sourceRoot) { aSource = util.relative(this.sourceRoot, aSource); } if (this._sources.has(aSource)) { return this.sourcesContent[this._sources.indexOf(aSource)]; } var url; if (this.sourceRoot && (url = util.urlParse(this.sourceRoot))) { // XXX: file:// URIs and absolute paths lead to unexpected behavior for // many users. We can help them out when they expect file:// URIs to // behave like it would if they were running a local HTTP server. See // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); if (url.scheme == "file" && this._sources.has(fileUriAbsPath)) { return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] } if ((!url.path || url.path == "/") && this._sources.has("/" + aSource)) { return this.sourcesContent[this._sources.indexOf("/" + aSource)]; } } throw new Error('"' + aSource + '" is not in the SourceMap.'); }; /** * Returns the generated line and column information for the original source, * line, and column positions provided. The only argument is an object with * the following properties: * * - source: The filename of the original source. * - line: The line number in the original source. * - column: The column number in the original source. * * and an object is returned with the following properties: * * - line: The line number in the generated source, or null. * - column: The column number in the generated source, or null. */ SourceMapConsumer.prototype.generatedPositionFor = function SourceMapConsumer_generatedPositionFor(aArgs) { var needle = { source: util.getArg(aArgs, 'source'), originalLine: util.getArg(aArgs, 'line'), originalColumn: util.getArg(aArgs, 'column') }; if (this.sourceRoot) { needle.source = util.relative(this.sourceRoot, needle.source); } var mapping = this._findMapping(needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions); if (mapping) { return { line: util.getArg(mapping, 'generatedLine', null), column: util.getArg(mapping, 'generatedColumn', null) }; } return { line: null, column: null }; }; SourceMapConsumer.GENERATED_ORDER = 1; SourceMapConsumer.ORIGINAL_ORDER = 2; /** * Iterate over each mapping between an original source/line/column and a * generated line/column in this source map. * * @param Function aCallback * The function that is called with each mapping. * @param Object aContext * Optional. If specified, this object will be the value of `this` every * time that `aCallback` is called. * @param aOrder * Either `SourceMapConsumer.GENERATED_ORDER` or * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to * iterate over the mappings sorted by the generated file's line/column * order or the original's source/line/column order, respectively. Defaults to * `SourceMapConsumer.GENERATED_ORDER`. */ SourceMapConsumer.prototype.eachMapping = function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { var context = aContext || null; var order = aOrder || SourceMapConsumer.GENERATED_ORDER; var mappings; switch (order) { case SourceMapConsumer.GENERATED_ORDER: mappings = this._generatedMappings; break; case SourceMapConsumer.ORIGINAL_ORDER: mappings = this._originalMappings; break; default: throw new Error("Unknown order of iteration."); } var sourceRoot = this.sourceRoot; mappings.map(function (mapping) { var source = mapping.source; if (source && sourceRoot) { source = util.join(sourceRoot, source); } return { source: source, generatedLine: mapping.generatedLine, generatedColumn: mapping.generatedColumn, originalLine: mapping.originalLine, originalColumn: mapping.originalColumn, name: mapping.name }; }).forEach(aCallback, context); }; exports.SourceMapConsumer = SourceMapConsumer; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { /** * Recursive implementation of binary search. * * @param aLow Indices here and lower do not contain the needle. * @param aHigh Indices here and higher do not contain the needle. * @param aNeedle The element being searched for. * @param aHaystack The non-empty array being searched. * @param aCompare Function which takes two elements and returns -1, 0, or 1. */ function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { // This function terminates when one of the following is true: // // 1. We find the exact element we are looking for. // // 2. We did not find the exact element, but we can return the next // closest element that is less than that element. // // 3. We did not find the exact element, and there is no next-closest // element which is less than the one we are searching for, so we // return null. var mid = Math.floor((aHigh - aLow) / 2) + aLow; var cmp = aCompare(aNeedle, aHaystack[mid], true); if (cmp === 0) { // Found the element we are looking for. return aHaystack[mid]; } else if (cmp > 0) { // aHaystack[mid] is greater than our needle. if (aHigh - mid > 1) { // The element is in the upper half. return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); } // We did not find an exact match, return the next closest one // (termination case 2). return aHaystack[mid]; } else { // aHaystack[mid] is less than our needle. if (mid - aLow > 1) { // The element is in the lower half. return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); } // The exact needle element was not found in this haystack. Determine if // we are in termination case (2) or (3) and return the appropriate thing. return aLow < 0 ? null : aHaystack[aLow]; } } /** * This is an implementation of binary search which will always try and return * the next lowest value checked if there is no exact hit. This is because * mappings between original and generated line/col pairs are single points, * and there is an implicit region between each of them, so a miss just means * that you aren't on the very start of a region. * * @param aNeedle The element you are looking for. * @param aHaystack The array that is being searched. * @param aCompare A function which takes the needle and an element in the * array and returns -1, 0, or 1 depending on whether the needle is less * than, equal to, or greater than the element, respectively. */ exports.search = function search(aNeedle, aHaystack, aCompare) { return aHaystack.length > 0 ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) : null; }; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; var util = require('./util'); /** * SourceNodes provide a way to abstract over interpolating/concatenating * snippets of generated JavaScript source code while maintaining the line and * column information associated with the original source code. * * @param aLine The original line number. * @param aColumn The original column number. * @param aSource The original source's filename. * @param aChunks Optional. An array of strings which are snippets of * generated JS, or other SourceNodes. * @param aName The original identifier. */ function SourceNode(aLine, aColumn, aSource, aChunks, aName) { this.children = []; this.sourceContents = {}; this.line = aLine === undefined ? null : aLine; this.column = aColumn === undefined ? null : aColumn; this.source = aSource === undefined ? null : aSource; this.name = aName === undefined ? null : aName; if (aChunks != null) this.add(aChunks); } /** * Creates a SourceNode from generated code and a SourceMapConsumer. * * @param aGeneratedCode The generated code * @param aSourceMapConsumer The SourceMap for the generated code */ SourceNode.fromStringWithSourceMap = function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { // The SourceNode we want to fill with the generated code // and the SourceMap var node = new SourceNode(); // The generated code // Processed fragments are removed from this array. var remainingLines = aGeneratedCode.split('\n'); // We need to remember the position of "remainingLines" var lastGeneratedLine = 1, lastGeneratedColumn = 0; // The generate SourceNodes we need a code range. // To extract it current and last mapping is used. // Here we store the last mapping. var lastMapping = null; aSourceMapConsumer.eachMapping(function (mapping) { if (lastMapping === null) { // We add the generated code until the first mapping // to the SourceNode without any mapping. // Each line is added as separate string. while (lastGeneratedLine < mapping.generatedLine) { node.add(remainingLines.shift() + "\n"); lastGeneratedLine++; } if (lastGeneratedColumn < mapping.generatedColumn) { var nextLine = remainingLines[0]; node.add(nextLine.substr(0, mapping.generatedColumn)); remainingLines[0] = nextLine.substr(mapping.generatedColumn); lastGeneratedColumn = mapping.generatedColumn; } } else { // We add the code from "lastMapping" to "mapping": // First check if there is a new line in between. if (lastGeneratedLine < mapping.generatedLine) { var code = ""; // Associate full lines with "lastMapping" do { code += remainingLines.shift() + "\n"; lastGeneratedLine++; lastGeneratedColumn = 0; } while (lastGeneratedLine < mapping.generatedLine); // When we reached the correct line, we add code until we // reach the correct column too. if (lastGeneratedColumn < mapping.generatedColumn) { var nextLine = remainingLines[0]; code += nextLine.substr(0, mapping.generatedColumn); remainingLines[0] = nextLine.substr(mapping.generatedColumn); lastGeneratedColumn = mapping.generatedColumn; } // Create the SourceNode. addMappingWithCode(lastMapping, code); } else { // There is no new line in between. // Associate the code between "lastGeneratedColumn" and // "mapping.generatedColumn" with "lastMapping" var nextLine = remainingLines[0]; var code = nextLine.substr(0, mapping.generatedColumn - lastGeneratedColumn); remainingLines[0] = nextLine.substr(mapping.generatedColumn - lastGeneratedColumn); lastGeneratedColumn = mapping.generatedColumn; addMappingWithCode(lastMapping, code); } } lastMapping = mapping; }, this); // We have processed all mappings. // Associate the remaining code in the current line with "lastMapping" // and add the remaining lines without any mapping addMappingWithCode(lastMapping, remainingLines.join("\n")); // Copy sourcesContent into SourceNode aSourceMapConsumer.sources.forEach(function (sourceFile) { var content = aSourceMapConsumer.sourceContentFor(sourceFile); if (content) { node.setSourceContent(sourceFile, content); } }); return node; function addMappingWithCode(mapping, code) { if (mapping === null || mapping.source === undefined) { node.add(code); } else { node.add(new SourceNode(mapping.originalLine, mapping.originalColumn, mapping.source, code, mapping.name)); } } }; /** * Add a chunk of generated JS to this source node. * * @param aChunk A string snippet of generated JS code, another instance of * SourceNode, or an array where each member is one of those things. */ SourceNode.prototype.add = function SourceNode_add(aChunk) { if (Array.isArray(aChunk)) { aChunk.forEach(function (chunk) { this.add(chunk); }, this); } else if (aChunk instanceof SourceNode || typeof aChunk === "string") { if (aChunk) { this.children.push(aChunk); } } else { throw new TypeError( "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk ); } return this; }; /** * Add a chunk of generated JS to the beginning of this source node. * * @param aChunk A string snippet of generated JS code, another instance of * SourceNode, or an array where each member is one of those things. */ SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { if (Array.isArray(aChunk)) { for (var i = aChunk.length-1; i >= 0; i--) { this.prepend(aChunk[i]); } } else if (aChunk instanceof SourceNode || typeof aChunk === "string") { this.children.unshift(aChunk); } else { throw new TypeError( "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk ); } return this; }; /** * Walk over the tree of JS snippets in this node and its children. The * walking function is called once for each snippet of JS and is passed that * snippet and the its original associated source's line/column location. * * @param aFn The traversal function. */ SourceNode.prototype.walk = function SourceNode_walk(aFn) { var chunk; for (var i = 0, len = this.children.length; i < len; i++) { chunk = this.children[i]; if (chunk instanceof SourceNode) { chunk.walk(aFn); } else { if (chunk !== '') { aFn(chunk, { source: this.source, line: this.line, column: this.column, name: this.name }); } } } }; /** * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between * each of `this.children`. * * @param aSep The separator. */ SourceNode.prototype.join = function SourceNode_join(aSep) { var newChildren; var i; var len = this.children.length; if (len > 0) { newChildren = []; for (i = 0; i < len-1; i++) { newChildren.push(this.children[i]); newChildren.push(aSep); } newChildren.push(this.children[i]); this.children = newChildren; } return this; }; /** * Call String.prototype.replace on the very right-most source snippet. Useful * for trimming whitespace from the end of a source node, etc. * * @param aPattern The pattern to replace. * @param aReplacement The thing to replace the pattern with. */ SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { var lastChild = this.children[this.children.length - 1]; if (lastChild instanceof SourceNode) { lastChild.replaceRight(aPattern, aReplacement); } else if (typeof lastChild === 'string') { this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); } else { this.children.push(''.replace(aPattern, aReplacement)); } return this; }; /** * Set the source content for a source file. This will be added to the SourceMapGenerator * in the sourcesContent field. * * @param aSourceFile The filename of the source file * @param aSourceContent The content of the source file */ SourceNode.prototype.setSourceContent = function SourceNode_setSourceContent(aSourceFile, aSourceContent) { this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; }; /** * Walk over the tree of SourceNodes. The walking function is called for each * source file content and is passed the filename and source content. * * @param aFn The traversal function. */ SourceNode.prototype.walkSourceContents = function SourceNode_walkSourceContents(aFn) { for (var i = 0, len = this.children.length; i < len; i++) { if (this.children[i] instanceof SourceNode) { this.children[i].walkSourceContents(aFn); } } var sources = Object.keys(this.sourceContents); for (var i = 0, len = sources.length; i < len; i++) { aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); } }; /** * Return the string representation of this source node. Walks over the tree * and concatenates all the various snippets together to one string. */ SourceNode.prototype.toString = function SourceNode_toString() { var str = ""; this.walk(function (chunk) { str += chunk; }); return str; }; /** * Returns the string representation of this source node along with a source * map. */ SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { var generated = { code: "", line: 1, column: 0 }; var map = new SourceMapGenerator(aArgs); var sourceMappingActive = false; var lastOriginalSource = null; var lastOriginalLine = null; var lastOriginalColumn = null; var lastOriginalName = null; this.walk(function (chunk, original) { generated.code += chunk; if (original.source !== null && original.line !== null && original.column !== null) { if(lastOriginalSource !== original.source || lastOriginalLine !== original.line || lastOriginalColumn !== original.column || lastOriginalName !== original.name) { map.addMapping({ source: original.source, original: { line: original.line, column: original.column }, generated: { line: generated.line, column: generated.column }, name: original.name }); } lastOriginalSource = original.source; lastOriginalLine = original.line; lastOriginalColumn = original.column; lastOriginalName = original.name; sourceMappingActive = true; } else if (sourceMappingActive) { map.addMapping({ generated: { line: generated.line, column: generated.column } }); lastOriginalSource = null; sourceMappingActive = false; } chunk.split('').forEach(function (ch) { if (ch === '\n') { generated.line++; generated.column = 0; } else { generated.column++; } }); }); this.walkSourceContents(function (sourceFile, sourceContent) { map.setSourceContent(sourceFile, sourceContent); }); return { code: generated.code, map: map }; }; exports.SourceNode = SourceNode; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /////////////////////////////////////////////////////////////////////////////// this.sourceMap = { SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, SourceNode: require('source-map/source-node').SourceNode };