UNPKG

todomvc

Version:

> Helping you select an MV\* framework

1,884 lines (1,622 loc) 158 kB
/** * 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