Merge branch 'develop' into tidy-up-core
This commit is contained in:
commit
8b13270f67
@ -88,6 +88,19 @@ v0.15.0 (UNRELEASED)
|
||||
The methods are still not implemented, but now the commands are accepted as
|
||||
valid.
|
||||
|
||||
**HTTP frontend**
|
||||
|
||||
- Fix too broad truthness test that caused :class:`~mopidy.models.TlTrack`
|
||||
objects with ``tlid`` set to ``0`` to be sent to the HTTP client without the
|
||||
``tlid`` field. (Fixes: :issue:`501`)
|
||||
|
||||
- Upgrade Mopidy.js dependencies.
|
||||
|
||||
**Extension support**
|
||||
|
||||
- :class:`~mopidy.config.Secret` is now deserialized to unicode strings instead
|
||||
of bytestrings. This may require modifications to extensions.
|
||||
|
||||
|
||||
v0.14.2 (2013-07-01)
|
||||
====================
|
||||
|
||||
@ -46,6 +46,22 @@ Issues:
|
||||
https://github.com/dz0ny/mopidy-beets/issues
|
||||
|
||||
|
||||
Mopidy-GMusic
|
||||
-------------
|
||||
|
||||
Provides a backend for playing music from `Google Play Music
|
||||
<https://play.google.com/music/>`_.
|
||||
|
||||
Author:
|
||||
Ronald Hecht
|
||||
PyPI:
|
||||
`Mopidy-GMusic <https://pypi.python.org/pypi/Mopidy-GMusic>`_
|
||||
GitHub:
|
||||
`hechtus/mopidy-gmusic <https://github.com/hechtus/mopidy-gmusic>`_
|
||||
Issues:
|
||||
https://github.com/hechtus/mopidy-gmusic/issues
|
||||
|
||||
|
||||
Mopidy-NAD
|
||||
----------
|
||||
|
||||
@ -61,6 +77,22 @@ Issues:
|
||||
https://github.com/mopidy/mopidy/issues
|
||||
|
||||
|
||||
Mopidy-SomaFM
|
||||
-------------
|
||||
|
||||
Provides a backend for playing music from the `SomaFM <http://somafm.com/>`_
|
||||
service.
|
||||
|
||||
Author:
|
||||
Alexandre Petitjean
|
||||
PyPI:
|
||||
`Mopidy-SomaFM <https://pypi.python.org/pypi/Mopidy-SomaFM>`_
|
||||
GitHub:
|
||||
`AlexandrePTJ/mopidy-somafm <https://github.com/AlexandrePTJ/mopidy-somafm/>`_
|
||||
Issues:
|
||||
https://github.com/AlexandrePTJ/mopidy-somafm/issues
|
||||
|
||||
|
||||
Mopidy-SoundCloud
|
||||
-----------------
|
||||
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
*
|
||||
* https://github.com/busterjs/bane
|
||||
*
|
||||
* @version 0.4.0
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
((typeof define === "function" && define.amd && function (m) { define(m); }) ||
|
||||
((typeof define === "function" && define.amd && function (m) { define("bane", m); }) ||
|
||||
(typeof module === "object" && function (m) { module.exports = m(); }) ||
|
||||
function (m) { this.bane = m(); }
|
||||
)(function () {
|
||||
@ -152,7 +152,7 @@
|
||||
notifyListener(event, toNotify[i], args);
|
||||
}
|
||||
|
||||
toNotify = listeners(this, event).slice()
|
||||
toNotify = listeners(this, event).slice();
|
||||
args = slice.call(arguments, 1);
|
||||
for (i = 0, l = toNotify.length; i < l; ++i) {
|
||||
notifyListener(event, toNotify[i], args);
|
||||
@ -9,27 +9,30 @@
|
||||
*
|
||||
* @author Brian Cavalier
|
||||
* @author John Hann
|
||||
* @version 2.0.0
|
||||
* @version 2.4.0
|
||||
*/
|
||||
(function(define) { 'use strict';
|
||||
define(function () {
|
||||
(function(define, global) { 'use strict';
|
||||
define(function (require) {
|
||||
|
||||
// Public API
|
||||
|
||||
when.defer = defer; // Create a deferred
|
||||
when.promise = promise; // Create a pending promise
|
||||
when.resolve = resolve; // Create a resolved promise
|
||||
when.reject = reject; // Create a rejected promise
|
||||
when.defer = defer; // Create a {promise, resolver} pair
|
||||
|
||||
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.isPromise = isPromiseLike; // DEPRECATED: use isPromiseLike
|
||||
when.isPromiseLike = isPromiseLike; // Is something promise-like, aka thenable
|
||||
|
||||
/**
|
||||
* Register an observer for a promise or immediate value.
|
||||
@ -57,13 +60,35 @@ define(function () {
|
||||
* a trusted when.js promise. Any other duck-typed promise is considered
|
||||
* untrusted.
|
||||
* @constructor
|
||||
* @param {function} sendMessage function to deliver messages to the promise's handler
|
||||
* @param {function?} inspect function that reports the promise's state
|
||||
* @name Promise
|
||||
*/
|
||||
function Promise(then) {
|
||||
this.then = then;
|
||||
function Promise(sendMessage, inspect) {
|
||||
this._message = sendMessage;
|
||||
this.inspect = inspect;
|
||||
}
|
||||
|
||||
Promise.prototype = {
|
||||
/**
|
||||
* Register handlers for this promise.
|
||||
* @param [onFulfilled] {Function} fulfillment handler
|
||||
* @param [onRejected] {Function} rejection handler
|
||||
* @param [onProgress] {Function} progress handler
|
||||
* @return {Promise} new Promise
|
||||
*/
|
||||
then: function(onFulfilled, onRejected, onProgress) {
|
||||
/*jshint unused:false*/
|
||||
var args, sendMessage;
|
||||
|
||||
args = arguments;
|
||||
sendMessage = this._message;
|
||||
|
||||
return _promise(function(resolve, reject, notify) {
|
||||
sendMessage('when', args, resolve, notify);
|
||||
}, this._status && this._status.observed());
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a rejection handler. Shortcut for .then(undefined, onRejected)
|
||||
* @param {function?} onRejected
|
||||
@ -84,9 +109,7 @@ define(function () {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
ensure: function(onFulfilledOrRejected) {
|
||||
var self = this;
|
||||
|
||||
return this.then(injectHandler, injectHandler).yield(self);
|
||||
return this.then(injectHandler, injectHandler)['yield'](this);
|
||||
|
||||
function injectHandler() {
|
||||
return resolve(onFulfilledOrRejected());
|
||||
@ -107,6 +130,16 @@ define(function () {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs a side effect when this promise fulfills, without changing the
|
||||
* fulfillment value.
|
||||
* @param {function} onFulfilledSideEffect
|
||||
* @returns {Promise}
|
||||
*/
|
||||
tap: function(onFulfilledSideEffect) {
|
||||
return this.then(onFulfilledSideEffect)['yield'](this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Assumes that this promise will fulfill with an array, and arranges
|
||||
* for the onFulfilled to be called with the array as its argument list
|
||||
@ -162,13 +195,16 @@ define(function () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Deferred with fully isolated resolver and promise parts,
|
||||
* either or both of which may be given out safely to consumers.
|
||||
* Creates a {promise, resolver} pair, either or both of which
|
||||
* may be given out safely to consumers.
|
||||
* The resolver has resolve, reject, and progress. The promise
|
||||
* only has then.
|
||||
* has then plus extended promise API.
|
||||
*
|
||||
* @return {{
|
||||
* promise: Promise,
|
||||
* resolve: function:Promise,
|
||||
* reject: function:Promise,
|
||||
* notify: function:Promise
|
||||
* resolver: {
|
||||
* resolve: function:Promise,
|
||||
* reject: function:Promise,
|
||||
@ -216,12 +252,26 @@ define(function () {
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
return _promise(resolver, monitorApi.PromiseStatus && monitorApi.PromiseStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new promise, linked to parent, whose fate is determined
|
||||
* by resolver.
|
||||
* @param {function} resolver function(resolve, reject, notify)
|
||||
* @param {Promise?} status promise from which the new promise is begotten
|
||||
* @returns {Promise} promise whose fate is determine by resolver
|
||||
* @private
|
||||
*/
|
||||
function _promise(resolver, status) {
|
||||
var self, value, consumers = [];
|
||||
|
||||
self = new Promise(_message, inspect);
|
||||
self._status = status;
|
||||
|
||||
// Call the provider resolver to seal the promise's fate
|
||||
try {
|
||||
@ -231,29 +281,34 @@ define(function () {
|
||||
}
|
||||
|
||||
// Return the promise
|
||||
return new Promise(then);
|
||||
return self;
|
||||
|
||||
/**
|
||||
* Register handlers for this promise.
|
||||
* @param [onFulfilled] {Function} fulfillment handler
|
||||
* @param [onRejected] {Function} rejection handler
|
||||
* @param [onProgress] {Function} progress handler
|
||||
* @return {Promise} new Promise
|
||||
* Private message delivery. Queues and delivers messages to
|
||||
* the promise's ultimate fulfillment value or rejection reason.
|
||||
* @private
|
||||
* @param {String} type
|
||||
* @param {Array} args
|
||||
* @param {Function} resolve
|
||||
* @param {Function} notify
|
||||
*/
|
||||
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 _message(type, args, resolve, notify) {
|
||||
consumers ? consumers.push(deliver) : enqueue(function() { deliver(value); });
|
||||
|
||||
function deliver(p) {
|
||||
p._message(type, args, resolve, notify);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the promise's state at the instant inspect()
|
||||
* is called. The returned object is not live and will not update as
|
||||
* the promise's state changes.
|
||||
* @returns {{ state:String, value?:*, reason?:* }} status snapshot
|
||||
* of the promise.
|
||||
*/
|
||||
function inspect() {
|
||||
return value ? value.inspect() : toPendingState();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,14 +317,17 @@ define(function () {
|
||||
* @param {*|Promise} val resolution value
|
||||
*/
|
||||
function promiseResolve(val) {
|
||||
if(!handlers) {
|
||||
if(!consumers) {
|
||||
return;
|
||||
}
|
||||
|
||||
value = coerce(val);
|
||||
scheduleHandlers(handlers, value);
|
||||
scheduleConsumers(consumers, value);
|
||||
consumers = undef;
|
||||
|
||||
handlers = undef;
|
||||
if(status) {
|
||||
updateStatus(value, status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -285,27 +343,90 @@ define(function () {
|
||||
* @param {*} update progress event payload to pass to all listeners
|
||||
*/
|
||||
function promiseNotify(update) {
|
||||
if(handlers) {
|
||||
scheduleHandlers(handlers, progressing(update));
|
||||
if(consumers) {
|
||||
scheduleConsumers(consumers, progressed(update));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fulfilled, local promise as a proxy for a value
|
||||
* NOTE: must never be exposed
|
||||
* @param {*} value fulfillment value
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function fulfilled(value) {
|
||||
return near(
|
||||
new NearFulfilledProxy(value),
|
||||
function() { return toFulfilledState(value); }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rejected, local promise with the supplied reason
|
||||
* NOTE: must never be exposed
|
||||
* @param {*} reason rejection reason
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function rejected(reason) {
|
||||
return near(
|
||||
new NearRejectedProxy(reason),
|
||||
function() { return toRejectedState(reason); }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a near promise using the provided proxy
|
||||
* NOTE: must never be exposed
|
||||
* @param {object} proxy proxy for the promise's ultimate value or reason
|
||||
* @param {function} inspect function that returns a snapshot of the
|
||||
* returned near promise's state
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function near(proxy, inspect) {
|
||||
return new Promise(function (type, args, resolve) {
|
||||
try {
|
||||
resolve(proxy[type].apply(proxy, args));
|
||||
} catch(e) {
|
||||
resolve(rejected(e));
|
||||
}
|
||||
}, inspect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a progress promise with the supplied update.
|
||||
* @private
|
||||
* @param {*} update
|
||||
* @return {Promise} progress promise
|
||||
*/
|
||||
function progressed(update) {
|
||||
return new Promise(function (type, args, _, notify) {
|
||||
var onProgress = args[2];
|
||||
try {
|
||||
notify(typeof onProgress === 'function' ? onProgress(update) : update);
|
||||
} catch(e) {
|
||||
notify(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerces x to a trusted Promise
|
||||
*
|
||||
* @private
|
||||
* @param {*} x thing to coerce
|
||||
* @returns {Promise} Guaranteed to return a trusted Promise. If x
|
||||
* @returns {*} 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) {
|
||||
if (x instanceof Promise) {
|
||||
return x;
|
||||
} else if (x !== Object(x)) {
|
||||
}
|
||||
|
||||
if (!(x === Object(x) && 'then' in x)) {
|
||||
return fulfilled(x);
|
||||
}
|
||||
|
||||
@ -332,61 +453,34 @@ define(function () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an already-fulfilled promise for the supplied value
|
||||
* @private
|
||||
* Proxy for a near, fulfilled value
|
||||
* @param {*} value
|
||||
* @return {Promise} fulfilled promise
|
||||
* @constructor
|
||||
*/
|
||||
function fulfilled(value) {
|
||||
var self = new Promise(function (onFulfilled) {
|
||||
try {
|
||||
return typeof onFulfilled == 'function'
|
||||
? coerce(onFulfilled(value)) : self;
|
||||
} catch (e) {
|
||||
return rejected(e);
|
||||
}
|
||||
});
|
||||
|
||||
return self;
|
||||
function NearFulfilledProxy(value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
NearFulfilledProxy.prototype.when = function(onResult) {
|
||||
return typeof onResult === 'function' ? onResult(this.value) : this.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an already-rejected promise with the supplied rejection reason.
|
||||
* @private
|
||||
* Proxy for a near rejection
|
||||
* @param {*} reason
|
||||
* @return {Promise} rejected promise
|
||||
* @constructor
|
||||
*/
|
||||
function rejected(reason) {
|
||||
var self = new Promise(function (_, onRejected) {
|
||||
try {
|
||||
return typeof onRejected == 'function'
|
||||
? coerce(onRejected(reason)) : self;
|
||||
} catch (e) {
|
||||
return rejected(e);
|
||||
}
|
||||
});
|
||||
|
||||
return self;
|
||||
function NearRejectedProxy(reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
NearRejectedProxy.prototype.when = function(_, onError) {
|
||||
if(typeof onError === 'function') {
|
||||
return onError(this.reason);
|
||||
} else {
|
||||
throw this.reason;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedule a task that will process a list of handlers
|
||||
@ -395,7 +489,7 @@ define(function () {
|
||||
* @param {Array} handlers queue of handlers to execute
|
||||
* @param {*} value passed as the only arg to each handler
|
||||
*/
|
||||
function scheduleHandlers(handlers, value) {
|
||||
function scheduleConsumers(handlers, value) {
|
||||
enqueue(function() {
|
||||
var handler, i = 0;
|
||||
while (handler = handlers[i++]) {
|
||||
@ -404,14 +498,23 @@ define(function () {
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatus(value, status) {
|
||||
value.then(statusFulfilled, statusRejected);
|
||||
|
||||
function statusFulfilled() { status.fulfilled(); }
|
||||
function statusRejected(r) { status.rejected(r); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if promiseOrValue is a promise or not
|
||||
*
|
||||
* @param {*} promiseOrValue anything
|
||||
* @returns {boolean} true if promiseOrValue is a {@link Promise}
|
||||
* Determines if x is promise-like, i.e. a thenable object
|
||||
* NOTE: Will return true for *any thenable object*, and isn't truly
|
||||
* safe, since it may attempt to access the `then` property of x (i.e.
|
||||
* clever/malicious getters may do weird things)
|
||||
* @param {*} x anything
|
||||
* @returns {boolean} true if x is promise-like
|
||||
*/
|
||||
function isPromise(promiseOrValue) {
|
||||
return promiseOrValue && typeof promiseOrValue.then === 'function';
|
||||
function isPromiseLike(x) {
|
||||
return x && typeof x.then === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -423,17 +526,15 @@ define(function () {
|
||||
* @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] resolution handler
|
||||
* @param {function?} [onRejected] rejection handler
|
||||
* @param {function?} [onProgress] progress handler
|
||||
* @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) {
|
||||
|
||||
checkCallbacks(2, arguments);
|
||||
|
||||
return when(promisesOrValues, function(promisesOrValues) {
|
||||
|
||||
return promise(resolveSome).then(onFulfilled, onRejected, onProgress);
|
||||
@ -457,7 +558,7 @@ define(function () {
|
||||
rejectOne = function(reason) {
|
||||
reasons.push(reason);
|
||||
if(!--toReject) {
|
||||
fulfillOne = rejectOne = noop;
|
||||
fulfillOne = rejectOne = identity;
|
||||
reject(reasons);
|
||||
}
|
||||
};
|
||||
@ -466,7 +567,7 @@ define(function () {
|
||||
// This orders the values based on promise resolution order
|
||||
values.push(val);
|
||||
if (!--toResolve) {
|
||||
fulfillOne = rejectOne = noop;
|
||||
fulfillOne = rejectOne = identity;
|
||||
resolve(values);
|
||||
}
|
||||
};
|
||||
@ -496,9 +597,9 @@ define(function () {
|
||||
*
|
||||
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix
|
||||
* of {@link Promise}s and values
|
||||
* @param {function?} [onFulfilled] resolution handler
|
||||
* @param {function?} [onRejected] rejection handler
|
||||
* @param {function?} [onProgress] progress handler
|
||||
* @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.
|
||||
*/
|
||||
@ -519,14 +620,13 @@ define(function () {
|
||||
*
|
||||
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix
|
||||
* of {@link Promise}s and values
|
||||
* @param {function?} [onFulfilled] resolution handler
|
||||
* @param {function?} [onRejected] rejection handler
|
||||
* @param {function?} [onProgress] progress handler
|
||||
* @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) {
|
||||
checkCallbacks(1, arguments);
|
||||
return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
|
||||
return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -535,28 +635,49 @@ define(function () {
|
||||
* have fulfilled, or will reject when *any one* of the input promises rejects.
|
||||
*/
|
||||
function join(/* ...promises */) {
|
||||
return map(arguments, identity);
|
||||
return _map(arguments, identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traditional map function, similar to `Array.prototype.map()`, but allows
|
||||
* input to contain {@link Promise}s and/or values, and mapFunc may return
|
||||
* either a value or a {@link Promise}
|
||||
*
|
||||
* @param {Array|Promise} array array of anything, may contain a mix
|
||||
* of {@link Promise}s and values
|
||||
* @param {function} mapFunc mapping function mapFunc(value) which may return
|
||||
* either a {@link Promise} or value
|
||||
* @returns {Promise} a {@link Promise} that will resolve to an array containing
|
||||
* the mapped output values.
|
||||
* 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);
|
||||
return _promise(resolveMap);
|
||||
|
||||
function resolveMap(resolve, reject, notify) {
|
||||
var results, len, toResolve, resolveOne, i;
|
||||
var results, len, toResolve, i;
|
||||
|
||||
// Since we know the resulting length, we can preallocate the results
|
||||
// array to avoid array expansions.
|
||||
@ -565,27 +686,28 @@ define(function () {
|
||||
|
||||
if(!toResolve) {
|
||||
resolve(results);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
resolveOne = function(item, i) {
|
||||
when(item, mapFunc).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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveOne(item, i) {
|
||||
when(item, mapFunc, fallback).then(function(mapped) {
|
||||
results[i] = mapped;
|
||||
notify(mapped);
|
||||
|
||||
if(!--toResolve) {
|
||||
resolve(results);
|
||||
}
|
||||
}, reject);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -625,12 +747,46 @@ define(function () {
|
||||
});
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Internals, utilities, etc.
|
||||
//
|
||||
|
||||
var reduceArray, slice, fcall, nextTick, handlerQueue,
|
||||
timeout, funcProto, call, arrayProto, undef;
|
||||
setTimeout, funcProto, call, arrayProto, monitorApi,
|
||||
cjsRequire, undef;
|
||||
|
||||
cjsRequire = require;
|
||||
|
||||
//
|
||||
// Shared handler queue processing
|
||||
@ -648,20 +804,13 @@ define(function () {
|
||||
*/
|
||||
function enqueue(task) {
|
||||
if(handlerQueue.push(task) === 1) {
|
||||
scheduleDrainQueue();
|
||||
nextTick(drainQueue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the queue to be drained in the next tick.
|
||||
*/
|
||||
function scheduleDrainQueue() {
|
||||
nextTick(drainQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drain the handler queue entirely or partially, being careful to allow
|
||||
* the queue to be extended while it is being processed, and to continue
|
||||
* 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() {
|
||||
@ -674,20 +823,36 @@ define(function () {
|
||||
handlerQueue = [];
|
||||
}
|
||||
|
||||
//
|
||||
// Capture function and array utils
|
||||
//
|
||||
/*global setImmediate:true*/
|
||||
// capture setTimeout to avoid being caught by fake timers
|
||||
// used in time based tests
|
||||
setTimeout = global.setTimeout;
|
||||
|
||||
// capture setTimeout to avoid being caught by fake timers used in time based tests
|
||||
timeout = setTimeout;
|
||||
nextTick = typeof setImmediate === 'function'
|
||||
? typeof window === 'undefined'
|
||||
? setImmediate
|
||||
: setImmediate.bind(window)
|
||||
: typeof process === 'object'
|
||||
? process.nextTick
|
||||
: function(task) { timeout(task, 0); };
|
||||
// Allow attaching the monitor to when() if env has no console
|
||||
monitorApi = typeof console != 'undefined' ? console : when;
|
||||
|
||||
// Prefer setImmediate or MessageChannel, cascade to node,
|
||||
// vertx and finally setTimeout
|
||||
/*global setImmediate,MessageChannel,process*/
|
||||
if (typeof setImmediate === 'function') {
|
||||
nextTick = setImmediate.bind(global);
|
||||
} else if(typeof MessageChannel !== 'undefined') {
|
||||
var channel = new MessageChannel();
|
||||
channel.port1.onmessage = drainQueue;
|
||||
nextTick = function() { channel.port2.postMessage(0); };
|
||||
} else if (typeof process === 'object' && process.nextTick) {
|
||||
nextTick = process.nextTick;
|
||||
} else {
|
||||
try {
|
||||
// vert.x 1.x || 2.x
|
||||
nextTick = cjsRequire('vertx').runOnLoop || cjsRequire('vertx').runOnContext;
|
||||
} catch(ignore) {
|
||||
nextTick = function(t) { setTimeout(t, 0); };
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Capture/polyfill function and array utils
|
||||
//
|
||||
|
||||
// Safe function calls
|
||||
funcProto = Function.prototype;
|
||||
@ -748,40 +913,10 @@ define(function () {
|
||||
return reduced;
|
||||
};
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Helper that checks arrayOfCallbacks to ensure that each element is either
|
||||
* a function, or null or undefined.
|
||||
* @private
|
||||
* @param {number} start index at which to start checking items in arrayOfCallbacks
|
||||
* @param {Array} arrayOfCallbacks array to check
|
||||
* @throws {Error} if any element of arrayOfCallbacks is something other than
|
||||
* a functions, null, or undefined.
|
||||
*/
|
||||
function checkCallbacks(start, arrayOfCallbacks) {
|
||||
// TODO: Promises/A+ update type checking and docs
|
||||
var arg, i = arrayOfCallbacks.length;
|
||||
|
||||
while(i > start) {
|
||||
arg = arrayOfCallbacks[--i];
|
||||
|
||||
if (arg != null && typeof arg != 'function') {
|
||||
throw new Error('arg '+i+' must be a function');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
return when;
|
||||
});
|
||||
})(
|
||||
typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); }
|
||||
);
|
||||
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }, this);
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mopidy",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "Client lib for controlling a Mopidy music server over a WebSocket",
|
||||
"homepage": "http://www.mopidy.com/",
|
||||
"author": {
|
||||
@ -14,19 +14,19 @@
|
||||
},
|
||||
"main": "src/mopidy.js",
|
||||
"dependencies": {
|
||||
"bane": "~0.4.0",
|
||||
"faye-websocket": "~0.4.4",
|
||||
"when": "~2.0.0"
|
||||
"bane": "~1.0.0",
|
||||
"faye-websocket": "~0.7.0",
|
||||
"when": "~2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"buster": "~0.6.12",
|
||||
"buster": "~0.6.13",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-buster": "~0.2.1",
|
||||
"grunt-contrib-concat": "~0.3.0",
|
||||
"grunt-contrib-jshint": "~0.4.3",
|
||||
"grunt-contrib-uglify": "~0.2.0",
|
||||
"grunt-contrib-watch": "~0.4.3",
|
||||
"phantomjs": "~1.9.0"
|
||||
"grunt-contrib-jshint": "~0.6.4",
|
||||
"grunt-contrib-uglify": "~0.2.4",
|
||||
"grunt-contrib-watch": "~0.5.3",
|
||||
"phantomjs": "~1.9.2-0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
|
||||
@ -17,12 +17,6 @@ mopidy_args = sys.argv[1:]
|
||||
sys.argv[1:] = []
|
||||
|
||||
|
||||
# Add ../ to the path so we can run Mopidy from a Git checkout without
|
||||
# installing it on the system.
|
||||
sys.path.insert(
|
||||
0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
|
||||
|
||||
|
||||
from mopidy import commands, ext
|
||||
from mopidy.audio import Audio
|
||||
from mopidy import config as config_lib
|
||||
@ -42,15 +36,12 @@ def main():
|
||||
if args.show_deps:
|
||||
commands.show_deps()
|
||||
|
||||
loop = gobject.MainLoop()
|
||||
enabled_extensions = [] # Make sure it is defined before the finally block
|
||||
logging_initialized = False
|
||||
|
||||
# TODO: figure out a way to make the boilerplate in this file reusable in
|
||||
# scanner and other places we need it.
|
||||
|
||||
try:
|
||||
# Initial config without extensions to bootstrap logging.
|
||||
logging_initialized = False
|
||||
logging_config, _ = config_lib.load(
|
||||
args.config_files, [], args.config_overrides)
|
||||
|
||||
@ -59,12 +50,16 @@ def main():
|
||||
logging_config, args.verbosity_level, args.save_debug_log)
|
||||
logging_initialized = True
|
||||
|
||||
create_file_structures()
|
||||
check_old_locations()
|
||||
|
||||
installed_extensions = ext.load_extensions()
|
||||
|
||||
config, config_errors = config_lib.load(
|
||||
args.config_files, installed_extensions, args.config_overrides)
|
||||
|
||||
# Filter out disabled extensions and remove any config errors for them.
|
||||
enabled_extensions = []
|
||||
for extension in installed_extensions:
|
||||
enabled = config[extension.ext_name]['enabled']
|
||||
if ext.validate_extension(extension) and enabled:
|
||||
@ -79,31 +74,38 @@ def main():
|
||||
proxied_config = config_lib.Proxy(config)
|
||||
|
||||
log.setup_log_levels(proxied_config)
|
||||
create_file_structures()
|
||||
check_old_locations()
|
||||
ext.register_gstreamer_elements(enabled_extensions)
|
||||
|
||||
# Anything that wants to exit after this point must use
|
||||
# mopidy.utils.process.exit_process as actors have been started.
|
||||
audio = setup_audio(proxied_config)
|
||||
backends = setup_backends(proxied_config, enabled_extensions, audio)
|
||||
core = setup_core(audio, backends)
|
||||
setup_frontends(proxied_config, enabled_extensions, core)
|
||||
loop.run()
|
||||
start(proxied_config, enabled_extensions)
|
||||
except KeyboardInterrupt:
|
||||
if logging_initialized:
|
||||
logger.info('Interrupted. Exiting...')
|
||||
pass
|
||||
except Exception as ex:
|
||||
if logging_initialized:
|
||||
logger.exception(ex)
|
||||
raise
|
||||
finally:
|
||||
loop.quit()
|
||||
stop_frontends(enabled_extensions)
|
||||
stop_core()
|
||||
stop_backends(enabled_extensions)
|
||||
stop_audio()
|
||||
process.stop_remaining_actors()
|
||||
|
||||
|
||||
def create_file_structures():
|
||||
path.get_or_create_dir(b'$XDG_DATA_DIR/mopidy')
|
||||
path.get_or_create_file(b'$XDG_CONFIG_DIR/mopidy/mopidy.conf')
|
||||
|
||||
|
||||
def check_old_locations():
|
||||
dot_mopidy_dir = path.expand_path(b'~/.mopidy')
|
||||
if os.path.isdir(dot_mopidy_dir):
|
||||
logger.warning(
|
||||
'Old Mopidy dot dir found at %s. Please migrate your config to '
|
||||
'the ini-file based config format. See release notes for further '
|
||||
'instructions.', dot_mopidy_dir)
|
||||
|
||||
old_settings_file = path.expand_path(b'$XDG_CONFIG_DIR/mopidy/settings.py')
|
||||
if os.path.isfile(old_settings_file):
|
||||
logger.warning(
|
||||
'Old Mopidy settings file found at %s. Please migrate your '
|
||||
'config to the ini-file based config format. See release notes '
|
||||
'for further instructions.', old_settings_file)
|
||||
|
||||
|
||||
def log_extension_info(all_extensions, enabled_extensions):
|
||||
@ -125,28 +127,27 @@ def check_config_errors(errors):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def check_old_locations():
|
||||
dot_mopidy_dir = path.expand_path(b'~/.mopidy')
|
||||
if os.path.isdir(dot_mopidy_dir):
|
||||
logger.warning(
|
||||
'Old Mopidy dot dir found at %s. Please migrate your config to '
|
||||
'the ini-file based config format. See release notes for further '
|
||||
'instructions.', dot_mopidy_dir)
|
||||
|
||||
old_settings_file = path.expand_path(b'$XDG_CONFIG_DIR/mopidy/settings.py')
|
||||
if os.path.isfile(old_settings_file):
|
||||
logger.warning(
|
||||
'Old Mopidy settings file found at %s. Please migrate your '
|
||||
'config to the ini-file based config format. See release notes '
|
||||
'for further instructions.', old_settings_file)
|
||||
def start(config, extensions):
|
||||
loop = gobject.MainLoop()
|
||||
try:
|
||||
audio = start_audio(config)
|
||||
backends = start_backends(config, extensions, audio)
|
||||
core = start_core(audio, backends)
|
||||
start_frontends(config, extensions, core)
|
||||
loop.run()
|
||||
except KeyboardInterrupt:
|
||||
logger.info('Interrupted. Exiting...')
|
||||
return
|
||||
finally:
|
||||
loop.quit()
|
||||
stop_frontends(extensions)
|
||||
stop_core()
|
||||
stop_backends(extensions)
|
||||
stop_audio()
|
||||
process.stop_remaining_actors()
|
||||
|
||||
|
||||
def create_file_structures():
|
||||
path.get_or_create_dir(b'$XDG_DATA_DIR/mopidy')
|
||||
path.get_or_create_file(b'$XDG_CONFIG_DIR/mopidy/mopidy.conf')
|
||||
|
||||
|
||||
def setup_audio(config):
|
||||
def start_audio(config):
|
||||
logger.info('Starting Mopidy audio')
|
||||
return Audio.start(config=config).proxy()
|
||||
|
||||
@ -156,7 +157,7 @@ def stop_audio():
|
||||
process.stop_actors_by_class(Audio)
|
||||
|
||||
|
||||
def setup_backends(config, extensions, audio):
|
||||
def start_backends(config, extensions, audio):
|
||||
backend_classes = []
|
||||
for extension in extensions:
|
||||
backend_classes.extend(extension.get_backend_classes())
|
||||
@ -180,7 +181,7 @@ def stop_backends(extensions):
|
||||
process.stop_actors_by_class(backend_class)
|
||||
|
||||
|
||||
def setup_core(audio, backends):
|
||||
def start_core(audio, backends):
|
||||
logger.info('Starting Mopidy core')
|
||||
return Core.start(audio=audio, backends=backends).proxy()
|
||||
|
||||
@ -190,7 +191,7 @@ def stop_core():
|
||||
process.stop_actors_by_class(Core)
|
||||
|
||||
|
||||
def setup_frontends(config, extensions, core):
|
||||
def start_frontends(config, extensions, core):
|
||||
frontend_classes = []
|
||||
for extension in extensions:
|
||||
frontend_classes.extend(extension.get_frontend_classes())
|
||||
|
||||
@ -82,30 +82,22 @@ class String(ConfigValue):
|
||||
return encode(value)
|
||||
|
||||
|
||||
class Secret(ConfigValue):
|
||||
"""Secret value.
|
||||
class Secret(String):
|
||||
"""Secret string value.
|
||||
|
||||
Should be used for passwords, auth tokens etc. Deserializing will not
|
||||
convert to unicode. Will mask value when being displayed.
|
||||
Is decoded as utf-8 and \\n \\t escapes should work and be preserved.
|
||||
|
||||
Should be used for passwords, auth tokens etc. Will mask value when being
|
||||
displayed.
|
||||
"""
|
||||
def __init__(self, optional=False, choices=None):
|
||||
self._required = not optional
|
||||
|
||||
def deserialize(self, value):
|
||||
value = value.strip()
|
||||
validators.validate_required(value, self._required)
|
||||
if not value:
|
||||
return None
|
||||
return value
|
||||
self._choices = None # Choices doesn't make sense for secrets
|
||||
|
||||
def serialize(self, value, display=False):
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode('utf-8')
|
||||
if value is None:
|
||||
return b''
|
||||
elif display:
|
||||
if value is not None and display:
|
||||
return b'********'
|
||||
return value
|
||||
return super(Secret, self).serialize(value, display)
|
||||
|
||||
|
||||
class Integer(ConfigValue):
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/*! Mopidy.js - built 2013-03-31
|
||||
/*! Mopidy.js - built 2013-09-17
|
||||
* http://www.mopidy.com/
|
||||
* Copyright (c) 2013 Stein Magnus Jodal and contributors
|
||||
* Licensed under the Apache License, Version 2.0 */
|
||||
((typeof define === "function" && define.amd && function (m) { define(m); }) ||
|
||||
((typeof define === "function" && define.amd && function (m) { define("bane", m); }) ||
|
||||
(typeof module === "object" && function (m) { module.exports = m(); }) ||
|
||||
function (m) { this.bane = m(); }
|
||||
)(function () {
|
||||
@ -148,7 +148,7 @@
|
||||
notifyListener(event, toNotify[i], args);
|
||||
}
|
||||
|
||||
toNotify = listeners(this, event).slice()
|
||||
toNotify = listeners(this, event).slice();
|
||||
args = slice.call(arguments, 1);
|
||||
for (i = 0, l = toNotify.length; i < l; ++i) {
|
||||
notifyListener(event, toNotify[i], args);
|
||||
@ -187,27 +187,30 @@ if (typeof window !== "undefined") {
|
||||
*
|
||||
* @author Brian Cavalier
|
||||
* @author John Hann
|
||||
* @version 2.0.0
|
||||
* @version 2.4.0
|
||||
*/
|
||||
(function(define) { 'use strict';
|
||||
define(function () {
|
||||
(function(define, global) { 'use strict';
|
||||
define(function (require) {
|
||||
|
||||
// Public API
|
||||
|
||||
when.defer = defer; // Create a deferred
|
||||
when.promise = promise; // Create a pending promise
|
||||
when.resolve = resolve; // Create a resolved promise
|
||||
when.reject = reject; // Create a rejected promise
|
||||
when.defer = defer; // Create a {promise, resolver} pair
|
||||
|
||||
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.isPromise = isPromiseLike; // DEPRECATED: use isPromiseLike
|
||||
when.isPromiseLike = isPromiseLike; // Is something promise-like, aka thenable
|
||||
|
||||
/**
|
||||
* Register an observer for a promise or immediate value.
|
||||
@ -235,13 +238,35 @@ define(function () {
|
||||
* a trusted when.js promise. Any other duck-typed promise is considered
|
||||
* untrusted.
|
||||
* @constructor
|
||||
* @param {function} sendMessage function to deliver messages to the promise's handler
|
||||
* @param {function?} inspect function that reports the promise's state
|
||||
* @name Promise
|
||||
*/
|
||||
function Promise(then) {
|
||||
this.then = then;
|
||||
function Promise(sendMessage, inspect) {
|
||||
this._message = sendMessage;
|
||||
this.inspect = inspect;
|
||||
}
|
||||
|
||||
Promise.prototype = {
|
||||
/**
|
||||
* Register handlers for this promise.
|
||||
* @param [onFulfilled] {Function} fulfillment handler
|
||||
* @param [onRejected] {Function} rejection handler
|
||||
* @param [onProgress] {Function} progress handler
|
||||
* @return {Promise} new Promise
|
||||
*/
|
||||
then: function(onFulfilled, onRejected, onProgress) {
|
||||
/*jshint unused:false*/
|
||||
var args, sendMessage;
|
||||
|
||||
args = arguments;
|
||||
sendMessage = this._message;
|
||||
|
||||
return _promise(function(resolve, reject, notify) {
|
||||
sendMessage('when', args, resolve, notify);
|
||||
}, this._status && this._status.observed());
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a rejection handler. Shortcut for .then(undefined, onRejected)
|
||||
* @param {function?} onRejected
|
||||
@ -262,9 +287,7 @@ define(function () {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
ensure: function(onFulfilledOrRejected) {
|
||||
var self = this;
|
||||
|
||||
return this.then(injectHandler, injectHandler).yield(self);
|
||||
return this.then(injectHandler, injectHandler)['yield'](this);
|
||||
|
||||
function injectHandler() {
|
||||
return resolve(onFulfilledOrRejected());
|
||||
@ -285,6 +308,16 @@ define(function () {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs a side effect when this promise fulfills, without changing the
|
||||
* fulfillment value.
|
||||
* @param {function} onFulfilledSideEffect
|
||||
* @returns {Promise}
|
||||
*/
|
||||
tap: function(onFulfilledSideEffect) {
|
||||
return this.then(onFulfilledSideEffect)['yield'](this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Assumes that this promise will fulfill with an array, and arranges
|
||||
* for the onFulfilled to be called with the array as its argument list
|
||||
@ -340,13 +373,16 @@ define(function () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Deferred with fully isolated resolver and promise parts,
|
||||
* either or both of which may be given out safely to consumers.
|
||||
* Creates a {promise, resolver} pair, either or both of which
|
||||
* may be given out safely to consumers.
|
||||
* The resolver has resolve, reject, and progress. The promise
|
||||
* only has then.
|
||||
* has then plus extended promise API.
|
||||
*
|
||||
* @return {{
|
||||
* promise: Promise,
|
||||
* resolve: function:Promise,
|
||||
* reject: function:Promise,
|
||||
* notify: function:Promise
|
||||
* resolver: {
|
||||
* resolve: function:Promise,
|
||||
* reject: function:Promise,
|
||||
@ -394,12 +430,26 @@ define(function () {
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
return _promise(resolver, monitorApi.PromiseStatus && monitorApi.PromiseStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new promise, linked to parent, whose fate is determined
|
||||
* by resolver.
|
||||
* @param {function} resolver function(resolve, reject, notify)
|
||||
* @param {Promise?} status promise from which the new promise is begotten
|
||||
* @returns {Promise} promise whose fate is determine by resolver
|
||||
* @private
|
||||
*/
|
||||
function _promise(resolver, status) {
|
||||
var self, value, consumers = [];
|
||||
|
||||
self = new Promise(_message, inspect);
|
||||
self._status = status;
|
||||
|
||||
// Call the provider resolver to seal the promise's fate
|
||||
try {
|
||||
@ -409,29 +459,34 @@ define(function () {
|
||||
}
|
||||
|
||||
// Return the promise
|
||||
return new Promise(then);
|
||||
return self;
|
||||
|
||||
/**
|
||||
* Register handlers for this promise.
|
||||
* @param [onFulfilled] {Function} fulfillment handler
|
||||
* @param [onRejected] {Function} rejection handler
|
||||
* @param [onProgress] {Function} progress handler
|
||||
* @return {Promise} new Promise
|
||||
* Private message delivery. Queues and delivers messages to
|
||||
* the promise's ultimate fulfillment value or rejection reason.
|
||||
* @private
|
||||
* @param {String} type
|
||||
* @param {Array} args
|
||||
* @param {Function} resolve
|
||||
* @param {Function} notify
|
||||
*/
|
||||
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 _message(type, args, resolve, notify) {
|
||||
consumers ? consumers.push(deliver) : enqueue(function() { deliver(value); });
|
||||
|
||||
function deliver(p) {
|
||||
p._message(type, args, resolve, notify);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the promise's state at the instant inspect()
|
||||
* is called. The returned object is not live and will not update as
|
||||
* the promise's state changes.
|
||||
* @returns {{ state:String, value?:*, reason?:* }} status snapshot
|
||||
* of the promise.
|
||||
*/
|
||||
function inspect() {
|
||||
return value ? value.inspect() : toPendingState();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -440,14 +495,17 @@ define(function () {
|
||||
* @param {*|Promise} val resolution value
|
||||
*/
|
||||
function promiseResolve(val) {
|
||||
if(!handlers) {
|
||||
if(!consumers) {
|
||||
return;
|
||||
}
|
||||
|
||||
value = coerce(val);
|
||||
scheduleHandlers(handlers, value);
|
||||
scheduleConsumers(consumers, value);
|
||||
consumers = undef;
|
||||
|
||||
handlers = undef;
|
||||
if(status) {
|
||||
updateStatus(value, status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -463,27 +521,90 @@ define(function () {
|
||||
* @param {*} update progress event payload to pass to all listeners
|
||||
*/
|
||||
function promiseNotify(update) {
|
||||
if(handlers) {
|
||||
scheduleHandlers(handlers, progressing(update));
|
||||
if(consumers) {
|
||||
scheduleConsumers(consumers, progressed(update));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fulfilled, local promise as a proxy for a value
|
||||
* NOTE: must never be exposed
|
||||
* @param {*} value fulfillment value
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function fulfilled(value) {
|
||||
return near(
|
||||
new NearFulfilledProxy(value),
|
||||
function() { return toFulfilledState(value); }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rejected, local promise with the supplied reason
|
||||
* NOTE: must never be exposed
|
||||
* @param {*} reason rejection reason
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function rejected(reason) {
|
||||
return near(
|
||||
new NearRejectedProxy(reason),
|
||||
function() { return toRejectedState(reason); }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a near promise using the provided proxy
|
||||
* NOTE: must never be exposed
|
||||
* @param {object} proxy proxy for the promise's ultimate value or reason
|
||||
* @param {function} inspect function that returns a snapshot of the
|
||||
* returned near promise's state
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function near(proxy, inspect) {
|
||||
return new Promise(function (type, args, resolve) {
|
||||
try {
|
||||
resolve(proxy[type].apply(proxy, args));
|
||||
} catch(e) {
|
||||
resolve(rejected(e));
|
||||
}
|
||||
}, inspect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a progress promise with the supplied update.
|
||||
* @private
|
||||
* @param {*} update
|
||||
* @return {Promise} progress promise
|
||||
*/
|
||||
function progressed(update) {
|
||||
return new Promise(function (type, args, _, notify) {
|
||||
var onProgress = args[2];
|
||||
try {
|
||||
notify(typeof onProgress === 'function' ? onProgress(update) : update);
|
||||
} catch(e) {
|
||||
notify(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerces x to a trusted Promise
|
||||
*
|
||||
* @private
|
||||
* @param {*} x thing to coerce
|
||||
* @returns {Promise} Guaranteed to return a trusted Promise. If x
|
||||
* @returns {*} 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) {
|
||||
if (x instanceof Promise) {
|
||||
return x;
|
||||
} else if (x !== Object(x)) {
|
||||
}
|
||||
|
||||
if (!(x === Object(x) && 'then' in x)) {
|
||||
return fulfilled(x);
|
||||
}
|
||||
|
||||
@ -510,61 +631,34 @@ define(function () {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an already-fulfilled promise for the supplied value
|
||||
* @private
|
||||
* Proxy for a near, fulfilled value
|
||||
* @param {*} value
|
||||
* @return {Promise} fulfilled promise
|
||||
* @constructor
|
||||
*/
|
||||
function fulfilled(value) {
|
||||
var self = new Promise(function (onFulfilled) {
|
||||
try {
|
||||
return typeof onFulfilled == 'function'
|
||||
? coerce(onFulfilled(value)) : self;
|
||||
} catch (e) {
|
||||
return rejected(e);
|
||||
}
|
||||
});
|
||||
|
||||
return self;
|
||||
function NearFulfilledProxy(value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
NearFulfilledProxy.prototype.when = function(onResult) {
|
||||
return typeof onResult === 'function' ? onResult(this.value) : this.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an already-rejected promise with the supplied rejection reason.
|
||||
* @private
|
||||
* Proxy for a near rejection
|
||||
* @param {*} reason
|
||||
* @return {Promise} rejected promise
|
||||
* @constructor
|
||||
*/
|
||||
function rejected(reason) {
|
||||
var self = new Promise(function (_, onRejected) {
|
||||
try {
|
||||
return typeof onRejected == 'function'
|
||||
? coerce(onRejected(reason)) : self;
|
||||
} catch (e) {
|
||||
return rejected(e);
|
||||
}
|
||||
});
|
||||
|
||||
return self;
|
||||
function NearRejectedProxy(reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
NearRejectedProxy.prototype.when = function(_, onError) {
|
||||
if(typeof onError === 'function') {
|
||||
return onError(this.reason);
|
||||
} else {
|
||||
throw this.reason;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedule a task that will process a list of handlers
|
||||
@ -573,7 +667,7 @@ define(function () {
|
||||
* @param {Array} handlers queue of handlers to execute
|
||||
* @param {*} value passed as the only arg to each handler
|
||||
*/
|
||||
function scheduleHandlers(handlers, value) {
|
||||
function scheduleConsumers(handlers, value) {
|
||||
enqueue(function() {
|
||||
var handler, i = 0;
|
||||
while (handler = handlers[i++]) {
|
||||
@ -582,14 +676,23 @@ define(function () {
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatus(value, status) {
|
||||
value.then(statusFulfilled, statusRejected);
|
||||
|
||||
function statusFulfilled() { status.fulfilled(); }
|
||||
function statusRejected(r) { status.rejected(r); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if promiseOrValue is a promise or not
|
||||
*
|
||||
* @param {*} promiseOrValue anything
|
||||
* @returns {boolean} true if promiseOrValue is a {@link Promise}
|
||||
* Determines if x is promise-like, i.e. a thenable object
|
||||
* NOTE: Will return true for *any thenable object*, and isn't truly
|
||||
* safe, since it may attempt to access the `then` property of x (i.e.
|
||||
* clever/malicious getters may do weird things)
|
||||
* @param {*} x anything
|
||||
* @returns {boolean} true if x is promise-like
|
||||
*/
|
||||
function isPromise(promiseOrValue) {
|
||||
return promiseOrValue && typeof promiseOrValue.then === 'function';
|
||||
function isPromiseLike(x) {
|
||||
return x && typeof x.then === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -601,17 +704,15 @@ define(function () {
|
||||
* @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] resolution handler
|
||||
* @param {function?} [onRejected] rejection handler
|
||||
* @param {function?} [onProgress] progress handler
|
||||
* @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) {
|
||||
|
||||
checkCallbacks(2, arguments);
|
||||
|
||||
return when(promisesOrValues, function(promisesOrValues) {
|
||||
|
||||
return promise(resolveSome).then(onFulfilled, onRejected, onProgress);
|
||||
@ -635,7 +736,7 @@ define(function () {
|
||||
rejectOne = function(reason) {
|
||||
reasons.push(reason);
|
||||
if(!--toReject) {
|
||||
fulfillOne = rejectOne = noop;
|
||||
fulfillOne = rejectOne = identity;
|
||||
reject(reasons);
|
||||
}
|
||||
};
|
||||
@ -644,7 +745,7 @@ define(function () {
|
||||
// This orders the values based on promise resolution order
|
||||
values.push(val);
|
||||
if (!--toResolve) {
|
||||
fulfillOne = rejectOne = noop;
|
||||
fulfillOne = rejectOne = identity;
|
||||
resolve(values);
|
||||
}
|
||||
};
|
||||
@ -674,9 +775,9 @@ define(function () {
|
||||
*
|
||||
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix
|
||||
* of {@link Promise}s and values
|
||||
* @param {function?} [onFulfilled] resolution handler
|
||||
* @param {function?} [onRejected] rejection handler
|
||||
* @param {function?} [onProgress] progress handler
|
||||
* @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.
|
||||
*/
|
||||
@ -697,14 +798,13 @@ define(function () {
|
||||
*
|
||||
* @param {Array|Promise} promisesOrValues array of anything, may contain a mix
|
||||
* of {@link Promise}s and values
|
||||
* @param {function?} [onFulfilled] resolution handler
|
||||
* @param {function?} [onRejected] rejection handler
|
||||
* @param {function?} [onProgress] progress handler
|
||||
* @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) {
|
||||
checkCallbacks(1, arguments);
|
||||
return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
|
||||
return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -713,28 +813,49 @@ define(function () {
|
||||
* have fulfilled, or will reject when *any one* of the input promises rejects.
|
||||
*/
|
||||
function join(/* ...promises */) {
|
||||
return map(arguments, identity);
|
||||
return _map(arguments, identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traditional map function, similar to `Array.prototype.map()`, but allows
|
||||
* input to contain {@link Promise}s and/or values, and mapFunc may return
|
||||
* either a value or a {@link Promise}
|
||||
*
|
||||
* @param {Array|Promise} array array of anything, may contain a mix
|
||||
* of {@link Promise}s and values
|
||||
* @param {function} mapFunc mapping function mapFunc(value) which may return
|
||||
* either a {@link Promise} or value
|
||||
* @returns {Promise} a {@link Promise} that will resolve to an array containing
|
||||
* the mapped output values.
|
||||
* 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);
|
||||
return _promise(resolveMap);
|
||||
|
||||
function resolveMap(resolve, reject, notify) {
|
||||
var results, len, toResolve, resolveOne, i;
|
||||
var results, len, toResolve, i;
|
||||
|
||||
// Since we know the resulting length, we can preallocate the results
|
||||
// array to avoid array expansions.
|
||||
@ -743,27 +864,28 @@ define(function () {
|
||||
|
||||
if(!toResolve) {
|
||||
resolve(results);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
resolveOne = function(item, i) {
|
||||
when(item, mapFunc).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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveOne(item, i) {
|
||||
when(item, mapFunc, fallback).then(function(mapped) {
|
||||
results[i] = mapped;
|
||||
notify(mapped);
|
||||
|
||||
if(!--toResolve) {
|
||||
resolve(results);
|
||||
}
|
||||
}, reject);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -803,12 +925,46 @@ define(function () {
|
||||
});
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Internals, utilities, etc.
|
||||
//
|
||||
|
||||
var reduceArray, slice, fcall, nextTick, handlerQueue,
|
||||
timeout, funcProto, call, arrayProto, undef;
|
||||
setTimeout, funcProto, call, arrayProto, monitorApi,
|
||||
cjsRequire, undef;
|
||||
|
||||
cjsRequire = require;
|
||||
|
||||
//
|
||||
// Shared handler queue processing
|
||||
@ -826,20 +982,13 @@ define(function () {
|
||||
*/
|
||||
function enqueue(task) {
|
||||
if(handlerQueue.push(task) === 1) {
|
||||
scheduleDrainQueue();
|
||||
nextTick(drainQueue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the queue to be drained in the next tick.
|
||||
*/
|
||||
function scheduleDrainQueue() {
|
||||
nextTick(drainQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drain the handler queue entirely or partially, being careful to allow
|
||||
* the queue to be extended while it is being processed, and to continue
|
||||
* 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() {
|
||||
@ -852,20 +1001,36 @@ define(function () {
|
||||
handlerQueue = [];
|
||||
}
|
||||
|
||||
//
|
||||
// Capture function and array utils
|
||||
//
|
||||
/*global setImmediate:true*/
|
||||
// capture setTimeout to avoid being caught by fake timers
|
||||
// used in time based tests
|
||||
setTimeout = global.setTimeout;
|
||||
|
||||
// capture setTimeout to avoid being caught by fake timers used in time based tests
|
||||
timeout = setTimeout;
|
||||
nextTick = typeof setImmediate === 'function'
|
||||
? typeof window === 'undefined'
|
||||
? setImmediate
|
||||
: setImmediate.bind(window)
|
||||
: typeof process === 'object'
|
||||
? process.nextTick
|
||||
: function(task) { timeout(task, 0); };
|
||||
// Allow attaching the monitor to when() if env has no console
|
||||
monitorApi = typeof console != 'undefined' ? console : when;
|
||||
|
||||
// Prefer setImmediate or MessageChannel, cascade to node,
|
||||
// vertx and finally setTimeout
|
||||
/*global setImmediate,MessageChannel,process*/
|
||||
if (typeof setImmediate === 'function') {
|
||||
nextTick = setImmediate.bind(global);
|
||||
} else if(typeof MessageChannel !== 'undefined') {
|
||||
var channel = new MessageChannel();
|
||||
channel.port1.onmessage = drainQueue;
|
||||
nextTick = function() { channel.port2.postMessage(0); };
|
||||
} else if (typeof process === 'object' && process.nextTick) {
|
||||
nextTick = process.nextTick;
|
||||
} else {
|
||||
try {
|
||||
// vert.x 1.x || 2.x
|
||||
nextTick = cjsRequire('vertx').runOnLoop || cjsRequire('vertx').runOnContext;
|
||||
} catch(ignore) {
|
||||
nextTick = function(t) { setTimeout(t, 0); };
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Capture/polyfill function and array utils
|
||||
//
|
||||
|
||||
// Safe function calls
|
||||
funcProto = Function.prototype;
|
||||
@ -926,43 +1091,13 @@ define(function () {
|
||||
return reduced;
|
||||
};
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Helper that checks arrayOfCallbacks to ensure that each element is either
|
||||
* a function, or null or undefined.
|
||||
* @private
|
||||
* @param {number} start index at which to start checking items in arrayOfCallbacks
|
||||
* @param {Array} arrayOfCallbacks array to check
|
||||
* @throws {Error} if any element of arrayOfCallbacks is something other than
|
||||
* a functions, null, or undefined.
|
||||
*/
|
||||
function checkCallbacks(start, arrayOfCallbacks) {
|
||||
// TODO: Promises/A+ update type checking and docs
|
||||
var arg, i = arrayOfCallbacks.length;
|
||||
|
||||
while(i > start) {
|
||||
arg = arrayOfCallbacks[--i];
|
||||
|
||||
if (arg != null && typeof arg != 'function') {
|
||||
throw new Error('arg '+i+' must be a function');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
return when;
|
||||
});
|
||||
})(
|
||||
typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(); }
|
||||
);
|
||||
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }, this);
|
||||
|
||||
if (typeof module === "object" && typeof require === "function") {
|
||||
var bane = require("bane");
|
||||
|
||||
4
mopidy/frontends/http/data/mopidy.min.js
vendored
4
mopidy/frontends/http/data/mopidy.min.js
vendored
File diff suppressed because one or more lines are too long
@ -90,7 +90,7 @@ class ImmutableObject(object):
|
||||
for v in value]
|
||||
elif isinstance(value, ImmutableObject):
|
||||
value = value.serialize()
|
||||
if value:
|
||||
if not (isinstance(value, list) and len(value) == 0):
|
||||
data[public_key] = value
|
||||
return data
|
||||
|
||||
|
||||
@ -105,11 +105,11 @@ class StringTest(unittest.TestCase):
|
||||
|
||||
|
||||
class SecretTest(unittest.TestCase):
|
||||
def test_deserialize_passes_through(self):
|
||||
def test_deserialize_decodes_utf8(self):
|
||||
value = types.Secret()
|
||||
result = value.deserialize(b'foo')
|
||||
self.assertIsInstance(result, bytes)
|
||||
self.assertEqual(b'foo', result)
|
||||
result = value.deserialize('æøå'.encode('utf-8'))
|
||||
self.assertIsInstance(result, unicode)
|
||||
self.assertEqual('æøå', result)
|
||||
|
||||
def test_deserialize_enforces_required(self):
|
||||
value = types.Secret()
|
||||
|
||||
@ -12,7 +12,10 @@ class HelpTest(unittest.TestCase):
|
||||
def test_help_has_mopidy_options(self):
|
||||
mopidy_dir = os.path.dirname(mopidy.__file__)
|
||||
args = [sys.executable, mopidy_dir, '--help']
|
||||
process = subprocess.Popen(args, stdout=subprocess.PIPE)
|
||||
process = subprocess.Popen(
|
||||
args,
|
||||
env={'PYTHONPATH': os.path.join(mopidy_dir, '..')},
|
||||
stdout=subprocess.PIPE)
|
||||
output = process.communicate()[0]
|
||||
self.assertIn('--version', output)
|
||||
self.assertIn('--help', output)
|
||||
|
||||
@ -95,6 +95,11 @@ class ArtistTest(unittest.TestCase):
|
||||
{'__model__': 'Artist', 'uri': 'uri', 'name': 'name'},
|
||||
Artist(uri='uri', name='name').serialize())
|
||||
|
||||
def test_serialize_falsy_values(self):
|
||||
self.assertDictEqual(
|
||||
{'__model__': 'Artist', 'uri': '', 'name': None},
|
||||
Artist(uri='', name=None).serialize())
|
||||
|
||||
def test_to_json_and_back(self):
|
||||
artist1 = Artist(uri='uri', name='name')
|
||||
serialized = json.dumps(artist1, cls=ModelJSONEncoder)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user