todomvc
Version:
> Helping you select an MV\* framework
1,884 lines (1,622 loc) • 158 kB
JavaScript
/**
* troopjs-bundle - 2.0.0-93-g5b9d048
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:mikael@karon.se
*/
/**
* TroopJS utils/unique
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:mikael@karon.se
*/
/*global define:false */
define('troopjs-utils/unique',[],function UniqueModule() {
/*jshint strict:false */
var LENGTH = "length";
/**
* Reduces array to only contain unique values (evals left-right)
* @returns {Number} New length of array
*/
return function unique(comparator) {
var arg;
var args = this;
var i;
var j;
var k;
var iMax = args[LENGTH];
// Did we provide a comparator?
if (comparator) {
comparator_outer: for (i = k = 0; i < iMax; i++) {
arg = args[i];
for (j = 0; j < i; j++) {
if (comparator.call(args, arg, [j]) === true) {
continue comparator_outer;
}
}
args[k++] = arg;
}
}
// Otherwise use strict equality
else {
outer: for (i = k = 0; i < iMax; i++) {
arg = args[i];
for (j = 0; j < i; j++) {
if (arg === args[j]) {
continue outer;
}
}
args[k++] = arg;
}
}
// Assign and return new length
return args[LENGTH] = k;
};
});
/**
* poly common functions
*
* (c) copyright 2011-2013 Brian Cavalier and John Hann
*
* This module is part of the cujo.js family of libraries (http://cujojs.com/).
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
*/
define('poly/lib/_base',['require','exports','module'],function (require, exports, module) {
var toString;
toString = ({}).toString;
exports.isFunction = function (o) {
return typeof o == 'function';
};
exports.isString = function (o) {
return toString.call(o) == '[object String]';
};
exports.toString = function (o) {
return toString.apply(o);
};
exports.createCaster = function (caster, name) {
return function cast (o) {
if (o == null) throw new TypeError(name + ' method called on null or undefined');
return caster(o);
}
}
});
/**
* Object polyfill / shims
*
* (c) copyright 2011-2013 Brian Cavalier and John Hann
*
* This module is part of the cujo.js family of libraries (http://cujojs.com/).
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* The goal of these shims is to emulate a JavaScript 1.8.5+ environments as
* much as possible. While it's not feasible to fully shim Object,
* we can try to maximize code compatibility with older js engines.
*
* Note: these shims cannot fix `for (var p in obj) {}`. Instead, use this:
* Object.keys(obj).forEach(function (p) {}); // shimmed Array
*
* Also, these shims can't prevent writing to object properties.
*
* If you want your code to fail loudly if a shim can't mimic ES5 closely
* then set the AMD loader config option `failIfShimmed`. Possible values
* for `failIfShimmed` include:
*
* true: fail on every shimmed Object function
* false: fail never
* function: fail for shims whose name returns true from function (name) {}
*
* By default, no shims fail.
*
* The following functions are safely shimmed:
* create (unless the second parameter is specified since that calls defineProperties)
* keys
* getOwnPropertyNames
* getPrototypeOf
* isExtensible
*
* In order to play nicely with several third-party libs (including Promises/A
* implementations), the following functions don't fail by default even though
* they can't be correctly shimmed:
* freeze
* seal
* isFrozen
* isSealed
*
* The poly/strict module will set failIfShimmed to fail for some shims.
* See the documentation for more information.
*
* IE missing enum properties fixes copied from kangax:
* https://github.com/kangax/protolicious/blob/master/experimental/object.for_in.js
*
* TODO: fix Object#propertyIsEnumerable for IE's non-enumerable props to match Object.keys()
*/
define('poly/object',['./lib/_base'], function (base) {
"use strict";
var refObj,
refProto,
has__proto__,
hasNonEnumerableProps,
getPrototypeOf,
keys,
featureMap,
shims,
secrets,
protoSecretProp,
hasOwnProp = 'hasOwnProperty',
undef;
refObj = Object;
refProto = refObj.prototype;
has__proto__ = typeof {}.__proto__ == 'object';
hasNonEnumerableProps = (function () {
for (var p in { valueOf: 1 }) return false;
return true;
}());
// TODO: this still doesn't work for IE6-8 since object.constructor && object.constructor.prototype are clobbered/replaced when using `new` on a constructor that has a prototype. srsly.
// devs will have to do the following if they want this to work in IE6-8:
// Ctor.prototype.constructor = Ctor
getPrototypeOf = has__proto__
? function (object) { assertIsObject(object); return object.__proto__; }
: function (object) {
assertIsObject(object);
return protoSecretProp && object[protoSecretProp](secrets)
? object[protoSecretProp](secrets.proto)
: object.constructor ? object.constructor.prototype : refProto;
};
keys = !hasNonEnumerableProps
? _keys
: (function (masked) {
return function (object) {
var result = _keys(object), i = 0, m;
while (m = masked[i++]) {
if (hasProp(object, m)) result.push(m);
}
return result;
}
}([ 'constructor', hasOwnProp, 'isPrototypeOf', 'propertyIsEnumerable', 'toString', 'toLocaleString', 'valueOf' ]));
featureMap = {
'object-create': 'create',
'object-freeze': 'freeze',
'object-isfrozen': 'isFrozen',
'object-seal': 'seal',
'object-issealed': 'isSealed',
'object-getprototypeof': 'getPrototypeOf',
'object-keys': 'keys',
'object-getownpropertynames': 'getOwnPropertyNames',
'object-defineproperty': function hasDefineProperty(object) {
try {
return 'defineProperty' in object && "sentinel" in Object.defineProperty({}, "sentinel", {});
}
catch (e) {
}
},
'object-defineproperties': 'defineProperties',
'object-isextensible': 'isExtensible',
'object-preventextensions': 'preventExtensions',
'object-getownpropertydescriptor': function hasGetOwnPropertyDescriptorObject(object) {
try {
return 'getOwnPropertyDescriptor' in object && Object.getOwnPropertyDescriptor({"sentinel":0}).value === 0;
}
catch (e) {
}
}
};
shims = {};
secrets = {
proto: {}
};
protoSecretProp = !has('object-getprototypeof') && !has__proto__ && hasNonEnumerableProps && hasOwnProp;
function createFlameThrower (feature) {
return function () {
throw new Error('poly/object: ' + feature + ' is not safely supported.');
}
}
function has (feature) {
var prop = featureMap[feature];
return base.isFunction(prop) ? prop(refObj) : prop in refObj;
}
function PolyBase () {}
// for better compression
function hasProp (object, name) {
return object.hasOwnProperty(name);
}
function _keys (object) {
var result = [];
for (var p in object) {
if (hasProp(object, p)) {
result.push(p);
}
}
return result;
}
// we might create an owned property to hold the secrets, but make it look
// like it's not an owned property. (affects getOwnPropertyNames, too)
if (protoSecretProp) (function (_hop) {
refProto[hasOwnProp] = function (name) {
if (name == protoSecretProp) return false;
return _hop.call(this, name);
};
}(refProto[hasOwnProp]));
if (!has('object-create')) {
Object.create = shims.create = function create (proto, props) {
var obj;
if (typeof proto != 'object') throw new TypeError('prototype is not of type Object or Null.');
PolyBase.prototype = proto;
obj = new PolyBase();
PolyBase.prototype = null;
// provide a mechanism for retrieving the prototype in IE 6-8
if (protoSecretProp) {
var orig = obj[protoSecretProp];
obj[protoSecretProp] = function (name) {
if (name == secrets) return true; // yes, we're using secrets
if (name == secrets.proto) return proto;
return orig.call(this, name);
};
}
if (arguments.length > 1) {
// defineProperties could throw depending on `failIfShimmed`
Object.defineProperties(obj, props);
}
return obj;
};
}
if (!has('object-freeze')) {
Object.freeze = shims.freeze = function freeze (object) {
return object;
};
}
if (!has('object-isfrozen')) {
Object.isFrozen = shims.isFrozen = function isFrozen (object) {
return false;
};
}
if (!has('object-seal')) {
Object.seal = shims.seal = function seal (object) {
return object;
};
}
if (!has('object-issealed')) {
Object.isSealed = shims.isSealed = function isSealed (object) {
return false;
};
}
if (!has('object-getprototypeof')) {
Object.getPrototypeOf = shims.getPrototypeOf = getPrototypeOf;
}
if (!has('object-keys')) {
Object.keys = keys;
}
if (!has('object-getownpropertynames')) {
Object.getOwnPropertyNames = shims.getOwnPropertyNames = function getOwnPropertyNames (object) {
return keys(object);
};
}
if (!has('object-defineproperty') || !has('object-defineproperties')) {
Object.defineProperty = shims.defineProperty = function defineProperty (object, name, descriptor) {
object[name] = descriptor && descriptor.value;
return object;
};
}
if (!has('object-defineproperties') || !has('object-create')) {
Object.defineProperties = shims.defineProperties = function defineProperties (object, descriptors) {
var names, name;
names = keys(descriptors);
while ((name = names.pop())) {
Object.defineProperty(object, name, descriptors[name]);
}
return object;
};
}
if (!has('object-isextensible')) {
Object.isExtensible = shims.isExtensible = function isExtensible (object) {
var prop = '_poly_';
try {
// create unique property name
while (prop in object) prop += '_';
// try to set it
object[prop] = 1;
return hasProp(object, prop);
}
catch (ex) { return false; }
finally {
try { delete object[prop]; } catch (ex) { /* squelch */ }
}
};
}
if (!has('object-preventextensions')) {
Object.preventExtensions = shims.preventExtensions = function preventExtensions (object) {
return object;
};
}
if (!has('object-getownpropertydescriptor')) {
Object.getOwnPropertyDescriptor = shims.getOwnPropertyDescriptor = function getOwnPropertyDescriptor (object, name) {
return hasProp(object, name)
? {
value: object[name],
enumerable: true,
configurable: true,
writable: true
}
: undef;
};
}
function failIfShimmed (failTest) {
var shouldThrow;
if (typeof failTest == 'function') {
shouldThrow = failTest;
}
else {
// assume truthy/falsey
shouldThrow = function () { return failTest; };
}
// create throwers for some features
for (var feature in shims) {
Object[feature] = shouldThrow(feature)
? createFlameThrower(feature)
: shims[feature];
}
}
function assertIsObject (o) { if (typeof o != 'object') throw new TypeError('Object.getPrototypeOf called on non-object'); }
return {
failIfShimmed: failIfShimmed
};
});
/**
* TroopJS core/component/factory
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:mikael@karon.se
*/
/*global define:true*/
define('troopjs-core/component/factory',[ "troopjs-utils/unique", "poly/object" ], function ComponentFactoryModule(unique) {
/*jshint laxbreak:true */
var PROTOTYPE = "prototype";
var TOSTRING = "toString";
var ARRAY_PROTO = Array[PROTOTYPE];
var ARRAY_PUSH = ARRAY_PROTO.push;
var ARRAY_UNSHIFT = ARRAY_PROTO.unshift;
var OBJECT_TOSTRING = Object[PROTOTYPE][TOSTRING];
var TYPEOF_FUNCTION = "function";
var DISPLAYNAME = "displayName";
var LENGTH = "length";
var EXTEND = "extend";
var CREATE = "create";
var DECORATE = "decorate";
var DECORATED = "decorated";
var BEFORE = "before";
var AFTER = "after";
var AROUND = "around";
var CONSTRUCTOR = "constructor";
var CONSTRUCTORS = "constructors";
var SPECIALS = "specials";
var GROUP = "group";
var VALUE = "value";
var FEATURES = "features";
var TYPE = "type";
var NAME = "name";
var RE_SPECIAL = /^(\w+)(?::([^\/]+))?\/(.+)/;
var NOOP = function noop () {};
var factoryDescriptors = {};
/**
* Create a component
* @returns {*}
*/
function create() {
return extend.apply(this, arguments)();
}
/**
* Extends a component
* @returns {*} New component
*/
function extend() {
var args = [this];
ARRAY_PUSH.apply(args, arguments);
return Factory.apply(null, args);
}
/**
* Creates new Decorator
* @param {Function} decorated Original function
* @param {Function} decorate Function to re-write descriptor
* @constructor
*/
function Decorator(decorated, decorate) {
var descriptor = {};
// Add DECORATED to descriptor
descriptor[DECORATED] = {
"value" : decorated
};
// Add DECORATE to descriptor
descriptor[DECORATE] = {
"value" : decorate
};
// Define properties
Object.defineProperties(this, descriptor);
}
/**
* Before advise
* @param {Function} decorated Original function
* @returns {ComponentFactoryModule.Decorator}
*/
function before(decorated) {
return new Decorator(decorated, before[DECORATE]);
}
/**
* Describe before
* @param descriptor
* @returns {*}
*/
before[DECORATE] = function (descriptor) {
var previous = this[DECORATED];
var next = descriptor[VALUE];
descriptor[VALUE] = next
? function () {
var self = this;
var args = arguments;
return next.apply(self, args = previous.apply(self, args) || args);
}
: previous;
return descriptor;
};
/**
* After decorator
* @param decorated
* @returns {ComponentFactoryModule.Decorator}
*/
function after(decorated) {
return new Decorator(decorated, after[DECORATE]);
}
/**
* Decorate after
* @param descriptor
* @returns {*}
*/
after[DECORATE] = function (descriptor) {
var previous = descriptor[VALUE];
var next = this[DECORATED];
descriptor[VALUE] = previous
? function () {
var self = this;
var args = arguments;
return next.apply(self, args = previous.apply(self, args) || args);
}
: next;
return descriptor;
};
/**
* Around decorator
* @param decorated
* @returns {ComponentFactoryModule.Decorator}
*/
function around(decorated) {
return new Decorator(decorated, around[DECORATE]);
}
/**
* Decorate around
* @param descriptor
* @returns {*}
*/
around[DECORATE] = function (descriptor) {
descriptor[VALUE] = this[DECORATED](descriptor[VALUE] || NOOP);
return descriptor;
};
/**
* Returns a string representation of this constructor
* @returns {String}
*/
function ConstructorToString() {
var self = this;
var prototype = self[PROTOTYPE];
return DISPLAYNAME in prototype
? prototype[DISPLAYNAME]
: OBJECT_TOSTRING.call(self);
}
/**
* Creates components
* @returns {*} New component
* @constructor
*/
function Factory () {
var special;
var specials = [];
var specialsLength;
var arg;
var args = arguments;
var argsLength = args[LENGTH];
var constructors = [];
var constructorsLength;
var name;
var names;
var namesLength;
var i;
var j;
var group;
var type;
var matches;
var value;
var descriptor;
var prototype = {};
var prototypeDescriptors = {};
var constructorDescriptors = {};
// Iterate arguments
for (i = 0; i < argsLength; i++) {
// Get arg
arg = args[i];
// If this is a function we're going to add it as a constructor candidate
if(typeof arg === TYPEOF_FUNCTION) {
// If this is a synthetic constructor then add (child) constructors
if (CONSTRUCTORS in arg) {
ARRAY_PUSH.apply(constructors, arg[CONSTRUCTORS]);
}
// Otherwise add as usual
else {
ARRAY_PUSH.call(constructors, arg);
}
// If we have SPECIALS then unshift arg[SPECIALS] onto specials
if (SPECIALS in arg) {
ARRAY_UNSHIFT.apply(specials, arg[SPECIALS]);
}
// Continue if this is a dead cause
if (arg === arg[PROTOTYPE][CONSTRUCTOR]) {
continue;
}
// Arg is now arg prototype
arg = arg[PROTOTYPE];
}
// Get arg names
names = Object.getOwnPropertyNames(arg);
// Iterate names
for (j = 0, namesLength = names[LENGTH]; j < namesLength; j++) {
// Get name
name = names[j];
// Check if this matches a SPECIAL signature
if ((matches = RE_SPECIAL.exec(name))) {
// Create special
special = {};
// Set special properties
special[GROUP] = group = matches[1];
special[FEATURES] = matches[2];
special[TYPE] = type = matches[3];
special[NAME] = group + "/" + type;
special[VALUE] = arg[name];
// Unshift special onto specials
ARRAY_UNSHIFT.call(specials, special);
}
// Otherwise just add to prototypeDescriptors
else {
// Get descriptor for arg
descriptor = Object.getOwnPropertyDescriptor(arg, name);
// Get value
value = descriptor[VALUE];
// If value is instanceof Advice, we should re-describe, otherwise just use the original desciptor
prototypeDescriptors[name] = value instanceof Decorator
? value[DECORATE](prototypeDescriptors[name] || {
"enumerable" : true,
"configurable" : true,
"writable" : true
})
: descriptor;
}
}
}
// Define properties on prototype
Object.defineProperties(prototype, prototypeDescriptors);
// Reduce constructors to unique values
constructorsLength = unique.call(constructors);
// Reduce specials to unique values
specialsLength = unique.call(specials);
// Iterate specials
for (i = 0; i < specialsLength; i++) {
// Get special
special = specials[i];
// Get special properties
group = special[GROUP];
type = special[TYPE];
name = special[NAME];
// Get or create group object
group = group in specials
? specials[group]
: specials[group] = [];
// Get or create type object
type = type in group
? group[type]
: group[type] = specials[name] = [];
// Store special in group/type
group[group[LENGTH]] = type[type[LENGTH]] = special;
}
/**
* Component constructor
* @returns {Constructor} Constructor
* @constructor
*/
function Constructor () {
// Allow to be created either via 'new' or direct invocation
var instance = this instanceof Constructor
? this
: Object.create(prototype);
var _args = arguments;
var _i;
// Set the constructor on instance
Object.defineProperty(instance, CONSTRUCTOR, {
"value" : Constructor
});
// Iterate constructors
for (_i = 0; _i < constructorsLength; _i++) {
// Capture result as _args to pass to next constructor
_args = constructors[_i].apply(instance, _args) || _args;
}
return instance;
}
// Add PROTOTYPE to constructorDescriptors
constructorDescriptors[PROTOTYPE] = {
"value" : prototype
};
// Add CONSTRUCTORS to constructorDescriptors
constructorDescriptors[CONSTRUCTORS] = {
"value" : constructors
};
// Add SPECIALS to constructorDescriptors
constructorDescriptors[SPECIALS] = {
"value" : specials
};
constructorDescriptors[TOSTRING] = {
"value" : ConstructorToString
};
// Add EXTEND to constructorDescriptors
constructorDescriptors[EXTEND] = {
"value" : extend
};
// Add CREATE to constructorDescriptors
constructorDescriptors[CREATE] = {
"value" : create
};
// Define prototypeDescriptors on Constructor
Object.defineProperties(Constructor, constructorDescriptors);
// Return Constructor
return Constructor;
}
// Add CREATE to factoryDescriptors
factoryDescriptors[CREATE] = {
"value" : function FactoryCreate() {
return Factory.apply(null, arguments)();
}
};
// Add BEFORE to factoryDescriptors
factoryDescriptors[BEFORE] = {
"value" : before
};
// Add AFTER to factoryDescriptors
factoryDescriptors[AFTER] = {
"value" : after
};
// Add AROUND to factoryDescriptors
factoryDescriptors[AROUND] = {
"value" : around
};
// Define factoryDescriptors on Factory
Object.defineProperties(Factory, factoryDescriptors);
// Return Factory
return Factory;
});
/** @license MIT License (c) copyright 2011-2013 original author or authors */
/**
* A lightweight CommonJS Promises/A and when() implementation
* when is part of the cujo.js family of libraries (http://cujojs.com/)
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
* @author Brian Cavalier
* @author John Hann
* @version 2.1.0
*/
(function(define, global) { 'use strict';
define('when/when',[],function () {
// Public API
when.defer = defer; // Create a deferred
when.resolve = resolve; // Create a resolved promise
when.reject = reject; // Create a rejected promise
when.join = join; // Join 2 or more promises
when.all = all; // Resolve a list of promises
when.map = map; // Array.map() for promises
when.reduce = reduce; // Array.reduce() for promises
when.settle = settle; // Settle a list of promises
when.any = any; // One-winner race
when.some = some; // Multi-winner race
when.isPromise = isPromise; // Determine if a thing is a promise
when.promise = promise; // EXPERIMENTAL: May change. Use at your own risk
/**
* Register an observer for a promise or immediate value.
*
* @param {*} promiseOrValue
* @param {function?} [onFulfilled] callback to be called when promiseOrValue is
* successfully fulfilled. If promiseOrValue is an immediate value, callback
* will be invoked immediately.
* @param {function?} [onRejected] callback to be called when promiseOrValue is
* rejected.
* @param {function?} [onProgress] callback to be called when progress updates
* are issued for promiseOrValue.
* @returns {Promise} a new {@link Promise} that will complete with the return
* value of callback or errback or the completion value of promiseOrValue if
* callback and/or errback is not supplied.
*/
function when(promiseOrValue, onFulfilled, onRejected, onProgress) {
// Get a trusted promise for the input promiseOrValue, and then
// register promise handlers
return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress);
}
/**
* Trusted Promise constructor. A Promise created from this constructor is
* a trusted when.js promise. Any other duck-typed promise is considered
* untrusted.
* @constructor
* @name Promise
*/
function Promise(then, inspect) {
this.then = then;
this.inspect = inspect;
}
Promise.prototype = {
/**
* Register a rejection handler. Shortcut for .then(undefined, onRejected)
* @param {function?} onRejected
* @return {Promise}
*/
otherwise: function(onRejected) {
return this.then(undef, onRejected);
},
/**
* Ensures that onFulfilledOrRejected will be called regardless of whether
* this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT
* receive the promises' value or reason. Any returned value will be disregarded.
* onFulfilledOrRejected may throw or return a rejected promise to signal
* an additional error.
* @param {function} onFulfilledOrRejected handler to be called regardless of
* fulfillment or rejection
* @returns {Promise}
*/
ensure: function(onFulfilledOrRejected) {
return this.then(injectHandler, injectHandler).yield(this);
function injectHandler() {
return resolve(onFulfilledOrRejected());
}
},
/**
* Shortcut for .then(function() { return value; })
* @param {*} value
* @return {Promise} a promise that:
* - is fulfilled if value is not a promise, or
* - if value is a promise, will fulfill with its value, or reject
* with its reason.
*/
'yield': function(value) {
return this.then(function() {
return value;
});
},
/**
* Assumes that this promise will fulfill with an array, and arranges
* for the onFulfilled to be called with the array as its argument list
* i.e. onFulfilled.apply(undefined, array).
* @param {function} onFulfilled function to receive spread arguments
* @return {Promise}
*/
spread: function(onFulfilled) {
return this.then(function(array) {
// array may contain promises, so resolve its contents.
return all(array, function(array) {
return onFulfilled.apply(undef, array);
});
});
},
/**
* Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected)
* @deprecated
*/
always: function(onFulfilledOrRejected, onProgress) {
return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress);
}
};
/**
* Returns a resolved promise. The returned promise will be
* - fulfilled with promiseOrValue if it is a value, or
* - if promiseOrValue is a promise
* - fulfilled with promiseOrValue's value after it is fulfilled
* - rejected with promiseOrValue's reason after it is rejected
* @param {*} value
* @return {Promise}
*/
function resolve(value) {
return promise(function(resolve) {
resolve(value);
});
}
/**
* Returns a rejected promise for the supplied promiseOrValue. The returned
* promise will be rejected with:
* - promiseOrValue, if it is a value, or
* - if promiseOrValue is a promise
* - promiseOrValue's value after it is fulfilled
* - promiseOrValue's reason after it is rejected
* @param {*} promiseOrValue the rejected value of the returned {@link Promise}
* @return {Promise} rejected {@link Promise}
*/
function reject(promiseOrValue) {
return when(promiseOrValue, rejected);
}
/**
* Creates a new Deferred with fully isolated resolver and promise parts,
* either or both of which may be given out safely to consumers.
* The resolver has resolve, reject, and progress. The promise
* only has then.
*
* @return {{
* promise: Promise,
* resolve: function:Promise,
* reject: function:Promise,
* notify: function:Promise
* resolver: {
* resolve: function:Promise,
* reject: function:Promise,
* notify: function:Promise
* }}}
*/
function defer() {
var deferred, pending, resolved;
// Optimize object shape
deferred = {
promise: undef, resolve: undef, reject: undef, notify: undef,
resolver: { resolve: undef, reject: undef, notify: undef }
};
deferred.promise = pending = promise(makeDeferred);
return deferred;
function makeDeferred(resolvePending, rejectPending, notifyPending) {
deferred.resolve = deferred.resolver.resolve = function(value) {
if(resolved) {
return resolve(value);
}
resolved = true;
resolvePending(value);
return pending;
};
deferred.reject = deferred.resolver.reject = function(reason) {
if(resolved) {
return resolve(rejected(reason));
}
resolved = true;
rejectPending(reason);
return pending;
};
deferred.notify = deferred.resolver.notify = function(update) {
notifyPending(update);
return update;
};
}
}
/**
* Creates a new promise whose fate is determined by resolver.
* @private (for now)
* @param {function} resolver function(resolve, reject, notify)
* @returns {Promise} promise whose fate is determine by resolver
*/
function promise(resolver) {
var value, handlers = [];
// Call the provider resolver to seal the promise's fate
try {
resolver(promiseResolve, promiseReject, promiseNotify);
} catch(e) {
promiseReject(e);
}
// Return the promise
return new Promise(then, inspect);
/**
* Register handlers for this promise.
* @param [onFulfilled] {Function} fulfillment handler
* @param [onRejected] {Function} rejection handler
* @param [onProgress] {Function} progress handler
* @return {Promise} new Promise
*/
function then(onFulfilled, onRejected, onProgress) {
return promise(function(resolve, reject, notify) {
handlers
// Call handlers later, after resolution
? handlers.push(function(value) {
value.then(onFulfilled, onRejected, onProgress)
.then(resolve, reject, notify);
})
// Call handlers soon, but not in the current stack
: enqueue(function() {
value.then(onFulfilled, onRejected, onProgress)
.then(resolve, reject, notify);
});
});
}
function inspect() {
return value ? value.inspect() : toPendingState();
}
/**
* Transition from pre-resolution state to post-resolution state, notifying
* all listeners of the ultimate fulfillment or rejection
* @param {*|Promise} val resolution value
*/
function promiseResolve(val) {
if(!handlers) {
return;
}
value = coerce(val);
scheduleHandlers(handlers, value);
handlers = undef;
}
/**
* Reject this promise with the supplied reason, which will be used verbatim.
* @param {*} reason reason for the rejection
*/
function promiseReject(reason) {
promiseResolve(rejected(reason));
}
/**
* Issue a progress event, notifying all progress listeners
* @param {*} update progress event payload to pass to all listeners
*/
function promiseNotify(update) {
if(handlers) {
scheduleHandlers(handlers, progressing(update));
}
}
}
/**
* Coerces x to a trusted Promise
*
* @private
* @param {*} x thing to coerce
* @returns {Promise} Guaranteed to return a trusted Promise. If x
* is trusted, returns x, otherwise, returns a new, trusted, already-resolved
* Promise whose resolution value is:
* * the resolution value of x if it's a foreign promise, or
* * x if it's a value
*/
function coerce(x) {
if(x instanceof Promise) {
return x;
}
if (!(x === Object(x) && 'then' in x)) {
return fulfilled(x);
}
return promise(function(resolve, reject, notify) {
enqueue(function() {
try {
// We must check and assimilate in the same tick, but not the
// current tick, careful only to access promiseOrValue.then once.
var untrustedThen = x.then;
if(typeof untrustedThen === 'function') {
fcall(untrustedThen, x, resolve, reject, notify);
} else {
// It's a value, create a fulfilled wrapper
resolve(fulfilled(x));
}
} catch(e) {
// Something went wrong, reject
reject(e);
}
});
});
}
/**
* Create an already-fulfilled promise for the supplied value
* @private
* @param {*} value
* @return {Promise} fulfilled promise
*/
function fulfilled(value) {
var self = new Promise(function (onFulfilled) {
try {
return typeof onFulfilled == 'function'
? coerce(onFulfilled(value)) : self;
} catch (e) {
return rejected(e);
}
}, function() {
return toFulfilledState(value);
});
return self;
}
/**
* Create an already-rejected promise with the supplied rejection reason.
* @private
* @param {*} reason
* @return {Promise} rejected promise
*/
function rejected(reason) {
var self = new Promise(function (_, onRejected) {
try {
return typeof onRejected == 'function'
? coerce(onRejected(reason)) : self;
} catch (e) {
return rejected(e);
}
}, function() {
return toRejectedState(reason);
});
return self;
}
/**
* Create a progress promise with the supplied update.
* @private
* @param {*} update
* @return {Promise} progress promise
*/
function progressing(update) {
var self = new Promise(function (_, __, onProgress) {
try {
return typeof onProgress == 'function'
? progressing(onProgress(update)) : self;
} catch (e) {
return progressing(e);
}
});
return self;
}
/**
* Schedule a task that will process a list of handlers
* in the next queue drain run.
* @private
* @param {Array} handlers queue of handlers to execute
* @param {*} value passed as the only arg to each handler
*/
function scheduleHandlers(handlers, value) {
enqueue(function() {
var handler, i = 0;
while (handler = handlers[i++]) {
handler(value);
}
});
}
/**
* Determines if promiseOrValue is a promise or not
*
* @param {*} promiseOrValue anything
* @returns {boolean} true if promiseOrValue is a {@link Promise}
*/
function isPromise(promiseOrValue) {
return promiseOrValue && typeof promiseOrValue.then === 'function';
}
/**
* Initiates a competitive race, returning a promise that will resolve when
* howMany of the supplied promisesOrValues have resolved, or will reject when
* it becomes impossible for howMany to resolve, for example, when
* (promisesOrValues.length - howMany) + 1 input promises reject.
*
* @param {Array} promisesOrValues array of anything, may contain a mix
* of promises and values
* @param howMany {number} number of promisesOrValues to resolve
* @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
* @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
* @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
* @returns {Promise} promise that will resolve to an array of howMany values that
* resolved first, or will reject with an array of
* (promisesOrValues.length - howMany) + 1 rejection reasons.
*/
function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) {
return when(promisesOrValues, function(promisesOrValues) {
return promise(resolveSome).then(onFulfilled, onRejected, onProgress);
function resolveSome(resolve, reject, notify) {
var toResolve, toReject, values, reasons, fulfillOne, rejectOne, len, i;
len = promisesOrValues.length >>> 0;
toResolve = Math.max(0, Math.min(howMany, len));
values = [];
toReject = (len - toResolve) + 1;
reasons = [];
// No items in the input, resolve immediately
if (!toResolve) {
resolve(values);
} else {
rejectOne = function(reason) {
reasons.push(reason);
if(!--toReject) {
fulfillOne = rejectOne = identity;
reject(reasons);
}
};
fulfillOne = function(val) {
// This orders the values based on promise resolution order
values.push(val);
if (!--toResolve) {
fulfillOne = rejectOne = identity;
resolve(values);
}
};
for(i = 0; i < len; ++i) {
if(i in promisesOrValues) {
when(promisesOrValues[i], fulfiller, rejecter, notify);
}
}
}
function rejecter(reason) {
rejectOne(reason);
}
function fulfiller(val) {
fulfillOne(val);
}
}
});
}
/**
* Initiates a competitive race, returning a promise that will resolve when
* any one of the supplied promisesOrValues has resolved or will reject when
* *all* promisesOrValues have rejected.
*
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix
* of {@link Promise}s and values
* @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
* @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
* @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
* @returns {Promise} promise that will resolve to the value that resolved first, or
* will reject with an array of all rejected inputs.
*/
function any(promisesOrValues, onFulfilled, onRejected, onProgress) {
function unwrapSingleResult(val) {
return onFulfilled ? onFulfilled(val[0]) : val[0];
}
return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress);
}
/**
* Return a promise that will resolve only once all the supplied promisesOrValues
* have resolved. The resolution value of the returned promise will be an array
* containing the resolution values of each of the promisesOrValues.
* @memberOf when
*
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix
* of {@link Promise}s and values
* @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then()
* @param {function?} [onRejected] DEPRECATED, use returnedPromise.then()
* @param {function?} [onProgress] DEPRECATED, use returnedPromise.then()
* @returns {Promise}
*/
function all(promisesOrValues, onFulfilled, onRejected, onProgress) {
return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
}
/**
* Joins multiple promises into a single returned promise.
* @return {Promise} a promise that will fulfill when *all* the input promises
* have fulfilled, or will reject when *any one* of the input promises rejects.
*/
function join(/* ...promises */) {
return _map(arguments, identity);
}
/**
* Settles all input promises such that they are guaranteed not to
* be pending once the returned promise fulfills. The returned promise
* will always fulfill, except in the case where `array` is a promise
* that rejects.
* @param {Array|Promise} array or promise for array of promises to settle
* @returns {Promise} promise that always fulfills with an array of
* outcome snapshots for each input promise.
*/
function settle(array) {
return _map(array, toFulfilledState, toRejectedState);
}
/**
* Promise-aware array map function, similar to `Array.prototype.map()`,
* but input array may contain promises or values.
* @param {Array|Promise} array array of anything, may contain promises and values
* @param {function} mapFunc map function which may return a promise or value
* @returns {Promise} promise that will fulfill with an array of mapped values
* or reject if any input promise rejects.
*/
function map(array, mapFunc) {
return _map(array, mapFunc);
}
/**
* Internal map that allows a fallback to handle rejections
* @param {Array|Promise} array array of anything, may contain promises and values
* @param {function} mapFunc map function which may return a promise or value
* @param {function?} fallback function to handle rejected promises
* @returns {Promise} promise that will fulfill with an array of mapped values
* or reject if any input promise rejects.
*/
function _map(array, mapFunc, fallback) {
return when(array, function(array) {
return promise(resolveMap);
function resolveMap(resolve, reject, notify) {
var results, len, toResolve, resolveOne, i;
// Since we know the resulting length, we can preallocate the results
// array to avoid array expansions.
toResolve = len = array.length >>> 0;
results = [];
if(!toResolve) {
resolve(results);
return;
}
resolveOne = function(item, i) {
when(item, mapFunc, fallback).then(function(mapped) {
results[i] = mapped;
if(!--toResolve) {
resolve(results);
}
}, reject, notify);
};
// Since mapFunc may be async, get all invocations of it into flight
for(i = 0; i < len; i++) {
if(i in array) {
resolveOne(array[i], i);
} else {
--toResolve;
}
}
}
});
}
/**
* Traditional reduce function, similar to `Array.prototype.reduce()`, but
* input may contain promises and/or values, and reduceFunc
* may return either a value or a promise, *and* initialValue may
* be a promise for the starting value.
*
* @param {Array|Promise} promise array or promise for an array of anything,
* may contain a mix of promises and values.
* @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total),
* where total is the total number of items being reduced, and will be the same
* in each call to reduceFunc.
* @returns {Promise} that will resolve to the final reduced value
*/
function reduce(promise, reduceFunc /*, initialValue */) {
var args = fcall(slice, arguments, 1);
return when(promise, function(array) {
var total;
total = array.length;
// Wrap the supplied reduceFunc with one that handles promises and then
// delegates to the supplied.
args[0] = function (current, val, i) {
return when(current, function (c) {
return when(val, function (value) {
return reduceFunc(c, value, i, total);
});
});
};
return reduceArray.apply(array, args);
});
}
// Snapshot states
/**
* Creates a fulfilled state snapshot
* @private
* @param {*} x any value
* @returns {{state:'fulfilled',value:*}}
*/
function toFulfilledState(x) {
return { state: 'fulfilled', value: x };
}
/**
* Creates a rejected state snapshot
* @private
* @param {*} x any reason
* @returns {{state:'rejected',reason:*}}
*/
function toRejectedState(x) {
return { state: 'rejected', reason: x };
}
/**
* Creates a pending state snapshot
* @private
* @returns {{state:'pending'}}
*/
function toPendingState() {
return { state: 'pending' };
}
//
// Utilities, etc.
//
var reduceArray, slice, fcall, nextTick, handlerQueue,
setTimeout, funcProto, call, arrayProto, undef;
//
// Shared handler queue processing
//
// Credit to Twisol (https://github.com/Twisol) for suggesting
// this type of extensible queue + trampoline approach for
// next-tick conflation.
handlerQueue = [];
/**
* Enqueue a task. If the queue is not currently scheduled to be
* drained, schedule it.
* @param {function} task
*/
function enqueue(task) {
if(handlerQueue.push(task) === 1) {
scheduleDrainQueue();
}
}
/**
* Schedule the queue to be drained after the stack has cleared.
*/
function scheduleDrainQueue() {
nextTick(drainQueue);
}
/**
* Drain the handler queue entirely, being careful to allow the
* queue to be extended while it is being processed, and to continue
* processing until it is truly empty.
*/
function drainQueue() {
var task, i = 0;
while(task = handlerQueue[i++]) {
task();
}
handlerQueue = [];
}
//
// Capture function and array utils
//
/*global setImmediate,process,vertx*/
// capture setTimeout to avoid being caught by fake timers used in time based tests
setTimeout = global.setTimeout;
// Prefer setImmediate, cascade to node, vertx and finally setTimeout
nextTick = typeof setImmediate === 'function' ? setImmediate.bind(global)
: typeof process === 'object' && process.nextTick ? process.nextTick
: typeof vertx === 'object' ? vertx.runOnLoop // vert.x
: function(task) { setTimeout(task, 0); }; // fallback
// Safe function calls
funcProto = Function.prototype;
call = funcProto.call;
fcall = funcProto.bind
? call.bind(call)
: function(f, context) {
return f.apply(context, slice.call(arguments, 2));
};
// Safe array ops
arrayProto = [];
slice = arrayProto.slice;
// ES5 reduce implementation if native not available
// See: http://es5.github.com/#x15.4.4.21 as there are many
// specifics and edge cases. ES5 dictates that reduce.length === 1
// This implementation deviates from ES5 spec in the following ways:
// 1. It does not check if reduceFunc is a Callable
reduceArray = arrayProto.reduce ||
function(reduceFunc /*, initialValue */) {
/*jshint maxcomplexity: 7*/
var arr, args, reduced, len, i;
i = 0;
arr = Object(this);
len = arr.length >>> 0;
args = arguments;
// If no initialValue, use first item of array (we know length !== 0 here)
// and adjust i to start at second item
if(args.length <= 1) {
// Skip to the first real element in the array
for(;;) {
if(i in arr) {
reduced = arr[i++];
break;
}
// If we reached the end of the array without finding any real
// elements, it's a TypeError
if(++i >= len) {
throw new TypeError();
}
}
} else {
// If initialValue provided, use it
reduced = args[1];
}
// Do the actual reduce
for(;i < len; ++i) {
if(i in arr) {
reduced = reduceFunc(reduced, arr[i], i, arr);
}
}
return reduced;
};
function identity(x) {
return x;
}
return when;
});
})(
typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); },
this
);
define('when', ['when/when'], function (main) { return main; });
/**
* TroopJS utils/merge module
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:mikael@karon.se
*/
/*global define:false */
define('troopjs-utils/merge',[ "poly/object" ], function MergeModule() {
/*jshint strict:false */
var LENGTH = "length";
var ARRAY_PROTO = Array.prototype;
var ARRAY_CONCAT = ARRAY_PROTO.concat;
var OBJECT_PROTO = Object.prototype;
var OBJECT_TOSTRING = OBJECT_PROTO.toString;
var TOSTRING_OBJECT = OBJECT_TOSTRING.call(OBJECT_PROTO);
var TOSTRING_ARRAY = OBJECT_TOSTRING.call(ARRAY_PROTO);
return function merge(source) {
var target = this;
var key;
var keys;
var i;
var j;
var iMax;
var jMax;
var source_value;
var target_value;
var source_tostring;
var target_tostring;
// Iterate arguments
for (i = 0, iMax = arguments[LENGTH]; i < iMax; i++) {
// Get source
source = arguments[i];
// Get source keys
keys = Object.keys(source);
// Iterate keys
for (j = 0, jMax = keys[LENGTH]; j < jMax; j++) {
key = keys[j];
source_value = source[key];
target_value = target[key];
// No merge - copy source_value
if (!(key in target)) {
target[key] = source_value;
continue;
}
// Get 'types'
source_tostring = OBJECT_TOSTRING.call(source_value);
target_tostring = OBJECT_TOSTRING.call(target_value);
// Can we merge objects?
if (target_tostring === TOSTRING_OBJECT && source_tostring === TOSTRING_OBJECT) {
merge.call(target_value, source_value);
}
// Can we merge arrays?
else if (target_tostring === TOSTRING_ARRAY && source_tostring === TOSTRING_ARRAY) {
target[key] = ARRAY_CONCAT.call(target_value, source_value);
}
// No merge - override target[key]
else {
target[key] = source_value;
}
}
}
return target;
};
});
/**
* TroopJS core/component/base
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:mikael@karon.se
*/
/*global define:false */
define('troopjs-core/component/base',[ "./factory", "when", "troopjs-utils/merge" ], function ComponentModule(Factory, when, merge) {
/*jshint laxbreak:true */
var ARRAY_PROTO = Array.prototype;
var ARRAY_PUSH = ARRAY_PROTO.push;
var ARRAY_SLICE = ARRAY_PROTO.slice;
var INSTANCE_COUNT = "instanceCount";
var CONFIGURATION = "configuration";
var PHASE = "phase";
var VALUE = "value";
var SIG = "sig";
var COUNT = 0;
return Factory(
/**
* Creates a new component
* @constructor
*/
function Component() {
var self = this;
// Update instance count
self[INSTANCE_COUNT] = ++COUNT;
self[CONFIGURATION] = {};
}, {
"instanceCount" : COUNT,
"displayName" : "core/component/base",
/**
* Configures component
* @returns {Object} Updated configuration
*/
"configure" : function configure() {
return merge.apply(this[CONFIGURATION], arguments);
},
/**
* Signals the component
* @param _signal {String} Signal
* @return {*}
*/
"signal" : function onSignal(_signal) {
var self = this;
var args = ARRAY_SLICE.call(arguments, 1);
var specials = self.constructor.specials;
var signals = (SIG in specials && specials[SIG][_signal]) || [];
var signal;
var index = 0;
var result = [];
var resultLength = -2;
function next(_args) {
// Add result if resultLength is within bounds
if (++resultLength > -1) {
result[resultLength] = _args;
}
// Return a chained promise of next callback, or a promise resolved with _signal
return (signal = signals[index++])
? when(signal[VALUE].apply(self, args), next)
: when.resolve(result);
}
// Return promise
return next(args);
},
/**
* Start the component
* @return {*}
*/
"start" : function start() {
var self = this;
var signal = self.signal;
var args = [ self[PHASE] = "initialize" ];
// Add signal to arguments
ARRAY_PUSH.apply(args, arguments);
return signal.apply(self, args).then(function initialized(_initialized) {
// Modify args to change signal (and store in PHASE)
args[0] = self[PHASE] = "start";
return signal.apply(self, args).then(function started(_started) {
// Update phase
self[PHASE] = "started";
// Return concatenated result
return ARRAY_PROTO.concat(_initialized, _started);
});
});
},
/**
* Stops the component
* @return {*}
*/
"stop" : function stop() {
var self = this;
var signal = self.signal;
var args = [ self[PHASE] = "stop" ];
// Add signal to arguments
ARRAY_PUSH.apply(args, arguments);
return signal.apply(self, args).then(function stopped(_stopped) {
// Modify args to change signal (and store in PHASE)
args[0] = self[PHASE] = "finalize";
return signal.apply(self, args).then(function finalized(_finalized) {
// Update phase
self[PHASE] = "finalized";
// Return concatenated result
return ARRAY_PROTO.concat(_stopped, _finalized);
});
});
},
/**
* Generates string representation of this object
* @returns {string} displayName and instanceCount
*/
"toString" : function _toString() {
var self = this;
return self.displayName + "@" + self[INSTANCE_COUNT];
}
});
});
/**
* TroopJS core/logger/console
* @license MIT http://troopjs.mit-license.org/ © Mikael Karon mailto:mikael@karon.se
*/
/*global define:false */
define('troopjs-core/logger/console',[ "../component/base" ], function ConsoleLogger(Component) {
var CONSOLE = console;
function noop() {}
return Component.create({
"displayName" : "core/logger/console"
},
CONSOLE
? {
"log" : CONSOLE.log.bind(CONSOLE),
"warn" : CONSOLE.warn.bind(CONSOLE),
"debug" : CONSOLE.debug.bind(CONSOLE),
"info" : CONSOLE.info.bind(CONSOLE),
"error" : CONSOLE.error.bind(CONSOLE)
}
: {
"log" : noop,
"warn" : noop,
"debug" : noop,
"info" : noop,
"error" : noop
});
});
/*
Array -- a stand-alone module for using Javascript 1.6 array features
in lame-o browsers that don't support Javascript