Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit 6cd7fd61cf287a5ae316b67a75258369d8bd8c37 0 parents
@jdiamond authored
3  .coverignore
@@ -0,0 +1,3 @@
+index.js
+lib/esprima.js
+test/test.js
4 .gitignore
@@ -0,0 +1,4 @@
+/.coverage_data
+/cover_html
+/node_modules
+/*.sublime-*
22 LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2012 Jason Diamond <jason@diamond.name>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 Makefile
@@ -0,0 +1,27 @@
+all: build hint cover
+
+build: lib/harmonizr.js demo/lib/harmonizr.js test/lib/harmonizr.js
+
+# Node.js-style
+lib/harmonizr.js: src/harmonizr.js bin/harmonizr
+ node bin/harmonizr --node src/harmonizr.js --output lib/harmonizr.js --relatives esprima
+
+# AMD-style
+demo/lib/harmonizr.js: src/harmonizr.js bin/harmonizr
+ node bin/harmonizr --amd src/harmonizr.js --output demo/lib/harmonizr.js
+
+# Module Pattern-style
+test/lib/harmonizr.js: src/harmonizr.js bin/harmonizr
+ node bin/harmonizr --revealing src/harmonizr.js --output test/lib/harmonizr.js
+
+hint: lib/harmonizr.js PHONY
+ node node_modules/jshint/bin/hint lib/harmonizr.js bin/harmonizr demo test
+
+test: PHONY
+ node node_modules/mocha/bin/mocha
+
+cover: PHONY
+ node node_modules/cover/bin/cover run node_modules/mocha/bin/_mocha
+ node node_modules/cover/bin/cover report html
+ node node_modules/cover/bin/cover report cli
+PHONY:
73 README.md
@@ -0,0 +1,73 @@
+# Harmonizr
+
+A "transpiler" that brings tomorrow's Harmony to today's JavaScript.
+
+## Features:
+
+ - Harmony modules to AMD, Node.js, or Revealing Module Pattern
+ - Maintains line numbers
+
+## Demo
+
+Look in the demo directory or try it
+[here](http://jdiamond.github.com/harmonizr/demo/demo.html).
+
+## Installation
+
+ $ npm install harmonizr
+
+## Usage
+
+ $ ./node_modules/.bin/harmonizr [options] path/to/input
+
+If you install Harmonizr globally or have ./node_modules/.bin in your
+PATH, you can omit the path to the harmonizr script.
+
+Specify `--amd`, `--node`, or `--revealing` to transform Harmony-style
+modules into AMD, Node.js, or JavaScript Revealing Module Pattern-
+style modules.
+
+This transpiles src/foo.js into a Node.js-compatible version at
+lib/foo.js:
+
+ $ harmonizr --node --output lib/foo.js src/foo.js
+
+Use `--module` to implicitly wrap the entire file in a module
+declaration. The name of the module is required, but only appears in
+the output when using `--revealing`.
+
+Use `--relatives` with `--node` to indicate what modules should be
+loaded from the local directory and not the node_modules directory.
+
+For example, if a `foo` module needs to load `bar` and `baz` modules
+from the same directory as it (not from node_modules), you could do
+this:
+
+ $ harmonizr --node --relatives bar,baz --output lib/foo.js src/foo.js
+
+## Limitations
+
+ - No nested modules.
+ - No `import * from module;`.
+ - `export` is only allowed when in front of a simple function or
+ variable declaration.
+ - Probably bugged.
+
+## Code
+
+The actual source code is in the src directory. The Makefile
+transpiles that into a Node.js-style module in lib, AMD-style in demo,
+and Revealing Module Pattern-style in test.
+
+Harmonizr transpiles itself. Since Node.js doesn't support the newer
+syntax harmonizr.js uses in the src directory, it loads the
+harmonizr module out of the lib directory. Be careful when building.
+
+Tests are the test directory. Run them with `npm test` or by opening
+test.html.
+
+If you'd like to contribute, please try to include tests, ensure the
+code coverage stays at 100%, and that JSHint doesn't complain.
+
+Execute `make` to build, run JSHint, and run the tests (with code
+coverage).
55 bin/harmonizr
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+
+var fs = require('fs');
+var program = require('commander');
+var harmonizr = require('../');
+
+program
+ .usage('[options] input')
+ .option('--output <path>', 'output path')
+ .option('--style <style>', 'module style (amd, node, or revealing)')
+ .option('--amd', 'short for --module amd')
+ .option('--node', 'short for --module node')
+ .option('--revealing', 'short for --module revealing')
+ .option('--module <name>', 'implied module name')
+ .option('--relatives <modules>', 'relative modules (used by node)', list)
+ .parse(process.argv);
+
+var files = program.args;
+
+if (files.length) {
+ files.forEach(harmonize);
+} else {
+ process.stdout.write(program.helpInformation());
+}
+
+function harmonize(path) {
+ fs.readFile(path, 'utf8', function(err, src) {
+ if (err) {
+ process.stderr.write(err.message);
+ } else {
+ program.style = program.amd ? 'amd' :
+ program.node ? 'node' :
+ program.revealing ? 'revealing' :
+ program.style;
+
+ var options = {
+ style: program.style || 'node',
+ module: program.module,
+ relatives: program.relatives
+ };
+
+ src = harmonizr.harmonize(src, options);
+
+ if (program.output) {
+ fs.writeFileSync(program.output, src);
+ } else {
+ process.stdout.write(src);
+ }
+ }
+ });
+}
+
+function list(val) {
+ return val.split(',');
+}
20 demo/demo.css
@@ -0,0 +1,20 @@
+.CodeMirror {
+ border: 1px solid #666;
+}
+
+h1, #editor, #error, #result {
+ margin-top: 16px;
+ margin-bottom: 16px;
+}
+
+.currentLine {
+ background-color: #e8f2ff !important;
+}
+
+#options span {
+ margin-right: 16px;
+}
+
+#options input, select {
+ margin-bottom: auto;
+}
51 demo/demo.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>harmonizr demo</title>
+ <link rel="stylesheet" href="lib/bootstrap.css">
+ <link rel="stylesheet" href="lib/codemirror.css">
+ <link rel="stylesheet" href="demo.css">
+ </head>
+ <body>
+ <div class="container">
+ <h1>harmonizr demo</h1>
+
+ <div id="editor"></div>
+
+ <form id="options" class="form-vertical">
+ <span title="Module format style">
+ Transpile to:
+ <select id="styles">
+ <option value="amd">AMD</option>
+ <option value="node">Node.js</option>
+ <option value="revealing">Module Pattern</option>
+ </select>
+ </span>
+ <span title="Assumed module name">
+ Module:
+ <input id="module" type="text">
+ </span>
+ <span title="Comma-separated list of relative module names">
+ Relatives:
+ <input id="relatives" type="text">
+ </span>
+ </form>
+
+ <div id="error" class="alert alert-error" style="display: none;"></div>
+
+ <div id="result"></div>
+ </div>
+
+ <script type="example">
+module foo {
+ import bar from baz;
+ export function quux() {
+ return bar() + 1;
+ }
+}
+ </script>
+
+ <script src="lib/require.js" data-main="demo"></script>
+ </body>
+</html>
126 demo/demo.js
@@ -0,0 +1,126 @@
+require({
+ paths: {
+ harmonizr: 'lib/harmonizr',
+ esprima: '../lib/esprima',
+ jquery: 'lib/jquery-1.7.2',
+ codemirror: 'lib/codemirror',
+ codemirrorjavascript: 'lib/javascript'
+ },
+ shim: {
+ esprima: {
+ exports: 'esprima'
+ },
+ codemirror: {
+ exports: 'CodeMirror'
+ },
+ codemirrorjavascript: {
+ deps: ['codemirror']
+ }
+ }
+}, [
+ 'harmonizr',
+ 'jquery',
+ 'codemirror',
+ 'codemirrorjavascript'
+], function(harmonizr, $, CodeMirror) {
+
+ $(function() {
+ var editor = CodeMirror($('#editor')[0], {
+ lineNumbers: true,
+ matchBrackets: true,
+ autofocus: true,
+ onCursorActivity: cursorChanged,
+ onChange: codeChanged,
+ extraKeys: {
+ 'Esc': function() { $('#styles').focus(); }
+ }
+ });
+
+ var result = CodeMirror($('#result')[0], {
+ lineNumbers: true,
+ matchBrackets: true,
+ readOnly: true,
+ onCursorActivity: cursorChanged,
+ extraKeys: {
+ 'Esc': function() { editor.focus(); }
+ }
+ });
+
+ function cursorChanged(e) {
+ clearHighlights();
+ highlightLines(e.getCursor().line);
+ }
+
+ var highlightClass = 'currentLine';
+ var highlightIndex = -1;
+
+ function highlightLines(lineNumber) {
+ if (lineNumber < 0) return;
+ highlightIndex = lineNumber;
+ editor.setLineClass(highlightIndex, null, highlightClass);
+ result.setLineClass(highlightIndex, null, highlightClass);
+ }
+
+ function clearHighlights() {
+ if (highlightIndex !== -1) {
+ editor.setLineClass(highlightIndex, null, null);
+ result.setLineClass(highlightIndex, null, null);
+ highlightIndex = -1;
+ }
+ }
+
+ function codeChanged() {
+ try {
+ var code = editor.getValue();
+ var ast = esprima.parse(code);
+ run();
+ showResult();
+ } catch (e) {
+ showError(e.message);
+ }
+ }
+
+ function run() {
+ var src = editor.getValue();
+ var result = harmonizr.harmonize(src, {
+ style: $('#styles').val(),
+ module: $('#module').val(),
+ relatives: ($('#relatives').val() || '').split(',')
+ });
+ setResult(result);
+ }
+
+ function setResult(src) {
+ // Remember what lines are highlighted.
+ var currentLine = highlightIndex;
+ // Clear the existing highlights.
+ clearHighlights();
+ // Calling setValue() triggers onCursorActivity.
+ result.setValue(src);
+ // Clear the highlights created by setValue().
+ clearHighlights();
+ // Restore the original highlights.
+ highlightLines(currentLine);
+ }
+
+ function showResult() {
+ $('#result').show();
+ $('#error').hide().empty();
+ result.refresh();
+ }
+
+ function showError(msg) {
+ $('#error').html(msg).show();
+ $('#result').hide();
+ }
+
+ $('#styles, #module, #relatives').on('change input', run);
+
+ var src = $('script[type=example]').text();
+ editor.setValue(src.replace(/^\s*\r?\n|\r?\n\s*$/g, ''));
+
+ run();
+ highlightLines(0);
+ });
+
+});
4,960 demo/lib/bootstrap.css
4,960 additions, 0 deletions not shown
114 demo/lib/codemirror.css
@@ -0,0 +1,114 @@
+.CodeMirror {
+ line-height: 1em;
+ font-family: monospace;
+}
+
+.CodeMirror-scroll {
+ overflow: auto;
+ height: 300px;
+ /* This is needed to prevent an IE[67] bug where the scrolled content
+ is visible outside of the scrolling box. */
+ position: relative;
+ outline: none;
+}
+
+.CodeMirror-gutter {
+ position: absolute; left: 0; top: 0;
+ z-index: 10;
+ background-color: #f7f7f7;
+ border-right: 1px solid #eee;
+ min-width: 2em;
+ height: 100%;
+}
+.CodeMirror-gutter-text {
+ color: #aaa;
+ text-align: right;
+ padding: .4em .2em .4em .4em;
+ white-space: pre !important;
+}
+.CodeMirror-lines {
+ padding: .4em;
+ white-space: pre;
+}
+
+.CodeMirror pre {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ -o-border-radius: 0;
+ border-radius: 0;
+ border-width: 0; margin: 0; padding: 0; background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ padding: 0; margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+}
+
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+.CodeMirror-wrap .CodeMirror-scroll {
+ overflow-x: hidden;
+}
+
+.CodeMirror textarea {
+ outline: none !important;
+}
+
+.CodeMirror pre.CodeMirror-cursor {
+ z-index: 10;
+ position: absolute;
+ visibility: hidden;
+ border-left: 1px solid black;
+ border-right:none;
+ width:0;
+}
+.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
+.CodeMirror-focused pre.CodeMirror-cursor {
+ visibility: visible;
+}
+
+div.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
+
+.CodeMirror-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* Default theme */
+
+.cm-s-default span.cm-keyword {color: #708;}
+.cm-s-default span.cm-atom {color: #219;}
+.cm-s-default span.cm-number {color: #164;}
+.cm-s-default span.cm-def {color: #00f;}
+.cm-s-default span.cm-variable {color: black;}
+.cm-s-default span.cm-variable-2 {color: #05a;}
+.cm-s-default span.cm-variable-3 {color: #085;}
+.cm-s-default span.cm-property {color: black;}
+.cm-s-default span.cm-operator {color: black;}
+.cm-s-default span.cm-comment {color: #a50;}
+.cm-s-default span.cm-string {color: #a11;}
+.cm-s-default span.cm-string-2 {color: #f50;}
+.cm-s-default span.cm-meta {color: #555;}
+.cm-s-default span.cm-error {color: #f00;}
+.cm-s-default span.cm-qualifier {color: #555;}
+.cm-s-default span.cm-builtin {color: #30a;}
+.cm-s-default span.cm-bracket {color: #cc7;}
+.cm-s-default span.cm-tag {color: #170;}
+.cm-s-default span.cm-attribute {color: #00c;}
+.cm-s-default span.cm-header {color: #a0a;}
+.cm-s-default span.cm-quote {color: #090;}
+.cm-s-default span.cm-hr {color: #999;}
+.cm-s-default span.cm-link {color: #00c;}
+
+span.cm-header, span.cm-strong {font-weight: bold;}
+span.cm-em {font-style: italic;}
+span.cm-emstrong {font-style: italic; font-weight: bold;}
+span.cm-link {text-decoration: underline;}
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
3,047 demo/lib/codemirror.js
3,047 additions, 0 deletions not shown
268 demo/lib/harmonizr.js
@@ -0,0 +1,268 @@
+define(['esprima'], function(esprima) {
+
+var parse = esprima.parse, Syntax = esprima.Syntax;
+
+function harmonize(src, options) {
+ options = options || {};
+ return processModules(src, options, moduleStyles[options.style]);
+}
+
+function processModules(src, options, style) {
+ if (options.module) {
+ src = 'module ' + options.module + '{\n' + src + '\n}';
+ }
+
+ var ast = parse(src, { loc: true });
+
+ var modules = [];
+
+ traverse(ast, function(node) {
+ if (node.type === Syntax.ModuleDeclaration) {
+ modules.push(node);
+ return false;
+ }
+ });
+
+ var lines = src.split('\n');
+
+ modules.forEach(function(mod) {
+
+ options.indent = detectIndent(mod, lines);
+
+ var imps = [];
+
+ traverse(mod, function(node) {
+ if (node.type === Syntax.ModuleDeclaration && node !== mod) {
+ return false;
+ } else if (node.type === Syntax.ImportDeclaration &&
+ node.specifiers[0].type !== Syntax.Glob) {
+ imps.push(node);
+ }
+ });
+
+ var moduleStartLine = mod.loc.start.line - 1;
+ var moduleStartColumn = mod.loc.start.column;
+ var moduleEndLine = mod.loc.end.line - 1;
+ var moduleEndColumn = mod.loc.end.column;
+ var bodyStartColumn = mod.body.loc.start.column;
+ var bodyEndColumn = mod.body.loc.end.column;
+
+ // Modify the end first in case it's on the same line as the start.
+ lines[moduleEndLine] = splice(
+ lines[moduleEndLine],
+ bodyEndColumn - 1,
+ 1, // Delete the closing brace.
+ style.endModule(mod, options));
+
+ lines[moduleStartLine] = splice(
+ lines[moduleStartLine],
+ moduleStartColumn,
+ bodyStartColumn - moduleStartColumn + 1, // Delete from start of module to opening brace.
+ style.startModule(mod, imps, options));
+
+ imps.forEach(function(imp) {
+ var importStartLine = imp.loc.start.line - 1;
+ var importStartColumn = imp.loc.start.column;
+ var importEndColumn = imp.loc.end.column;
+ lines[importStartLine] = splice(
+ lines[importStartLine],
+ importStartColumn,
+ importEndColumn - importStartColumn,
+ style.importDeclaration(mod, imp, options));
+ });
+
+ var exps = [];
+
+ traverse(mod, function(node) {
+ if (node.type === Syntax.ModuleDeclaration && node !== mod) {
+ return false;
+ } else if (node.type === Syntax.ExportDeclaration) {
+ exps.push(node);
+ }
+ });
+
+ exps.forEach(function(exp) {
+ var exportStartLine = exp.loc.start.line - 1;
+ var exportStartColumn = exp.loc.start.column;
+ var declarationStartColumn = exp.declaration.loc.start.column;
+ lines[exportStartLine] = splice(
+ lines[exportStartLine],
+ exportStartColumn,
+ declarationStartColumn - exportStartColumn, // Delete the export keyword.
+ ''); // Nothing to insert.
+ });
+
+ if (exps.length) {
+ lines[moduleEndLine] = splice(
+ lines[moduleEndLine],
+ moduleEndColumn - 1,
+ 0,
+ style.exports(mod, exps, options));
+ }
+ });
+
+ src = lines.join('\n');
+
+ return src;
+}
+
+var moduleStyles = {
+ amd: {
+ startModule: function(mod, imps, options) {
+ var header = 'define(';
+ if (imps.length) {
+ header += '[\'' + imps.map(function(imp) { return modulePath(importFrom(imp), options); }).join('\', \'') + '\'], ';
+ }
+ header += 'function(';
+ if (imps.length) {
+ header += imps.map(function(imp) { return importFrom(imp); }).join(', ');
+ }
+ header += ') {';
+ return header;
+ },
+ importDeclaration: function(mod, imp, options) {
+ return 'var ' + imp.specifiers.map(function(spec) {
+ var id = spec.type === Syntax.Identifier ? spec.name : spec.id.name;
+ var from = spec.from ? joinPath(spec.from) : id;
+ return id + ' = ' + importFrom(imp) + '.' + from;
+ }).join(', ') + ';';
+ },
+ exports: function(mod, exps, options) {
+ var indent = options.indent;
+ var ret = indent;
+ ret += 'return {';
+ if (exps.length) {
+ ret += '\n';
+ ret += exps.map(function(exp) {
+ var id = exportName(exp);
+ return indent + indent + id + ': ' + id;
+ }).join(',\n');
+ ret += '\n';
+ ret += indent;
+ }
+ ret += '};\n';
+ return ret;
+ },
+ endModule: function(mod, options) {
+ return '});';
+ }
+ },
+
+ node: {
+ startModule: function(mod, imps, options) {
+ return '';
+ },
+ importDeclaration: function(mod, imp, options) {
+ return 'var ' + importFrom(imp) +
+ ' = require(\'' + modulePath(importFrom(imp), options) + '\'), ' +
+ imp.specifiers.map(function(spec) {
+ var id = spec.type === Syntax.Identifier ? spec.name : spec.id.name;
+ var from = spec.from ? joinPath(spec.from) : id;
+ return id + ' = ' + importFrom(imp) + '.' + from;
+ }).join(', ') + ';';
+ },
+ exports: function(mod, exps, options) {
+ var indent = options.indent;
+ var returns = indent + 'module.exports = {';
+ returns += '\n' + exps.map(function(exp) {
+ var id = exportName(exp);
+ return indent + indent + id + ': ' + id;
+ }).join(',\n');
+ returns += '\n' + indent;
+ returns += '};\n';
+ return returns;
+ },
+ endModule: function(mod, options) {
+ return '';
+ }
+ },
+
+ revealing: {
+ startModule: function(mod, imps, options) {
+ return 'var ' + mod.id.name + ' = function() {';
+ },
+ importDeclaration: function(mod, imp, options) {
+ return 'var ' + imp.specifiers.map(function(spec) {
+ var id = spec.type === Syntax.Identifier ? spec.name : spec.id.name;
+ var from = spec.from ? joinPath(spec.from) : id;
+ return id + ' = ' + importFrom(imp) + '.' + from;
+ }).join(', ') + ';';
+ },
+ exports: function(mod, exps, options) {
+ var indent = options.indent;
+ var returns = indent + 'return {';
+ if (exps.length) {
+ returns += '\n' + exps.map(function(exp) {
+ var id = exportName(exp);
+ return indent + indent + id + ': ' + id;
+ }).join(',\n');
+ returns += '\n' + indent;
+ }
+ returns += '};\n';
+ return returns;
+ },
+ endModule: function(mod, options) {
+ return '}();';
+ }
+ }
+};
+
+function traverse(node, visitor) {
+ if (visitor.call(null, node) === false) {
+ return;
+ }
+
+ Object.keys(node).forEach(function(key) {
+ var child = node[key];
+ if (child && typeof child === 'object') {
+ traverse(child, visitor);
+ }
+ });
+}
+
+function modulePath(moduleName, options) {
+ var isRelative = options.relatives && options.relatives.indexOf(moduleName) !== -1;
+ return (isRelative ? './' : '') + moduleName;
+}
+
+function importFrom(imp) {
+ return imp.from.body[0].name;
+}
+
+function exportName(exp) {
+ if (exp.declaration.type === Syntax.VariableDeclaration) {
+ return exp.declaration.declarations[0].id.name;
+ } else if (exp.declaration.type === Syntax.FunctionDeclaration) {
+ return exp.declaration.id.name;
+ }
+}
+
+function joinPath(path) {
+ return path.body.map(function(id) {
+ return id.name;
+ }).join('.');
+}
+
+function splice(str, index, howMany, insert) {
+ var a = str.split('');
+ a.splice(index, howMany, insert);
+ return a.join('');
+}
+
+function detectIndent(mod, lines) {
+ var moduleBodyStartLine = mod.body.loc.start.line - 1;
+ var line = lines[moduleBodyStartLine + 1];
+ if (line) {
+ var m = line.match(/^(\s*)\S/);
+ if (m) {
+ return m[1];
+ }
+ }
+ return '';
+}
+
+return {
+harmonize: harmonize,
+moduleStyles: moduleStyles
+};
+});
361 demo/lib/javascript.js
@@ -0,0 +1,361 @@
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+ var jsonMode = parserConfig.json;
+
+ // Tokenizer
+
+ var keywords = function(){
+ function kw(type) {return {type: type, style: "keyword"};}
+ var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+ var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+ return {
+ "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+ "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
+ "var": kw("var"), "const": kw("var"), "let": kw("var"),
+ "function": kw("function"), "catch": kw("catch"),
+ "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+ "in": operator, "typeof": operator, "instanceof": operator,
+ "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
+ };
+ }();
+
+ var isOperatorChar = /[+\-*&%=<>!?|]/;
+
+ function chain(stream, state, f) {
+ state.tokenize = f;
+ return f(stream, state);
+ }
+
+ function nextUntilUnescaped(stream, end) {
+ var escaped = false, next;
+ while ((next = stream.next()) != null) {
+ if (next == end && !escaped)
+ return false;
+ escaped = !escaped && next == "\\";
+ }
+ return escaped;
+ }
+
+ // Used as scratch variables to communicate multiple values without
+ // consing up tons of objects.
+ var type, content;
+ function ret(tp, style, cont) {
+ type = tp; content = cont;
+ return style;
+ }
+
+ function jsTokenBase(stream, state) {
+ var ch = stream.next();
+ if (ch == '"' || ch == "'")
+ return chain(stream, state, jsTokenString(ch));
+ else if (/[\[\]{}\(\),;\:\.]/.test(ch))
+ return ret(ch);
+ else if (ch == "0" && stream.eat(/x/i)) {
+ stream.eatWhile(/[\da-f]/i);
+ return ret("number", "number");
+ }
+ else if (/\d/.test(ch)) {
+ stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+ return ret("number", "number");
+ }
+ else if (ch == "/") {
+ if (stream.eat("*")) {
+ return chain(stream, state, jsTokenComment);
+ }
+ else if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ret("comment", "comment");
+ }
+ else if (state.reAllowed) {
+ nextUntilUnescaped(stream, "/");
+ stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
+ return ret("regexp", "string-2");
+ }
+ else {
+ stream.eatWhile(isOperatorChar);
+ return ret("operator", null, stream.current());
+ }
+ }
+ else if (ch == "#") {
+ stream.skipToEnd();
+ return ret("error", "error");
+ }
+ else if (isOperatorChar.test(ch)) {
+ stream.eatWhile(isOperatorChar);
+ return ret("operator", null, stream.current());
+ }
+ else {
+ stream.eatWhile(/[\w\$_]/);
+ var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+ return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
+ ret("variable", "variable", word);
+ }
+ }
+
+ function jsTokenString(quote) {
+ return function(stream, state) {
+ if (!nextUntilUnescaped(stream, quote))
+ state.tokenize = jsTokenBase;
+ return ret("string", "string");
+ };
+ }
+
+ function jsTokenComment(stream, state) {
+ var maybeEnd = false, ch;
+ while (ch = stream.next()) {
+ if (ch == "/" && maybeEnd) {
+ state.tokenize = jsTokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ret("comment", "comment");
+ }
+
+ // Parser
+
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+
+ function JSLexical(indented, column, type, align, prev, info) {
+ this.indented = indented;
+ this.column = column;
+ this.type = type;
+ this.prev = prev;
+ this.info = info;
+ if (align != null) this.align = align;
+ }
+
+ function inScope(state, varname) {
+ for (var v = state.localVars; v; v = v.next)
+ if (v.name == varname) return true;
+ }
+
+ function parseJS(state, style, type, content, stream) {
+ var cc = state.cc;
+ // Communicate our context to the combinators.
+ // (Less wasteful than consing up a hundred closures on every call.)
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
+
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = true;
+
+ while(true) {
+ var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+ if (combinator(type, content)) {
+ while(cc.length && cc[cc.length - 1].lex)
+ cc.pop()();
+ if (cx.marked) return cx.marked;
+ if (type == "variable" && inScope(state, content)) return "variable-2";
+ return style;
+ }
+ }
+ }
+
+ // Combinator utils
+
+ var cx = {state: null, column: null, marked: null, cc: null};
+ function pass() {
+ for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+ }
+ function cont() {
+ pass.apply(null, arguments);
+ return true;
+ }
+ function register(varname) {
+ var state = cx.state;
+ if (state.context) {
+ cx.marked = "def";
+ for (var v = state.localVars; v; v = v.next)
+ if (v.name == varname) return;
+ state.localVars = {name: varname, next: state.localVars};
+ }
+ }
+
+ // Combinators
+
+ var defaultVars = {name: "this", next: {name: "arguments"}};
+ function pushcontext() {
+ if (!cx.state.context) cx.state.localVars = defaultVars;
+ cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+ }
+ function popcontext() {
+ cx.state.localVars = cx.state.context.vars;
+ cx.state.context = cx.state.context.prev;
+ }
+ function pushlex(type, info) {
+ var result = function() {
+ var state = cx.state;
+ state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
+ };
+ result.lex = true;
+ return result;
+ }
+ function poplex() {
+ var state = cx.state;
+ if (state.lexical.prev) {
+ if (state.lexical.type == ")")
+ state.indented = state.lexical.indented;
+ state.lexical = state.lexical.prev;
+ }
+ }
+ poplex.lex = true;
+
+ function expect(wanted) {
+ return function expecting(type) {
+ if (type == wanted) return cont();
+ else if (wanted == ";") return pass();
+ else return cont(arguments.callee);
+ };
+ }
+
+ function statement(type) {
+ if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+ if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+ if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+ if (type == "{") return cont(pushlex("}"), block, poplex);
+ if (type == ";") return cont();
+ if (type == "function") return cont(functiondef);
+ if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
+ poplex, statement, poplex);
+ if (type == "variable") return cont(pushlex("stat"), maybelabel);
+ if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+ block, poplex, poplex);
+ if (type == "case") return cont(expression, expect(":"));
+ if (type == "default") return cont(expect(":"));
+ if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+ statement, poplex, popcontext);
+ return pass(pushlex("stat"), expression, expect(";"), poplex);
+ }
+ function expression(type) {
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
+ if (type == "function") return cont(functiondef);
+ if (type == "keyword c") return cont(maybeexpression);
+ if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
+ if (type == "operator") return cont(expression);
+ if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
+ if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
+ return cont();
+ }
+ function maybeexpression(type) {
+ if (type.match(/[;\}\)\],]/)) return pass();
+ return pass(expression);
+ }
+
+ function maybeoperator(type, value) {
+ if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
+ if (type == "operator" || type == ":") return cont(expression);
+ if (type == ";") return;
+ if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
+ if (type == ".") return cont(property, maybeoperator);
+ if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
+ }
+ function maybelabel(type) {
+ if (type == ":") return cont(poplex, statement);
+ return pass(maybeoperator, expect(";"), poplex);
+ }
+ function property(type) {
+ if (type == "variable") {cx.marked = "property"; return cont();}
+ }
+ function objprop(type) {
+ if (type == "variable") cx.marked = "property";
+ if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
+ }
+ function commasep(what, end) {
+ function proceed(type) {
+ if (type == ",") return cont(what, proceed);
+ if (type == end) return cont();
+ return cont(expect(end));
+ }
+ return function commaSeparated(type) {
+ if (type == end) return cont();
+ else return pass(what, proceed);
+ };
+ }
+ function block(type) {
+ if (type == "}") return cont();
+ return pass(statement, block);
+ }
+ function vardef1(type, value) {
+ if (type == "variable"){register(value); return cont(vardef2);}
+ return cont();
+ }
+ function vardef2(type, value) {
+ if (value == "=") return cont(expression, vardef2);
+ if (type == ",") return cont(vardef1);
+ }
+ function forspec1(type) {
+ if (type == "var") return cont(vardef1, forspec2);
+ if (type == ";") return pass(forspec2);
+ if (type == "variable") return cont(formaybein);
+ return pass(forspec2);
+ }
+ function formaybein(type, value) {
+ if (value == "in") return cont(expression);
+ return cont(maybeoperator, forspec2);
+ }
+ function forspec2(type, value) {
+ if (type == ";") return cont(forspec3);
+ if (value == "in") return cont(expression);
+ return cont(expression, expect(";"), forspec3);
+ }
+ function forspec3(type) {
+ if (type != ")") cont(expression);
+ }
+ function functiondef(type, value) {
+ if (type == "variable") {register(value); return cont(functiondef);}
+ if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
+ }
+ function funarg(type, value) {
+ if (type == "variable") {register(value); return cont();}
+ }
+
+ // Interface
+
+ return {
+ startState: function(basecolumn) {
+ return {
+ tokenize: jsTokenBase,
+ reAllowed: true,
+ kwAllowed: true,
+ cc: [],
+ lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+ localVars: parserConfig.localVars,
+ context: parserConfig.localVars && {vars: parserConfig.localVars},
+ indented: 0
+ };
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = false;
+ state.indented = stream.indentation();
+ }
+ if (stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+ if (type == "comment") return style;
+ state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
+ state.kwAllowed = type != '.';
+ return parseJS(state, style, type, content, stream);
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != jsTokenBase) return 0;
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+ if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+ var type = lexical.type, closing = firstChar == type;
+ if (type == "vardef") return lexical.indented + 4;
+ else if (type == "form" && firstChar == "{") return lexical.indented;
+ else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
+ else if (lexical.info == "switch" && !closing)
+ return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+ else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+ else return lexical.indented + (closing ? 0 : indentUnit);
+ },
+
+ electricChars: ":{}"
+ };
+});
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
9,404 demo/lib/jquery-1.7.2.js
9,404 additions, 0 deletions not shown
1,988 demo/lib/require.js
@@ -0,0 +1,1988 @@
+/** vim: et:ts=4:sw=4:sts=4
+ * @license RequireJS 2.0.1 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ * Available via the MIT or new BSD license.
+ * see: http://github.com/jrburke/requirejs for details
+ */
+/*jslint regexp: true, nomen: true */
+/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */
+
+var requirejs, require, define;
+(function (global) {
+ 'use strict';
+
+ var version = '2.0.1',
+ commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
+ cjsRequireRegExp = /require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
+ jsSuffixRegExp = /\.js$/,
+ currDirRegExp = /^\.\//,
+ ostring = Object.prototype.toString,
+ ap = Array.prototype,
+ aps = ap.slice,
+ apsp = ap.splice,
+ isBrowser = !!(typeof window !== 'undefined' && navigator && 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,
+ req, s, head, baseElement, dataMain, src,
+ interactiveScript, currentlyAddingScript, mainScript, subPath;
+
+ 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 (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 (func(ary[i], i, ary)) {
+ break;
+ }
+ }
+ }
+ }
+
+ function hasProp(obj, prop) {
+ return obj.hasOwnProperty(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 (obj.hasOwnProperty(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.
+ * This is not robust in IE for transferring methods that match
+ * Object.prototype names, but the uses of mixin here seem unlikely to
+ * trigger a problem related to that.
+ */
+ function mixin(target, source, force) {
+ if (source) {
+ eachProp(source, function (value, prop) {
+ if (force || !hasProp(target, prop)) {
+ target[prop] = value;
+ }
+ });
+ }
+ }
+
+ //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');
+ }
+
+ //Allow getting a global that 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;
+ }
+
+ function makeContextModuleFunc(func, relMap, enableBuildCallback) {
+ return function () {
+ //A version of a require function that passes a moduleName
+ //value for items that may need to
+ //look up paths relative to the moduleName
+ var args = aps.call(arguments, 0), lastArg;
+ if (enableBuildCallback &&
+ isFunction((lastArg = args[args.length - 1]))) {
+ lastArg.__requireJsBuild = true;
+ }
+ args.push(relMap);
+ return func.apply(null, args);
+ };
+ }
+
+ function addRequireMethods(req, context, relMap) {
+ each([
+ ['toUrl'],
+ ['undef'],
+ ['defined', 'requireDefined'],
+ ['specified', 'requireSpecified']
+ ], function (item) {
+ req[item[0]] = makeContextModuleFunc(context[item[1] || item[0]], relMap);
+ });
+ }
+
+ /**
+ * 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 and 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 config = {
+ waitSeconds: 7,
+ baseUrl: './',
+ paths: {},
+ pkgs: {},
+ shim: {}
+ },
+ registry = {},
+ undefEvents = {},
+ defQueue = [],
+ defined = {},
+ urlMap = {},
+ urlFetched = {},
+ requireCounter = 1,
+ unnormalizedCounter = 1,
+ //Used to track the order in which modules
+ //should be executed, by the order they
+ //load. Important for consistent cycle resolution
+ //behavior.
+ waitAry = [],
+ inCheckLoaded, Module, context, handlers,
+ checkLoadedTimeoutId;
+
+ /**
+ * 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; ary[i]; i+= 1) {
+ part = ary[i];
+ if (part === '.') {
+ ary.splice(i, 1);
+ i -= 1;
+ } else if (part === '..') {
+ if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
+ //End of the line. Keep at least one non-dot
+ //path segment at the front so it can be mapped
+ //correctly to disk. Otherwise, there is likely
+ //no path mapping for a path starting with '..'.
+ //This can still fail, but catches the most reasonable
+ //uses of ..
+ break;
+ } 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 baseParts = baseName && baseName.split('/'),
+ map = config.map,
+ starMap = map && map['*'],
+ pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
+ foundMap;
+
+ //Adjust any relative paths.
+ if (name && name.charAt(0) === '.') {
+ //If have a base name, try to normalize against it,
+ //otherwise, assume it is a top-level require that will
+ //be relative to baseUrl in the end.
+ if (baseName) {
+ if (config.pkgs[baseName]) {
+ //If the baseName is a package name, then just treat it as one
+ //name to concat the name with.
+ baseParts = [baseName];
+ } else {
+ //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.
+ baseParts = baseParts.slice(0, baseParts.length - 1);
+ }
+
+ name = baseParts.concat(name.split('/'));
+ trimDots(name);
+
+ //Some use of packages may use a . path to reference the
+ //'main' module name, so normalize for that.
+ pkgConfig = config.pkgs[(pkgName = name[0])];
+ name = name.join('/');
+ if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
+ name = pkgName;
+ }
+ } else if (name.indexOf('./') === 0) {
+ // No baseName, so this is ID is resolved relative
+ // to baseUrl, pull off the leading dot.
+ name = name.substring(2);
+ }
+ }
+
+ //Apply map config if available.
+ if (applyMap && (baseParts || starMap) && map) {
+ nameParts = name.split('/');
+
+ 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 = map[baseParts.slice(0, j).join('/')];
+
+ //baseName segment has config, find if it has one for
+ //this name.
+ if (mapValue) {
+ mapValue = mapValue[nameSegment];
+ if (mapValue) {
+ //Match, update name to the new value.
+ foundMap = mapValue;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!foundMap && starMap && starMap[nameSegment]) {
+ foundMap = starMap[nameSegment];
+ }
+
+ if (foundMap) {
+ nameParts.splice(0, i, foundMap);
+ name = nameParts.join('/');
+ break;
+ }
+ }
+ }
+
+ return 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 = config.paths[id];
+ if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
+ removeScript(id);
+ //Pop off the first array value, since it failed, and
+ //retry
+ pathConfig.shift();
+ context.undef(id);
+ context.require([id]);
+ return true;
+ }
+ }
+
+ /**
+ * 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 index = name ? name.indexOf('!') : -1,
+ prefix = null,
+ parentName = parentModuleMap ? parentModuleMap.name : null,
+ originalName = name,
+ isDefine = true,
+ normalizedName = '',
+ url, pluginModule, suffix;
+
+ //If no name, then it means it is a require call, generate an
+ //internal name.
+ if (!name) {
+ isDefine = false;
+ name = '_@r' + (requireCounter += 1);
+ }
+
+ if (index !== -1) {
+ prefix = name.substring(0, index);
+ name = name.substring(index + 1, name.length);
+ }
+
+ if (prefix) {
+ prefix = normalize(prefix, parentName, applyMap);
+ pluginModule = 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 {
+ normalizedName = normalize(name, parentName, applyMap);
+ }
+ } else {
+ //A regular module.
+ normalizedName = normalize(name, parentName, applyMap);
+
+ url = urlMap[normalizedName];
+ if (!url) {
+ //Calculate url for the module, if it has a name.
+ //Use name here since nameToUrl also calls normalize,
+ //and for relative names that are outside the baseUrl
+ //this causes havoc. Was thinking of just removing
+ //parentModuleMap to avoid extra normalization, but
+ //normalize() still does a dot removal because of
+ //issue #142, so just pass in name here and redo
+ //the normalization. Paths outside baseUrl are just
+ //messy to support.
+ url = context.nameToUrl(name, null, parentModuleMap);
+
+ //Store the URL mapping for later.
+ urlMap[normalizedName] = url;
+ }
+ }
+ }
+
+ //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 = registry[id];
+
+ if (!mod) {
+ mod = registry[id] = new context.Module(depMap);
+ }
+
+ return mod;
+ }
+
+ function on(depMap, name, fn) {
+ var id = depMap.id,
+ mod = registry[id];
+
+ if (hasProp(defined, id) &&
+ (!mod || mod.defineEmitComplete)) {
+ if (name === 'defined') {
+ fn(defined[id]);
+ }
+ } else {
+ getModule(depMap).on(name, fn);
+ }
+ }
+
+ function onError(err, errback) {
+ var ids = err.requireModules,
+ notified = false;
+
+ if (errback) {
+ errback(err);
+ } else {
+ each(ids, function (id) {
+ var mod = 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 - 1, 0].concat(globalDefQueue));
+ globalDefQueue = [];
+ }
+ }
+
+ /**
+ * Helper function that creates a require function object to give to
+ * modules that ask for it as a dependency. It needs to be specific
+ * per module because of the implication of path mappings that may
+ * need to be relative to the module name.
+ */
+ function makeRequire(mod, enableBuildCallback, altRequire) {
+ var relMap = mod && mod.map,
+ modRequire = makeContextModuleFunc(altRequire || context.require,
+ relMap,
+ enableBuildCallback);
+
+ addRequireMethods(modRequire, context, relMap);
+ modRequire.isBrowser = isBrowser;
+
+ return modRequire;
+ }
+
+ handlers = {
+ 'require': function (mod) {
+ return makeRequire(mod);
+ },
+ 'exports': function (mod) {
+ mod.usingExports = true;
+ if (mod.map.isDefine) {
+ return (mod.exports = defined[mod.map.id] = {});
+ }
+ },
+ 'module': function (mod) {
+ return (mod.module = {
+ id: mod.map.id,
+ uri: mod.map.url,
+ config: function () {
+ return (config.config && config.config[mod.map.id]) || {};
+ },
+ exports: defined[mod.map.id]
+ });
+ }
+ };
+
+ function removeWaiting(id) {
+ //Clean up machinery used for waiting modules.
+ delete registry[id];
+
+ each(waitAry, function (mod, i) {
+ if (mod.map.id === id) {
+ waitAry.splice(i, 1);
+ if (!mod.defined) {
+ context.waitCount -= 1;
+ }
+ return true;
+ }
+ });
+ }
+
+ function findCycle(mod, traced) {
+ var id = mod.map.id,
+ depArray = mod.depMaps,
+ foundModule;
+
+ //Do not bother with unitialized modules or not yet enabled
+ //modules.
+ if (!mod.inited) {
+ return;
+ }
+
+ //Found the cycle.
+ if (traced[id]) {
+ return mod;
+ }
+
+ traced[id] = true;
+
+ //Trace through the dependencies.
+ each(depArray, function (depMap) {
+ var depId = depMap.id,
+ depMod = registry[depId];
+
+ if (!depMod) {
+ return;
+ }
+
+ if (!depMod.inited || !depMod.enabled) {
+ //Dependency is not inited, so this cannot
+ //be used to determine a cycle.
+ foundModule = null;
+ delete traced[id];
+ return true;
+ }
+
+ return (foundModule = findCycle(depMod, traced));
+ });
+
+ return foundModule;
+ }
+
+ function forceExec(mod, traced, uninited) {
+ var id = mod.map.id,
+ depArray = mod.depMaps;
+
+ if (!mod.inited || !mod.map.isDefine) {
+ return;
+ }
+
+ if (traced[id]) {
+ return defined[id];
+ }
+
+ traced[id] = mod;
+
+ each(depArray, function(depMap) {
+ var depId = depMap.id,
+ depMod = registry[depId],
+ value;
+
+ if (handlers[depId]) {
+ return;
+ }
+
+ if (depMod) {
+ if (!depMod.inited || !depMod.enabled) {
+ //Dependency is not inited,
+ //so this module cannot be
+ //given a forced value yet.
+ uninited[id] = true;
+ return;
+ }
+
+ //Get the value for the current dependency
+ value = forceExec(depMod, traced, uninited);
+
+ //Even with forcing it may not be done,
+ //in particular if the module is waiting
+ //on a plugin resource.
+ if (!uninited[depId]) {
+ mod.defineDepById(depId, value);
+ }
+ }
+ });
+
+ mod.check(true);
+
+ return defined[id];
+ }
+
+ function modCheck(mod) {
+ mod.check();
+ }
+
+ function checkLoaded() {
+ var 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 = [],
+ stillLoading = false,
+ needCycleCheck = true,
+ map, modId, err, usingPathFallback;
+
+ //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(registry, function (mod) {
+ map = mod.map;
+ modId = map.id;
+
+ //Skip things that are not enabled or in error state.
+ if (!mod.enabled) {
+ return;
+ }
+
+ 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(waitAry, function (mod) {
+ if (mod.defined) {
+ return;
+ }
+
+ var cycleMod = findCycle(mod, {}),
+ traced = {};
+
+ if (cycleMod) {
+ forceExec(cycleMod, traced, {});
+
+ //traced modules may have been
+ //removed from the registry, but
+ //their listeners still need to
+ //be called.
+ eachProp(traced, modCheck);
+ }
+ });
+
+ //Now that dependencies have
+ //been satisfied, trigger the
+ //completion check that then
+ //notifies listeners.
+ eachProp(registry, modCheck);
+ }
+
+ //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 = undefEvents[map.id] || {};
+ this.map = map;
+ this.shim = 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);
+ });
+ }
+
+ each(depMaps, bind(this, function (depMap, i) {
+ if (typeof depMap === 'string') {
+ depMap = makeModuleMap(depMap,
+ (this.map.isDefine ? this.map : this.map.parentMap),
+ false,
+ true);
+ this.depMaps.push(depMap);
+ }
+
+ var handler = 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 (errback) {
+ on(depMap, 'error', 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();
+ }
+ },
+
+ defineDepById: function (id, depExports) {
+ var i;
+
+ //Find the index for this dependency.
+ each(this.depMaps, function (map, index) {
+ if (map.id === id) {
+ i = index;
+ return true;
+ }
+ });
+
+ return this.defineDep(i, depExports);
+ },
+
+ 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 (map.prefix) {
+ this.callPlugin();
+ } else if (this.shim) {
+ makeRequire(this, true)(this.shim.deps || [], bind(this, function () {
+ this.load();
+ }));
+ } else {
+ //Regular dependency.
+ this.load();
+ }
+ },
+
+ load: function() {
+ var url = this.map.url;
+
+ //Regular dependency.
+ if (!urlFetched[url]) {
+ urlFetched[url] = true;
+ context.load(this.map.id, url);
+ }
+ },
+
+ /**
+ * Checks is the module is ready to define itself, and if so,
+ * define it. If the silent argument is true, then it will just
+ * define, but not notify listeners, and not ask for a context-wide
+ * check of all loaded modules. That is useful for cycle breaking.
+ */
+ check: function (silent) {
+ if (!this.enabled) {
+ return;
+ }
+
+ var id = this.map.id,
+ depExports = this.depExports,
+ exports = this.exports,
+ factory = this.factory,
+ err, cjsModule;
+
+ 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.
+ if (this.events.error) {
+ try {
+ exports = context.execCb(id, factory, depExports, exports);
+ } catch (e) {
+ err = e;
+ }
+ } else {
+ exports = context.execCb(id, factory, depExports, exports);
+ }
+
+ if (this.map.isDefine) {
+ //If setting exports via 'module' is in play,
+ //favor that over return value and exports. After that,
+ //favor a non-undefined return value over exports use.
+ cjsModule = this.module;
+ if (cjsModule &&
+ cjsModule.exports !== undefined &&
+ //Make sure it is not already the exports value
+ cjsModule.exports !== this.exports) {
+ exports = cjsModule.exports;
+ } else if (exports === undefined && this.usingExports) {
+ //exports already set the defined value.
+ exports = this.exports;
+ }
+ }
+
+ if (err) {
+ err.requireMap = this.map;
+ err.requireModules = [this.map.id];
+ err.requireType = 'define';
+ 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
+ delete registry[id];
+
+ this.defined = true;
+ context.waitCount -= 1;
+ if (context.waitCount === 0) {
+ //Clear the wait array used for cycles.
+ waitAry = [];
+ }
+ }
+
+ //Finished the define stage. Allow calling check again
+ //to allow define notifications below in the case of a
+ //cycle.
+ this.defining = false;
+
+ if (!silent) {
+ 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,
+ pluginMap = makeModuleMap(map.prefix, null, false, true);
+
+ on(pluginMap, 'defined', bind(this, function (plugin) {
+ var name = this.map.name,
+ parentName = this.map.parentMap ? this.map.parentMap.name : null,
+ load, normalizedMap, normalizedMod;
+
+ //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);
+ }) || '';
+ }
+
+ normalizedMap = makeModuleMap(map.prefix + '!' + name);
+ on(normalizedMap,
+ 'defined', bind(this, function (value) {
+ this.init([], function () { return value; }, null, {
+ enabled: true,
+ ignore: true
+ });
+ }));
+ normalizedMod = registry[normalizedMap.id];
+ if (normalizedMod) {
+ if (this.events.error) {
+ normalizedMod.on('error', bind(this, function (err) {
+ this.emit('error', err);
+ }));
+ }
+ normalizedMod.enable();
+ }
+
+ 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) {
+ removeWaiting(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 = function (moduleName, text) {
+ /*jslint evil: true */
+ var hasInteractive = useInteractive;
+
+ //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;
+ }
+
+ req.exec(text);
+
+ if (hasInteractive) {
+ useInteractive = true;
+ }
+
+ //Support anonymous modules.
+ context.completeLoad(moduleName);
+ };
+
+ //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, makeRequire(map.parentMap, true, function (deps, cb) {
+ return context.require(deps, cb);
+ }), load, config);
+ }));
+
+ context.enable(pluginMap, this);
+ this.pluginMaps[pluginMap.id] = pluginMap;
+ },
+
+ enable: function () {
+ this.enabled = true;
+
+ if (!this.waitPushed) {
+ waitAry.push(this);
+ context.waitCount += 1;
+ this.waitPushed = true;
+ }
+
+ //Enable each dependency
+ each(this.depMaps, bind(this, function (map) {
+ var id = map.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 (!handlers[id] && mod && !mod.enabled) {
+ context.enable(map, this);
+ }
+ }));
+
+ //Enable each plugin that is used in
+ //a dependency
+ eachProp(this.pluginMaps, bind(this, function (pluginMap) {
+ var mod = registry[pluginMap.id];
+ if (mod && !mod.enabled) {
+ context.enable(pluginMap, this);
+ }
+ }));
+
+ 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/waitAry.
+ delete this.events[name];
+ }
+ }
+ };
+
+ function callGetModule(args) {
+ 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) {