diff --git a/Gruntfile.js b/Gruntfile.js
index 2a32708c9..ada6baa4b 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -59,7 +59,8 @@ module.exports = function( grunt ) {
},
files: {
"qunit": "qunit/qunit",
- "require.js": "requirejs/require.js"
+ "require.js": "requirejs/require.js",
+ "sinon/fake_timers.js": "sinon/lib/sinon/util/fake_timers.js"
}
}
},
diff --git a/test/index.html b/test/index.html
index 063ba2bd7..ce19c4d5e 100644
--- a/test/index.html
+++ b/test/index.html
@@ -16,8 +16,7 @@
-
-
+
diff --git a/test/libs/require.js b/test/libs/require.js
index 2ce09b5e3..e7bc14d36 100644
--- a/test/libs/require.js
+++ b/test/libs/require.js
@@ -1,5 +1,5 @@
/** vim: et:ts=4:sw=4:sts=4
- * @license RequireJS 2.1.9 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
+ * @license RequireJS 2.1.10 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
@@ -12,7 +12,7 @@ var requirejs, require, define;
(function (global) {
var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath,
- version = '2.1.9',
+ version = '2.1.10',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
@@ -108,7 +108,10 @@ var requirejs, require, define;
if (source) {
eachProp(source, function (value, prop) {
if (force || !hasProp(target, prop)) {
- if (deepStringMixin && typeof value !== 'string') {
+ if (deepStringMixin && typeof value === 'object' && value &&
+ !isArray(value) && !isFunction(value) &&
+ !(value instanceof RegExp)) {
+
if (!target[prop]) {
target[prop] = {};
}
@@ -201,6 +204,7 @@ var requirejs, require, define;
waitSeconds: 7,
baseUrl: './',
paths: {},
+ bundles: {},
pkgs: {},
shim: {},
config: {}
@@ -214,6 +218,7 @@ var requirejs, require, define;
defQueue = [],
defined = {},
urlFetched = {},
+ bundlesMap = {},
requireCounter = 1,
unnormalizedCounter = 1;
@@ -227,8 +232,8 @@ var requirejs, require, define;
* @param {Array} ary the array of path segments.
*/
function trimDots(ary) {
- var i, part;
- for (i = 0; ary[i]; i += 1) {
+ var i, part, length = ary.length;
+ for (i = 0; i < length; i++) {
part = ary[i];
if (part === '.') {
ary.splice(i, 1);
@@ -261,7 +266,7 @@ var requirejs, require, define;
* @returns {String} normalized name
*/
function normalize(name, baseName, applyMap) {
- var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
+ var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
foundMap, foundI, foundStarMap, starI,
baseParts = baseName && baseName.split('/'),
normalizedBaseParts = baseParts,
@@ -274,29 +279,26 @@ var requirejs, require, define;
//otherwise, assume it is a top-level require that will
//be relative to baseUrl in the end.
if (baseName) {
- if (getOwn(config.pkgs, baseName)) {
- //If the baseName is a package name, then just treat it as one
- //name to concat the name with.
- normalizedBaseParts = 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.
- normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
+ //Convert baseName to array, and lop off the last part,
+ //so that . matches that 'directory' and not name of the baseName's
+ //module. For instance, baseName of 'one/two/three', maps to
+ //'one/two/three.js', but we want the directory, 'one/two' for
+ //this normalization.
+ normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
+ name = name.split('/');
+ lastIndex = name.length - 1;
+
+ // If wanting node ID compatibility, strip .js from end
+ // of IDs. Have to do this here, and not in nameToUrl
+ // because node allows either .js or non .js to map
+ // to same file.
+ if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
+ name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
- name = normalizedBaseParts.concat(name.split('/'));
+ name = normalizedBaseParts.concat(name);
trimDots(name);
-
- //Some use of packages may use a . path to reference the
- //'main' module name, so normalize for that.
- pkgConfig = getOwn(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.
@@ -308,7 +310,7 @@ var requirejs, require, define;
if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/');
- for (i = nameParts.length; i > 0; i -= 1) {
+ outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
nameSegment = nameParts.slice(0, i).join('/');
if (baseParts) {
@@ -325,16 +327,12 @@ var requirejs, require, define;
//Match, update name to the new value.
foundMap = mapValue;
foundI = i;
- break;
+ break outerLoop;
}
}
}
}
- if (foundMap) {
- break;
- }
-
//Check for a star map match, but just hold on to it,
//if there is a shorter segment match later in a matching
//config, then favor over this star map.
@@ -355,7 +353,11 @@ var requirejs, require, define;
}
}
- return name;
+ // If the name points to a package's name, use
+ // the package main instead.
+ pkgMain = getOwn(config.pkgs, name);
+
+ return pkgMain ? pkgMain : name;
}
function removeScript(name) {
@@ -548,7 +550,7 @@ var requirejs, require, define;
//local var ref to defQueue, so cannot just reassign the one
//on context.
apsp.apply(defQueue,
- [defQueue.length - 1, 0].concat(globalDefQueue));
+ [defQueue.length, 0].concat(globalDefQueue));
globalDefQueue = [];
}
}
@@ -579,15 +581,9 @@ var requirejs, require, define;
id: mod.map.id,
uri: mod.map.url,
config: function () {
- var c,
- pkg = getOwn(config.pkgs, mod.map.id);
- // For packages, only support config targeted
- // at the main module.
- c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) :
- getOwn(config.config, mod.map.id);
- return c || {};
+ return getOwn(config.config, mod.map.id) || {};
},
- exports: defined[mod.map.id]
+ exports: handlers.exports(mod)
});
}
}
@@ -628,7 +624,7 @@ var requirejs, require, define;
}
function checkLoaded() {
- var map, modId, err, usingPathFallback,
+ var err, usingPathFallback,
waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
@@ -646,8 +642,8 @@ var requirejs, require, define;
//Figure out the state of all the modules.
eachProp(enabledRegistry, function (mod) {
- map = mod.map;
- modId = map.id;
+ var map = mod.map,
+ modId = map.id;
//Skip things that are not enabled or in error state.
if (!mod.enabled) {
@@ -870,17 +866,14 @@ var requirejs, require, define;
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.
+ // Favor return value over exports. If node/cjs in play,
+ // then will not have a return value anyway. Favor
+ // module.exports assignment over exports object.
+ if (this.map.isDefine && exports === undefined) {
cjsModule = this.module;
- if (cjsModule &&
- cjsModule.exports !== undefined &&
- //Make sure it is not already the exports value
- cjsModule.exports !== this.exports) {
+ if (cjsModule) {
exports = cjsModule.exports;
- } else if (exports === undefined && this.usingExports) {
+ } else if (this.usingExports) {
//exports already set the defined value.
exports = this.exports;
}
@@ -940,6 +933,7 @@ var requirejs, require, define;
on(pluginMap, 'defined', bind(this, function (plugin) {
var load, normalizedMap, normalizedMod,
+ bundleId = getOwn(bundlesMap, this.map.id),
name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, {
@@ -985,6 +979,14 @@ var requirejs, require, define;
return;
}
+ //If a paths config, then just load that file instead to
+ //resolve the plugin, as it is built into that paths layer.
+ if (bundleId) {
+ this.map.url = context.nameToUrl(bundleId);
+ this.load();
+ return;
+ }
+
load = bind(this, function (value) {
this.init([], function () { return value; }, null, {
enabled: true
@@ -1249,31 +1251,38 @@ var requirejs, require, define;
}
}
- //Save off the paths and packages since they require special processing,
+ //Save off the paths since they require special processing,
//they are additive.
- var pkgs = config.pkgs,
- shim = config.shim,
+ var shim = config.shim,
objs = {
paths: true,
+ bundles: true,
config: true,
map: true
};
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
- if (prop === 'map') {
- if (!config.map) {
- config.map = {};
- }
- mixin(config[prop], value, true, true);
- } else {
- mixin(config[prop], value, true);
+ if (!config[prop]) {
+ config[prop] = {};
}
+ mixin(config[prop], value, true, true);
} else {
config[prop] = value;
}
});
+ //Reverse map the bundles
+ if (cfg.bundles) {
+ eachProp(cfg.bundles, function (value, prop) {
+ each(value, function (v) {
+ if (v !== prop) {
+ bundlesMap[v] = prop;
+ }
+ });
+ });
+ }
+
//Merge shim
if (cfg.shim) {
eachProp(cfg.shim, function (value, id) {
@@ -1294,29 +1303,25 @@ var requirejs, require, define;
//Adjust packages if necessary.
if (cfg.packages) {
each(cfg.packages, function (pkgObj) {
- var location;
+ var location, name;
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
+
+ name = pkgObj.name;
location = pkgObj.location;
+ if (location) {
+ config.paths[name] = pkgObj.location;
+ }
- //Create a brand new object on pkgs, since currentPackages can
- //be passed in again, and config.pkgs is the internal transformed
- //state for all package configs.
- pkgs[pkgObj.name] = {
- name: pkgObj.name,
- location: location || pkgObj.name,
- //Remove leading dot in main, so main paths are normalized,
- //and remove any trailing .js, since different package
- //envs have different conventions: some use a module name,
- //some use a file name.
- main: (pkgObj.main || 'main')
- .replace(currDirRegExp, '')
- .replace(jsSuffixRegExp, '')
- };
+ //Save pointer to main module ID for pkg name.
+ //Remove leading dot in main, so main paths are normalized,
+ //and remove any trailing .js, since different package
+ //envs have different conventions: some use a module name,
+ //some use a file name.
+ config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
+ .replace(currDirRegExp, '')
+ .replace(jsSuffixRegExp, '');
});
-
- //Done with modifications, assing packages back to context config
- config.pkgs = pkgs;
}
//If there are any "waiting to execute" modules in the registry,
@@ -1469,6 +1474,15 @@ var requirejs, require, define;
delete urlFetched[map.url];
delete undefEvents[id];
+ //Clean queued defines too. Go backwards
+ //in array so that the splices do not
+ //mess up the iteration.
+ eachReverse(defQueue, function(args, i) {
+ if(args[0] === id) {
+ defQueue.splice(i, 1);
+ }
+ });
+
if (mod) {
//Hold on to listeners in case the
//module will be attempted to be reloaded
@@ -1562,8 +1576,19 @@ var requirejs, require, define;
* internal API, not a public one. Use toUrl for the public API.
*/
nameToUrl: function (moduleName, ext, skipExt) {
- var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
- parentPath;
+ var paths, syms, i, parentModule, url,
+ parentPath, bundleId,
+ pkgMain = getOwn(config.pkgs, moduleName);
+
+ if (pkgMain) {
+ moduleName = pkgMain;
+ }
+
+ bundleId = getOwn(bundlesMap, moduleName);
+
+ if (bundleId) {
+ return context.nameToUrl(bundleId, ext, skipExt);
+ }
//If a colon is in the URL, it indicates a protocol is used and it is just
//an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
@@ -1577,7 +1602,6 @@ var requirejs, require, define;
} else {
//A module that needs to be converted to a path.
paths = config.paths;
- pkgs = config.pkgs;
syms = moduleName.split('/');
//For each module name segment, see if there is a path
@@ -1585,7 +1609,7 @@ var requirejs, require, define;
//and work up from it.
for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/');
- pkg = getOwn(pkgs, parentModule);
+
parentPath = getOwn(paths, parentModule);
if (parentPath) {
//If an array, it means there are a few choices,
@@ -1595,16 +1619,6 @@ var requirejs, require, define;
}
syms.splice(0, i, parentPath);
break;
- } else if (pkg) {
- //If module name is just the package name, then looking
- //for the main module.
- if (moduleName === pkg.name) {
- pkgPath = pkg.location + '/' + pkg.main;
- } else {
- pkgPath = pkg.location;
- }
- syms.splice(0, i, pkgPath);
- break;
}
}
diff --git a/test/libs/sinon/fake_timers.js b/test/libs/sinon/fake_timers.js
new file mode 100644
index 000000000..ed7352ad9
--- /dev/null
+++ b/test/libs/sinon/fake_timers.js
@@ -0,0 +1,351 @@
+/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
+/*global module, require, window*/
+/**
+ * Fake timer API
+ * setTimeout
+ * setInterval
+ * clearTimeout
+ * clearInterval
+ * tick
+ * reset
+ * Date
+ *
+ * Inspired by jsUnitMockTimeOut from JsUnit
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+"use strict";
+
+if (typeof sinon == "undefined") {
+ var sinon = {};
+}
+
+(function (global) {
+ var id = 1;
+
+ function addTimer(args, recurring) {
+ if (args.length === 0) {
+ throw new Error("Function requires at least 1 parameter");
+ }
+
+ var toId = id++;
+ var delay = args[1] || 0;
+
+ if (!this.timeouts) {
+ this.timeouts = {};
+ }
+
+ this.timeouts[toId] = {
+ id: toId,
+ func: args[0],
+ callAt: this.now + delay,
+ invokeArgs: Array.prototype.slice.call(args, 2)
+ };
+
+ if (recurring === true) {
+ this.timeouts[toId].interval = delay;
+ }
+
+ return toId;
+ }
+
+ function parseTime(str) {
+ if (!str) {
+ return 0;
+ }
+
+ var strings = str.split(":");
+ var l = strings.length, i = l;
+ var ms = 0, parsed;
+
+ if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+ throw new Error("tick only understands numbers and 'h:m:s'");
+ }
+
+ while (i--) {
+ parsed = parseInt(strings[i], 10);
+
+ if (parsed >= 60) {
+ throw new Error("Invalid time " + str);
+ }
+
+ ms += parsed * Math.pow(60, (l - i - 1));
+ }
+
+ return ms * 1000;
+ }
+
+ function createObject(object) {
+ var newObject;
+
+ if (Object.create) {
+ newObject = Object.create(object);
+ } else {
+ var F = function () {};
+ F.prototype = object;
+ newObject = new F();
+ }
+
+ newObject.Date.clock = newObject;
+ return newObject;
+ }
+
+ sinon.clock = {
+ now: 0,
+
+ create: function create(now) {
+ var clock = createObject(this);
+
+ if (typeof now == "number") {
+ clock.now = now;
+ }
+
+ if (!!now && typeof now == "object") {
+ throw new TypeError("now should be milliseconds since UNIX epoch");
+ }
+
+ return clock;
+ },
+
+ setTimeout: function setTimeout(callback, timeout) {
+ return addTimer.call(this, arguments, false);
+ },
+
+ clearTimeout: function clearTimeout(timerId) {
+ if (!this.timeouts) {
+ this.timeouts = [];
+ }
+
+ if (timerId in this.timeouts) {
+ delete this.timeouts[timerId];
+ }
+ },
+
+ setInterval: function setInterval(callback, timeout) {
+ return addTimer.call(this, arguments, true);
+ },
+
+ clearInterval: function clearInterval(timerId) {
+ this.clearTimeout(timerId);
+ },
+
+ tick: function tick(ms) {
+ ms = typeof ms == "number" ? ms : parseTime(ms);
+ var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
+ var timer = this.firstTimerInRange(tickFrom, tickTo);
+
+ var firstException;
+ while (timer && tickFrom <= tickTo) {
+ if (this.timeouts[timer.id]) {
+ tickFrom = this.now = timer.callAt;
+ try {
+ this.callTimer(timer);
+ } catch (e) {
+ firstException = firstException || e;
+ }
+ }
+
+ timer = this.firstTimerInRange(previous, tickTo);
+ previous = tickFrom;
+ }
+
+ this.now = tickTo;
+
+ if (firstException) {
+ throw firstException;
+ }
+
+ return this.now;
+ },
+
+ firstTimerInRange: function (from, to) {
+ var timer, smallest, originalTimer;
+
+ for (var id in this.timeouts) {
+ if (this.timeouts.hasOwnProperty(id)) {
+ if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
+ continue;
+ }
+
+ if (!smallest || this.timeouts[id].callAt < smallest) {
+ originalTimer = this.timeouts[id];
+ smallest = this.timeouts[id].callAt;
+
+ timer = {
+ func: this.timeouts[id].func,
+ callAt: this.timeouts[id].callAt,
+ interval: this.timeouts[id].interval,
+ id: this.timeouts[id].id,
+ invokeArgs: this.timeouts[id].invokeArgs
+ };
+ }
+ }
+ }
+
+ return timer || null;
+ },
+
+ callTimer: function (timer) {
+ if (typeof timer.interval == "number") {
+ this.timeouts[timer.id].callAt += timer.interval;
+ } else {
+ delete this.timeouts[timer.id];
+ }
+
+ try {
+ if (typeof timer.func == "function") {
+ timer.func.apply(null, timer.invokeArgs);
+ } else {
+ eval(timer.func);
+ }
+ } catch (e) {
+ var exception = e;
+ }
+
+ if (!this.timeouts[timer.id]) {
+ if (exception) {
+ throw exception;
+ }
+ return;
+ }
+
+ if (exception) {
+ throw exception;
+ }
+ },
+
+ reset: function reset() {
+ this.timeouts = {};
+ },
+
+ Date: (function () {
+ var NativeDate = Date;
+
+ function ClockDate(year, month, date, hour, minute, second, ms) {
+ // Defensive and verbose to avoid potential harm in passing
+ // explicit undefined when user does not pass argument
+ switch (arguments.length) {
+ case 0:
+ return new NativeDate(ClockDate.clock.now);
+ case 1:
+ return new NativeDate(year);
+ case 2:
+ return new NativeDate(year, month);
+ case 3:
+ return new NativeDate(year, month, date);
+ case 4:
+ return new NativeDate(year, month, date, hour);
+ case 5:
+ return new NativeDate(year, month, date, hour, minute);
+ case 6:
+ return new NativeDate(year, month, date, hour, minute, second);
+ default:
+ return new NativeDate(year, month, date, hour, minute, second, ms);
+ }
+ }
+
+ return mirrorDateProperties(ClockDate, NativeDate);
+ }())
+ };
+
+ function mirrorDateProperties(target, source) {
+ if (source.now) {
+ target.now = function now() {
+ return target.clock.now;
+ };
+ } else {
+ delete target.now;
+ }
+
+ if (source.toSource) {
+ target.toSource = function toSource() {
+ return source.toSource();
+ };
+ } else {
+ delete target.toSource;
+ }
+
+ target.toString = function toString() {
+ return source.toString();
+ };
+
+ target.prototype = source.prototype;
+ target.parse = source.parse;
+ target.UTC = source.UTC;
+ target.prototype.toUTCString = source.prototype.toUTCString;
+ return target;
+ }
+
+ var methods = ["Date", "setTimeout", "setInterval",
+ "clearTimeout", "clearInterval"];
+
+ function restore() {
+ var method;
+
+ for (var i = 0, l = this.methods.length; i < l; i++) {
+ method = this.methods[i];
+ if (global[method].hadOwnProperty) {
+ global[method] = this["_" + method];
+ } else {
+ delete global[method];
+ }
+ }
+
+ // Prevent multiple executions which will completely remove these props
+ this.methods = [];
+ }
+
+ function stubGlobal(method, clock) {
+ clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
+ clock["_" + method] = global[method];
+
+ if (method == "Date") {
+ var date = mirrorDateProperties(clock[method], global[method]);
+ global[method] = date;
+ } else {
+ global[method] = function () {
+ return clock[method].apply(clock, arguments);
+ };
+
+ for (var prop in clock[method]) {
+ if (clock[method].hasOwnProperty(prop)) {
+ global[method][prop] = clock[method][prop];
+ }
+ }
+ }
+
+ global[method].clock = clock;
+ }
+
+ sinon.useFakeTimers = function useFakeTimers(now) {
+ var clock = sinon.clock.create(now);
+ clock.restore = restore;
+ clock.methods = Array.prototype.slice.call(arguments,
+ typeof now == "number" ? 1 : 0);
+
+ if (clock.methods.length === 0) {
+ clock.methods = methods;
+ }
+
+ for (var i = 0, l = clock.methods.length; i < l; i++) {
+ stubGlobal(clock.methods[i], clock);
+ }
+
+ return clock;
+ };
+}(typeof global != "undefined" && typeof global !== "function" ? global : this));
+
+sinon.timers = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setInterval: setInterval,
+ clearInterval: clearInterval,
+ Date: Date
+};
+
+if (typeof module == "object" && typeof require == "function") {
+ module.exports = sinon;
+}