diff --git a/js/lib/when-1.6.1.js b/js/lib/when-1.8.1.js similarity index 60% rename from js/lib/when-1.6.1.js rename to js/lib/when-1.8.1.js index e9a3bfc3..05c5a429 100644 --- a/js/lib/when-1.6.1.js +++ b/js/lib/when-1.8.1.js @@ -1,4 +1,4 @@ -/** @license MIT License (c) copyright B Cavalier & J Hann */ +/** @license MIT License (c) copyright 2011-2013 original author or authors */ /** * A lightweight CommonJS Promises/A and when() implementation @@ -7,11 +7,14 @@ * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * - * @version 1.6.1 + * @author Brian Cavalier + * @author John Hann + * + * @version 1.8.1 */ (function(define) { 'use strict'; -define(['module'], function () { +define(function () { var reduceArray, slice, undef; // @@ -25,110 +28,106 @@ define(['module'], function () { when.join = join; // Join 2 or more promises when.all = all; // Resolve a list of promises - when.some = some; // Resolve a sub-set of promises - when.any = any; // Resolve one promise in a list - when.map = map; // Array.map() for promises when.reduce = reduce; // Array.reduce() for promises + when.any = any; // One-winner race + when.some = some; // Multi-winner race + when.chain = chain; // Make a promise trigger another resolver when.isPromise = isPromise; // Determine if a thing is a promise /** * Register an observer for a promise or immediate value. - * @function - * @name when - * @namespace * - * @param promiseOrValue {*} - * @param {Function} [callback] callback to be called when promiseOrValue is + * @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} [errback] callback to be called when promiseOrValue is + * @param {function?} [onRejected] callback to be called when promiseOrValue is * rejected. - * @param {Function} [progressHandler] callback to be called when progress updates + * @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, callback, errback, progressHandler) { + function when(promiseOrValue, onFulfilled, onRejected, onProgress) { // Get a trusted promise for the input promiseOrValue, and then // register promise handlers - return resolve(promiseOrValue).then(callback, errback, progressHandler); + return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); } /** * Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if * promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} * whose value is promiseOrValue if promiseOrValue is an immediate value. - * @memberOf when * - * @param promiseOrValue {*} - * @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} - * returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} - * whose resolution value is: + * @param {*} promiseOrValue + * @returns {Promise} Guaranteed to return a trusted Promise. If promiseOrValue + * is trusted, returns promiseOrValue, otherwise, returns a new, already-resolved + * when.js promise whose resolution value is: * * the resolution value of promiseOrValue if it's a foreign promise, or * * promiseOrValue if it's a value */ function resolve(promiseOrValue) { - var promise, deferred; + var promise; if(promiseOrValue instanceof Promise) { // It's a when.js promise, so we trust it promise = promiseOrValue; + } else if(isPromise(promiseOrValue)) { + // Assimilate foreign promises + promise = assimilate(promiseOrValue); } else { - // It's not a when.js promise. See if it's a foreign promise or a value. - - // Some promises, particularly Q promises, provide a valueOf method that - // attempts to synchronously return the fulfilled value of the promise, or - // returns the unresolved promise itself. Attempting to break a fulfillment - // value out of a promise appears to be necessary to break cycles between - // Q and When attempting to coerce each-other's promises in an infinite loop. - // For promises that do not implement "valueOf", the Object#valueOf is harmless. - // See: https://github.com/kriskowal/q/issues/106 - // IMPORTANT: Must check for a promise here, since valueOf breaks other things - // like Date. - if (isPromise(promiseOrValue) && typeof promiseOrValue.valueOf === 'function') { - promiseOrValue = promiseOrValue.valueOf(); - } - - if(isPromise(promiseOrValue)) { - // It looks like a thenable, but we don't know where it came from, - // so we don't trust its implementation entirely. Introduce a trusted - // middleman when.js promise - deferred = defer(); - - // IMPORTANT: This is the only place when.js should ever call .then() on - // an untrusted promise. - promiseOrValue.then(deferred.resolve, deferred.reject, deferred.progress); - promise = deferred.promise; - - } else { - // It's a value, not a promise. Create a resolved promise for it. - promise = fulfilled(promiseOrValue); - } + // It's a value, create a fulfilled promise for it. + promise = fulfilled(promiseOrValue); } return promise; } /** - * Returns a rejected promise for the supplied promiseOrValue. If - * promiseOrValue is a value, it will be the rejection value of the - * returned promise. If promiseOrValue is a promise, its - * completion value will be the rejected value of the returned promise - * @memberOf when + * Assimilate an untrusted thenable by introducing a trusted middle man. + * Not a perfect strategy, but possibly the best we can do. + * IMPORTANT: This is the only place when.js should ever call an untrusted + * thenable's then() on an. Don't expose the return value to the untrusted thenable * - * @param promiseOrValue {*} the rejected value of the returned {@link Promise} + * @param {*} thenable + * @param {function} thenable.then + * @returns {Promise} + */ + function assimilate(thenable) { + var d = defer(); + + // TODO: Enqueue this for future execution in 2.0 + try { + thenable.then( + function(value) { d.resolve(value); }, + function(reason) { d.reject(reason); }, + function(update) { d.progress(update); } + ); + } catch(e) { + d.reject(e); + } + + return d.promise; + } + + /** + * 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, function(value) { - return rejected(value); - }); + return when(promiseOrValue, rejected); } /** @@ -145,25 +144,53 @@ define(['module'], function () { Promise.prototype = { /** * Register a callback that will be called when a promise is - * resolved or rejected. Optionally also register a progress handler. - * Shortcut for .then(alwaysback, alwaysback, progback) - * @memberOf Promise - * @param alwaysback {Function} - * @param progback {Function} + * fulfilled or rejected. Optionally also register a progress handler. + * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) + * @param {function?} [onFulfilledOrRejected] + * @param {function?} [onProgress] * @return {Promise} */ - always: function(alwaysback, progback) { - return this.then(alwaysback, alwaysback, progback); + always: function(onFulfilledOrRejected, onProgress) { + return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); }, /** - * Register a rejection handler. Shortcut for .then(null, errback) - * @memberOf Promise - * @param errback {Function} + * Register a rejection handler. Shortcut for .then(undefined, onRejected) + * @param {function?} onRejected * @return {Promise} */ - otherwise: function(errback) { - return this.then(undef, errback); + otherwise: function(onRejected) { + return this.then(undef, onRejected); + }, + + /** + * 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); + }); + }); } }; @@ -171,13 +198,13 @@ define(['module'], function () { * Create an already-resolved promise for the supplied value * @private * - * @param value anything - * @return {Promise} + * @param {*} value + * @return {Promise} fulfilled promise */ function fulfilled(value) { - var p = new Promise(function(callback) { + var p = new Promise(function(onFulfilled) { try { - return resolve(callback ? callback(value) : value); + return resolve(typeof onFulfilled == 'function' ? onFulfilled(value) : value); } catch(e) { return rejected(e); } @@ -191,13 +218,13 @@ define(['module'], function () { * rejection reason. * @private * - * @param reason rejection reason - * @return {Promise} + * @param {*} reason + * @return {Promise} rejected promise */ function rejected(reason) { - var p = new Promise(function(callback, errback) { + var p = new Promise(function(_, onRejected) { try { - return errback ? resolve(errback(reason)) : rejected(reason); + return resolve(typeof onRejected == 'function' ? onRejected(reason) : rejected(reason)); } catch(e) { return rejected(e); } @@ -212,14 +239,12 @@ define(['module'], function () { * The Deferred itself has the full API: resolve, reject, progress, and * then. The resolver has resolve, reject, and progress. The promise * only has then. - * @memberOf when - * @function * * @return {Deferred} */ function defer() { var deferred, promise, handlers, progressHandlers, - _then, _progress, _resolve; + _then, _notify, _resolve; /** * The promise for the new deferred @@ -233,18 +258,19 @@ define(['module'], function () { * @name Deferred */ deferred = { - then: then, + then: then, // DEPRECATED: use deferred.promise.then resolve: promiseResolve, reject: promiseReject, - // TODO: Consider renaming progress() to notify() - progress: promiseProgress, + progress: promiseNotify, // DEPRECATED: use deferred.notify + notify: promiseNotify, promise: promise, resolver: { resolve: promiseResolve, reject: promiseReject, - progress: promiseProgress + progress: promiseNotify, // DEPRECATED: use deferred.notify + notify: promiseNotify } }; @@ -256,29 +282,29 @@ define(['module'], function () { * functions to the registered listeners * @private * - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler - * @throws {Error} if any argument is not null, undefined, or a Function + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler */ - _then = function(callback, errback, progback) { + _then = function(onFulfilled, onRejected, onProgress) { var deferred, progressHandler; deferred = defer(); - progressHandler = progback + + progressHandler = typeof onProgress === 'function' ? function(update) { try { // Allow progress handler to transform progress event - deferred.progress(progback(update)); + deferred.notify(onProgress(update)); } catch(e) { // Use caught value as progress - deferred.progress(e); + deferred.notify(e); } } - : deferred.progress; + : function(update) { deferred.notify(update); }; handlers.push(function(promise) { - promise.then(callback, errback) + promise.then(onFulfilled, onRejected) .then(deferred.resolve, deferred.reject, progressHandler); }); @@ -290,9 +316,9 @@ define(['module'], function () { /** * Issue a progress event, notifying all progress listeners * @private - * @param update {*} progress event payload to pass to all listeners + * @param {*} update progress event payload to pass to all listeners */ - _progress = function(update) { + _notify = function(update) { processQueue(progressHandlers, update); return update; }; @@ -301,61 +327,58 @@ define(['module'], function () { * Transition from pre-resolution state to post-resolution state, notifying * all listeners of the resolution or rejection * @private - * @param completed {Promise} the completed value of this deferred + * @param {*} value the value of this deferred */ - _resolve = function(completed) { - completed = resolve(completed); - + _resolve = function(value) { // Replace _then with one that directly notifies with the result. - _then = completed.then; - // Replace _resolve so that this Deferred can only be completed once + _then = value.then; + // Replace _resolve so that this Deferred can only be resolved once _resolve = resolve; // Make _progress a noop, to disallow progress for the resolved promise. - _progress = noop; + _notify = identity; // Notify handlers - processQueue(handlers, completed); + processQueue(handlers, value); // Free progressHandlers array since we'll never issue progress events progressHandlers = handlers = undef; - return completed; + return value; }; return deferred; /** * Wrapper to allow _then to be replaced safely - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler - * @return {Promise} new Promise - * @throws {Error} if any argument is not null, undefined, or a Function + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler + * @return {Promise} new promise */ - function then(callback, errback, progback) { - return _then(callback, errback, progback); + function then(onFulfilled, onRejected, onProgress) { + // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress + return _then(onFulfilled, onRejected, onProgress); } /** * Wrapper to allow _resolve to be replaced */ function promiseResolve(val) { - return _resolve(val); + return _resolve(resolve(val)); } /** - * Wrapper to allow _resolve to be replaced + * Wrapper to allow _reject to be replaced */ function promiseReject(err) { return _resolve(rejected(err)); } /** - * Wrapper to allow _progress to be replaced - * @param {*} update progress update + * Wrapper to allow _notify to be replaced */ - function promiseProgress(update) { - return _progress(update); + function promiseNotify(update) { + return _notify(update); } } @@ -365,7 +388,7 @@ define(['module'], function () { * promiseOrValue is a promise. * * @param {*} promiseOrValue anything - * @returns {Boolean} true if promiseOrValue is a {@link Promise} + * @returns {boolean} true if promiseOrValue is a {@link Promise} */ function isPromise(promiseOrValue) { return promiseOrValue && typeof promiseOrValue.then === 'function'; @@ -376,25 +399,24 @@ define(['module'], function () { * 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. - * @memberOf when * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param howMany {Number} number of promisesOrValues to resolve - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler + * @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 * @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, callback, errback, progback) { + function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { checkCallbacks(2, arguments); return when(promisesOrValues, function(promisesOrValues) { - var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; + var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, notify, len, i; len = promisesOrValues.length >>> 0; @@ -411,7 +433,7 @@ define(['module'], function () { deferred.resolve(values); } else { - progress = deferred.progress; + notify = deferred.notify; rejectOne = function(reason) { reasons.push(reason); @@ -435,12 +457,12 @@ define(['module'], function () { for(i = 0; i < len; ++i) { if(i in promisesOrValues) { - when(promisesOrValues[i], fulfiller, rejecter, progress); + when(promisesOrValues[i], fulfiller, rejecter, notify); } } } - return deferred.then(callback, errback, progback); + return deferred.promise.then(onFulfilled, onRejected, onProgress); function rejecter(reason) { rejectOne(reason); @@ -457,23 +479,22 @@ define(['module'], function () { * 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. - * @memberOf when * - * @param promisesOrValues {Array|Promise} array of anything, may contain a mix + * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler * @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, callback, errback, progback) { + function any(promisesOrValues, onFulfilled, onRejected, onProgress) { function unwrapSingleResult(val) { - return callback ? callback(val[0]) : val[0]; + return onFulfilled ? onFulfilled(val[0]) : val[0]; } - return some(promisesOrValues, 1, unwrapSingleResult, errback, progback); + return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); } /** @@ -482,22 +503,20 @@ define(['module'], function () { * containing the resolution values of each of the promisesOrValues. * @memberOf when * - * @param promisesOrValues {Array|Promise} array of anything, may contain a mix + * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values - * @param [callback] {Function} - * @param [errback] {Function} - * @param [progressHandler] {Function} + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler * @returns {Promise} */ - function all(promisesOrValues, callback, errback, progressHandler) { + function all(promisesOrValues, onFulfilled, onRejected, onProgress) { checkCallbacks(1, arguments); - return map(promisesOrValues, identity).then(callback, errback, progressHandler); + return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); } /** * Joins multiple promises into a single returned promise. - * @memberOf when - * @param {Promise|*} [...promises] two or more promises to join * @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. */ @@ -510,18 +529,16 @@ define(['module'], function () { * input to contain {@link Promise}s and/or values, and mapFunc may return * either a value or a {@link Promise} * - * @memberOf when - * - * @param promise {Array|Promise} array of anything, may contain a mix + * @param {Array|Promise} promise array of anything, may contain a mix * of {@link Promise}s and values - * @param mapFunc {Function} mapping function mapFunc(value) which may return + * @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. */ function map(promise, mapFunc) { return when(promise, function(array) { - var results, len, toResolve, resolve, reject, i, d; + var results, len, toResolve, resolve, i, d; // Since we know the resulting length, we can preallocate the results // array to avoid array expansions. @@ -533,7 +550,6 @@ define(['module'], function () { d.resolve(results); } else { - reject = d.reject; resolve = function resolveOne(item, i) { when(item, mapFunc).then(function(mapped) { results[i] = mapped; @@ -541,7 +557,7 @@ define(['module'], function () { if(!--toResolve) { d.resolve(results); } - }, reject); + }, d.reject); }; // Since mapFunc may be async, get all invocations of it into flight @@ -562,18 +578,15 @@ define(['module'], function () { /** * Traditional reduce function, similar to `Array.prototype.reduce()`, but - * input may contain {@link Promise}s and/or values, and reduceFunc - * may return either a value or a {@link Promise}, *and* initialValue may - * be a {@link Promise} for the starting value. - * @memberOf when + * 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 promise {Array|Promise} array of anything, may contain a mix - * of {@link Promise}s and values. May also be a {@link Promise} for - * an array. - * @param reduceFunc {Function} reduce function reduce(currentValue, nextValue, index, total), + * @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. - * @param [initialValue] {*} starting value, or a {@link Promise} for the starting value * @returns {Promise} that will resolve to the final reduced value */ function reduce(promise, reduceFunc /*, initialValue */) { @@ -599,13 +612,14 @@ define(['module'], function () { } /** - * Ensure that resolution of promiseOrValue will complete resolver with the completion - * value of promiseOrValue, or instead with resolveValue if it is provided. - * @memberOf when + * Ensure that resolution of promiseOrValue will trigger resolver with the + * value or reason of promiseOrValue, or instead with resolveValue if it is provided. * * @param promiseOrValue - * @param resolver {Resolver} - * @param [resolveValue] anything + * @param {Object} resolver + * @param {function} resolver.resolve + * @param {function} resolver.reject + * @param {*} [resolveValue] * @returns {Promise} */ function chain(promiseOrValue, resolver, resolveValue) { @@ -613,10 +627,18 @@ define(['module'], function () { return when(promiseOrValue, function(val) { - return resolver.resolve(useResolveValue ? resolveValue : val); + val = useResolveValue ? resolveValue : val; + resolver.resolve(val); + return val; }, - resolver.reject, - resolver.progress + function(reason) { + resolver.reject(reason); + return rejected(reason); + }, + function(update) { + typeof resolver.notify === 'function' && resolver.notify(update); + return update; + } ); } @@ -624,6 +646,11 @@ define(['module'], function () { // Utility functions // + /** + * Apply all functions in queue to value + * @param {Array} queue array of functions to execute + * @param {*} value argument passed to each function + */ function processQueue(queue, value) { var handler, i = 0; @@ -636,12 +663,13 @@ define(['module'], function () { * Helper that checks arrayOfCallbacks to ensure that each element is either * a function, or null or undefined. * @private - * - * @param arrayOfCallbacks {Array} array to check + * @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. + * 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) { @@ -723,7 +751,7 @@ define(['module'], function () { }); })(typeof define == 'function' && define.amd ? define - : function (deps, factory) { typeof exports === 'object' + : function (factory) { typeof exports === 'object' ? (module.exports = factory()) : (this.when = factory()); } diff --git a/mopidy/frontends/http/data/mopidy.js b/mopidy/frontends/http/data/mopidy.js index 449e43b3..20e43014 100644 --- a/mopidy/frontends/http/data/mopidy.js +++ b/mopidy/frontends/http/data/mopidy.js @@ -173,11 +173,14 @@ * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * - * @version 1.6.1 + * @author Brian Cavalier + * @author John Hann + * + * @version 1.8.1 */ (function(define) { 'use strict'; -define(['module'], function () { +define(function () { var reduceArray, slice, undef; // @@ -191,110 +194,106 @@ define(['module'], function () { when.join = join; // Join 2 or more promises when.all = all; // Resolve a list of promises - when.some = some; // Resolve a sub-set of promises - when.any = any; // Resolve one promise in a list - when.map = map; // Array.map() for promises when.reduce = reduce; // Array.reduce() for promises + when.any = any; // One-winner race + when.some = some; // Multi-winner race + when.chain = chain; // Make a promise trigger another resolver when.isPromise = isPromise; // Determine if a thing is a promise /** * Register an observer for a promise or immediate value. - * @function - * @name when - * @namespace * - * @param promiseOrValue {*} - * @param {Function} [callback] callback to be called when promiseOrValue is + * @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} [errback] callback to be called when promiseOrValue is + * @param {function?} [onRejected] callback to be called when promiseOrValue is * rejected. - * @param {Function} [progressHandler] callback to be called when progress updates + * @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, callback, errback, progressHandler) { + function when(promiseOrValue, onFulfilled, onRejected, onProgress) { // Get a trusted promise for the input promiseOrValue, and then // register promise handlers - return resolve(promiseOrValue).then(callback, errback, progressHandler); + return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); } /** * Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if * promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} * whose value is promiseOrValue if promiseOrValue is an immediate value. - * @memberOf when * - * @param promiseOrValue {*} - * @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} - * returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} - * whose resolution value is: + * @param {*} promiseOrValue + * @returns {Promise} Guaranteed to return a trusted Promise. If promiseOrValue + * is trusted, returns promiseOrValue, otherwise, returns a new, already-resolved + * when.js promise whose resolution value is: * * the resolution value of promiseOrValue if it's a foreign promise, or * * promiseOrValue if it's a value */ function resolve(promiseOrValue) { - var promise, deferred; + var promise; if(promiseOrValue instanceof Promise) { // It's a when.js promise, so we trust it promise = promiseOrValue; + } else if(isPromise(promiseOrValue)) { + // Assimilate foreign promises + promise = assimilate(promiseOrValue); } else { - // It's not a when.js promise. See if it's a foreign promise or a value. - - // Some promises, particularly Q promises, provide a valueOf method that - // attempts to synchronously return the fulfilled value of the promise, or - // returns the unresolved promise itself. Attempting to break a fulfillment - // value out of a promise appears to be necessary to break cycles between - // Q and When attempting to coerce each-other's promises in an infinite loop. - // For promises that do not implement "valueOf", the Object#valueOf is harmless. - // See: https://github.com/kriskowal/q/issues/106 - // IMPORTANT: Must check for a promise here, since valueOf breaks other things - // like Date. - if (isPromise(promiseOrValue) && typeof promiseOrValue.valueOf === 'function') { - promiseOrValue = promiseOrValue.valueOf(); - } - - if(isPromise(promiseOrValue)) { - // It looks like a thenable, but we don't know where it came from, - // so we don't trust its implementation entirely. Introduce a trusted - // middleman when.js promise - deferred = defer(); - - // IMPORTANT: This is the only place when.js should ever call .then() on - // an untrusted promise. - promiseOrValue.then(deferred.resolve, deferred.reject, deferred.progress); - promise = deferred.promise; - - } else { - // It's a value, not a promise. Create a resolved promise for it. - promise = fulfilled(promiseOrValue); - } + // It's a value, create a fulfilled promise for it. + promise = fulfilled(promiseOrValue); } return promise; } /** - * Returns a rejected promise for the supplied promiseOrValue. If - * promiseOrValue is a value, it will be the rejection value of the - * returned promise. If promiseOrValue is a promise, its - * completion value will be the rejected value of the returned promise - * @memberOf when + * Assimilate an untrusted thenable by introducing a trusted middle man. + * Not a perfect strategy, but possibly the best we can do. + * IMPORTANT: This is the only place when.js should ever call an untrusted + * thenable's then() on an. Don't expose the return value to the untrusted thenable * - * @param promiseOrValue {*} the rejected value of the returned {@link Promise} + * @param {*} thenable + * @param {function} thenable.then + * @returns {Promise} + */ + function assimilate(thenable) { + var d = defer(); + + // TODO: Enqueue this for future execution in 2.0 + try { + thenable.then( + function(value) { d.resolve(value); }, + function(reason) { d.reject(reason); }, + function(update) { d.progress(update); } + ); + } catch(e) { + d.reject(e); + } + + return d.promise; + } + + /** + * 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, function(value) { - return rejected(value); - }); + return when(promiseOrValue, rejected); } /** @@ -311,25 +310,53 @@ define(['module'], function () { Promise.prototype = { /** * Register a callback that will be called when a promise is - * resolved or rejected. Optionally also register a progress handler. - * Shortcut for .then(alwaysback, alwaysback, progback) - * @memberOf Promise - * @param alwaysback {Function} - * @param progback {Function} + * fulfilled or rejected. Optionally also register a progress handler. + * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) + * @param {function?} [onFulfilledOrRejected] + * @param {function?} [onProgress] * @return {Promise} */ - always: function(alwaysback, progback) { - return this.then(alwaysback, alwaysback, progback); + always: function(onFulfilledOrRejected, onProgress) { + return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); }, /** - * Register a rejection handler. Shortcut for .then(null, errback) - * @memberOf Promise - * @param errback {Function} + * Register a rejection handler. Shortcut for .then(undefined, onRejected) + * @param {function?} onRejected * @return {Promise} */ - otherwise: function(errback) { - return this.then(undef, errback); + otherwise: function(onRejected) { + return this.then(undef, onRejected); + }, + + /** + * 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); + }); + }); } }; @@ -337,13 +364,13 @@ define(['module'], function () { * Create an already-resolved promise for the supplied value * @private * - * @param value anything - * @return {Promise} + * @param {*} value + * @return {Promise} fulfilled promise */ function fulfilled(value) { - var p = new Promise(function(callback) { + var p = new Promise(function(onFulfilled) { try { - return resolve(callback ? callback(value) : value); + return resolve(typeof onFulfilled == 'function' ? onFulfilled(value) : value); } catch(e) { return rejected(e); } @@ -357,13 +384,13 @@ define(['module'], function () { * rejection reason. * @private * - * @param reason rejection reason - * @return {Promise} + * @param {*} reason + * @return {Promise} rejected promise */ function rejected(reason) { - var p = new Promise(function(callback, errback) { + var p = new Promise(function(_, onRejected) { try { - return errback ? resolve(errback(reason)) : rejected(reason); + return resolve(typeof onRejected == 'function' ? onRejected(reason) : rejected(reason)); } catch(e) { return rejected(e); } @@ -378,14 +405,12 @@ define(['module'], function () { * The Deferred itself has the full API: resolve, reject, progress, and * then. The resolver has resolve, reject, and progress. The promise * only has then. - * @memberOf when - * @function * * @return {Deferred} */ function defer() { var deferred, promise, handlers, progressHandlers, - _then, _progress, _resolve; + _then, _notify, _resolve; /** * The promise for the new deferred @@ -399,18 +424,19 @@ define(['module'], function () { * @name Deferred */ deferred = { - then: then, + then: then, // DEPRECATED: use deferred.promise.then resolve: promiseResolve, reject: promiseReject, - // TODO: Consider renaming progress() to notify() - progress: promiseProgress, + progress: promiseNotify, // DEPRECATED: use deferred.notify + notify: promiseNotify, promise: promise, resolver: { resolve: promiseResolve, reject: promiseReject, - progress: promiseProgress + progress: promiseNotify, // DEPRECATED: use deferred.notify + notify: promiseNotify } }; @@ -422,29 +448,29 @@ define(['module'], function () { * functions to the registered listeners * @private * - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler - * @throws {Error} if any argument is not null, undefined, or a Function + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler */ - _then = function(callback, errback, progback) { + _then = function(onFulfilled, onRejected, onProgress) { var deferred, progressHandler; deferred = defer(); - progressHandler = progback + + progressHandler = typeof onProgress === 'function' ? function(update) { try { // Allow progress handler to transform progress event - deferred.progress(progback(update)); + deferred.notify(onProgress(update)); } catch(e) { // Use caught value as progress - deferred.progress(e); + deferred.notify(e); } } - : deferred.progress; + : function(update) { deferred.notify(update); }; handlers.push(function(promise) { - promise.then(callback, errback) + promise.then(onFulfilled, onRejected) .then(deferred.resolve, deferred.reject, progressHandler); }); @@ -456,9 +482,9 @@ define(['module'], function () { /** * Issue a progress event, notifying all progress listeners * @private - * @param update {*} progress event payload to pass to all listeners + * @param {*} update progress event payload to pass to all listeners */ - _progress = function(update) { + _notify = function(update) { processQueue(progressHandlers, update); return update; }; @@ -467,61 +493,58 @@ define(['module'], function () { * Transition from pre-resolution state to post-resolution state, notifying * all listeners of the resolution or rejection * @private - * @param completed {Promise} the completed value of this deferred + * @param {*} value the value of this deferred */ - _resolve = function(completed) { - completed = resolve(completed); - + _resolve = function(value) { // Replace _then with one that directly notifies with the result. - _then = completed.then; - // Replace _resolve so that this Deferred can only be completed once + _then = value.then; + // Replace _resolve so that this Deferred can only be resolved once _resolve = resolve; // Make _progress a noop, to disallow progress for the resolved promise. - _progress = noop; + _notify = identity; // Notify handlers - processQueue(handlers, completed); + processQueue(handlers, value); // Free progressHandlers array since we'll never issue progress events progressHandlers = handlers = undef; - return completed; + return value; }; return deferred; /** * Wrapper to allow _then to be replaced safely - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler - * @return {Promise} new Promise - * @throws {Error} if any argument is not null, undefined, or a Function + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler + * @return {Promise} new promise */ - function then(callback, errback, progback) { - return _then(callback, errback, progback); + function then(onFulfilled, onRejected, onProgress) { + // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress + return _then(onFulfilled, onRejected, onProgress); } /** * Wrapper to allow _resolve to be replaced */ function promiseResolve(val) { - return _resolve(val); + return _resolve(resolve(val)); } /** - * Wrapper to allow _resolve to be replaced + * Wrapper to allow _reject to be replaced */ function promiseReject(err) { return _resolve(rejected(err)); } /** - * Wrapper to allow _progress to be replaced - * @param {*} update progress update + * Wrapper to allow _notify to be replaced */ - function promiseProgress(update) { - return _progress(update); + function promiseNotify(update) { + return _notify(update); } } @@ -531,7 +554,7 @@ define(['module'], function () { * promiseOrValue is a promise. * * @param {*} promiseOrValue anything - * @returns {Boolean} true if promiseOrValue is a {@link Promise} + * @returns {boolean} true if promiseOrValue is a {@link Promise} */ function isPromise(promiseOrValue) { return promiseOrValue && typeof promiseOrValue.then === 'function'; @@ -542,25 +565,24 @@ define(['module'], function () { * 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. - * @memberOf when * - * @param promisesOrValues {Array} array of anything, may contain a mix - * of {@link Promise}s and values - * @param howMany {Number} number of promisesOrValues to resolve - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler + * @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 * @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, callback, errback, progback) { + function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { checkCallbacks(2, arguments); return when(promisesOrValues, function(promisesOrValues) { - var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; + var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, notify, len, i; len = promisesOrValues.length >>> 0; @@ -577,7 +599,7 @@ define(['module'], function () { deferred.resolve(values); } else { - progress = deferred.progress; + notify = deferred.notify; rejectOne = function(reason) { reasons.push(reason); @@ -601,12 +623,12 @@ define(['module'], function () { for(i = 0; i < len; ++i) { if(i in promisesOrValues) { - when(promisesOrValues[i], fulfiller, rejecter, progress); + when(promisesOrValues[i], fulfiller, rejecter, notify); } } } - return deferred.then(callback, errback, progback); + return deferred.promise.then(onFulfilled, onRejected, onProgress); function rejecter(reason) { rejectOne(reason); @@ -623,23 +645,22 @@ define(['module'], function () { * 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. - * @memberOf when * - * @param promisesOrValues {Array|Promise} array of anything, may contain a mix + * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values - * @param [callback] {Function} resolution handler - * @param [errback] {Function} rejection handler - * @param [progback] {Function} progress handler + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler * @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, callback, errback, progback) { + function any(promisesOrValues, onFulfilled, onRejected, onProgress) { function unwrapSingleResult(val) { - return callback ? callback(val[0]) : val[0]; + return onFulfilled ? onFulfilled(val[0]) : val[0]; } - return some(promisesOrValues, 1, unwrapSingleResult, errback, progback); + return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); } /** @@ -648,22 +669,20 @@ define(['module'], function () { * containing the resolution values of each of the promisesOrValues. * @memberOf when * - * @param promisesOrValues {Array|Promise} array of anything, may contain a mix + * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values - * @param [callback] {Function} - * @param [errback] {Function} - * @param [progressHandler] {Function} + * @param {function?} [onFulfilled] resolution handler + * @param {function?} [onRejected] rejection handler + * @param {function?} [onProgress] progress handler * @returns {Promise} */ - function all(promisesOrValues, callback, errback, progressHandler) { + function all(promisesOrValues, onFulfilled, onRejected, onProgress) { checkCallbacks(1, arguments); - return map(promisesOrValues, identity).then(callback, errback, progressHandler); + return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); } /** * Joins multiple promises into a single returned promise. - * @memberOf when - * @param {Promise|*} [...promises] two or more promises to join * @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. */ @@ -676,18 +695,16 @@ define(['module'], function () { * input to contain {@link Promise}s and/or values, and mapFunc may return * either a value or a {@link Promise} * - * @memberOf when - * - * @param promise {Array|Promise} array of anything, may contain a mix + * @param {Array|Promise} promise array of anything, may contain a mix * of {@link Promise}s and values - * @param mapFunc {Function} mapping function mapFunc(value) which may return + * @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. */ function map(promise, mapFunc) { return when(promise, function(array) { - var results, len, toResolve, resolve, reject, i, d; + var results, len, toResolve, resolve, i, d; // Since we know the resulting length, we can preallocate the results // array to avoid array expansions. @@ -699,7 +716,6 @@ define(['module'], function () { d.resolve(results); } else { - reject = d.reject; resolve = function resolveOne(item, i) { when(item, mapFunc).then(function(mapped) { results[i] = mapped; @@ -707,7 +723,7 @@ define(['module'], function () { if(!--toResolve) { d.resolve(results); } - }, reject); + }, d.reject); }; // Since mapFunc may be async, get all invocations of it into flight @@ -728,18 +744,15 @@ define(['module'], function () { /** * Traditional reduce function, similar to `Array.prototype.reduce()`, but - * input may contain {@link Promise}s and/or values, and reduceFunc - * may return either a value or a {@link Promise}, *and* initialValue may - * be a {@link Promise} for the starting value. - * @memberOf when + * 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 promise {Array|Promise} array of anything, may contain a mix - * of {@link Promise}s and values. May also be a {@link Promise} for - * an array. - * @param reduceFunc {Function} reduce function reduce(currentValue, nextValue, index, total), + * @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. - * @param [initialValue] {*} starting value, or a {@link Promise} for the starting value * @returns {Promise} that will resolve to the final reduced value */ function reduce(promise, reduceFunc /*, initialValue */) { @@ -765,13 +778,14 @@ define(['module'], function () { } /** - * Ensure that resolution of promiseOrValue will complete resolver with the completion - * value of promiseOrValue, or instead with resolveValue if it is provided. - * @memberOf when + * Ensure that resolution of promiseOrValue will trigger resolver with the + * value or reason of promiseOrValue, or instead with resolveValue if it is provided. * * @param promiseOrValue - * @param resolver {Resolver} - * @param [resolveValue] anything + * @param {Object} resolver + * @param {function} resolver.resolve + * @param {function} resolver.reject + * @param {*} [resolveValue] * @returns {Promise} */ function chain(promiseOrValue, resolver, resolveValue) { @@ -779,10 +793,18 @@ define(['module'], function () { return when(promiseOrValue, function(val) { - return resolver.resolve(useResolveValue ? resolveValue : val); + val = useResolveValue ? resolveValue : val; + resolver.resolve(val); + return val; }, - resolver.reject, - resolver.progress + function(reason) { + resolver.reject(reason); + return rejected(reason); + }, + function(update) { + typeof resolver.notify === 'function' && resolver.notify(update); + return update; + } ); } @@ -790,6 +812,11 @@ define(['module'], function () { // Utility functions // + /** + * Apply all functions in queue to value + * @param {Array} queue array of functions to execute + * @param {*} value argument passed to each function + */ function processQueue(queue, value) { var handler, i = 0; @@ -802,12 +829,13 @@ define(['module'], function () { * Helper that checks arrayOfCallbacks to ensure that each element is either * a function, or null or undefined. * @private - * - * @param arrayOfCallbacks {Array} array to check + * @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. + * 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) { @@ -889,7 +917,7 @@ define(['module'], function () { }); })(typeof define == 'function' && define.amd ? define - : function (deps, factory) { typeof exports === 'object' + : function (factory) { typeof exports === 'object' ? (module.exports = factory()) : (this.when = factory()); } diff --git a/mopidy/frontends/http/data/mopidy.min.js b/mopidy/frontends/http/data/mopidy.min.js index 7bb82336..fd8a0baf 100644 --- a/mopidy/frontends/http/data/mopidy.min.js +++ b/mopidy/frontends/http/data/mopidy.min.js @@ -2,4 +2,4 @@ * http://www.mopidy.com/ * Copyright (c) 2013 Stein Magnus Jodal and contributors * Licensed under the Apache License, Version 2.0 */ -function Mopidy(e){return this instanceof Mopidy?(this._settings=this._configure(e||{}),this._console=this._getConsole(),this._backoffDelay=this._settings.backoffDelayMin,this._pendingRequests={},this._webSocket=null,bane.createEventEmitter(this),this._delegateEvents(),this._settings.autoConnect&&this.connect(),void 0):new Mopidy(e)}("function"==typeof define&&define.amd&&function(e){define(e)}||"object"==typeof module&&function(e){module.exports=e()}||function(e){this.bane=e()})(function(){"use strict";function e(e,t,n){var o,r=n.length;if(r>0)for(o=0;r>o;++o)n[o](e,t);else setTimeout(function(){throw t.message=e+" listener threw error: "+t.message,t},0)}function t(e){if("function"!=typeof e)throw new TypeError("Listener is not function");return e}function n(e){return e.supervisors||(e.supervisors=[]),e.supervisors}function o(e,t){return e.listeners||(e.listeners={}),t&&!e.listeners[t]&&(e.listeners[t]=[]),t?e.listeners[t]:e.listeners}function r(e){return e.errbacks||(e.errbacks=[]),e.errbacks}function i(i){function c(t,n,o){try{n.listener.apply(n.thisp||i,o)}catch(s){e(t,s,r(i))}}return i=i||{},i.on=function(e,r,i){return"function"==typeof e?n(this).push({listener:e,thisp:r}):(o(this,e).push({listener:t(r),thisp:i}),void 0)},i.off=function(e,t){var i,s,c,u;if(!e){i=n(this),i.splice(0,i.length),s=o(this);for(c in s)s.hasOwnProperty(c)&&(i=o(this,c),i.splice(0,i.length));return i=r(this),i.splice(0,i.length),void 0}if("function"==typeof e?(i=n(this),t=e):i=o(this,e),!t)return i.splice(0,i.length),void 0;for(c=0,u=i.length;u>c;++c)if(i[c].listener===t)return i.splice(c,1),void 0},i.once=function(e,t,n){var o=function(){i.off(e,o),t.apply(this,arguments)};i.on(e,o,n)},i.bind=function(e,t){var n,o,r;if(t)for(o=0,r=t.length;r>o;++o){if("function"!=typeof e[t[o]])throw Error("No such method "+t[o]);this.on(t[o],e[t[o]],e)}else for(n in e)"function"==typeof e[n]&&this.on(n,e[n],e);return e},i.emit=function(e){var t,r,i=n(this),u=s.call(arguments);for(t=0,r=i.length;r>t;++t)c(e,i[t],u);for(i=o(this,e).slice(),u=s.call(arguments,1),t=0,r=i.length;r>t;++t)c(e,i[t],u)},i.errback=function(e){this.errbacks||(this.errbacks=[]),this.errbacks.push(t(e))},i}var s=Array.prototype.slice;return{createEventEmitter:i}}),function(e){"use strict";e(["module"],function(){function e(e,n,o,r){return t(e).then(n,o,r)}function t(e){var t,n;return e instanceof o?t=e:(c(e)&&"function"==typeof e.valueOf&&(e=e.valueOf()),c(e)?(n=s(),e.then(n.resolve,n.reject,n.progress),t=n.promise):t=r(e)),t}function n(t){return e(t,function(e){return i(e)})}function o(e){this.then=e}function r(e){var n=new o(function(n){try{return t(n?n(e):e)}catch(o){return i(o)}});return n}function i(e){var n=new o(function(n,o){try{return o?t(o(e)):i(e)}catch(r){return i(r)}});return n}function s(){function e(e,t,n){return l(e,t,n)}function n(e){return d(e)}function r(e){return d(i(e))}function c(e){return p(e)}var u,f,a,h,l,p,d;return f=new o(e),u={then:e,resolve:n,reject:r,progress:c,promise:f,resolver:{resolve:n,reject:r,progress:c}},a=[],h=[],l=function(e,t,n){var o,r;return o=s(),r=n?function(e){try{o.progress(n(e))}catch(t){o.progress(t)}}:o.progress,a.push(function(n){n.then(e,t).then(o.resolve,o.reject,r)}),h.push(r),o.promise},p=function(e){return y(h,e),e},d=function(e){return e=t(e),l=e.then,d=t,p=g,y(a,e),h=a=w,e},u}function c(e){return e&&"function"==typeof e.then}function u(t,n,o,r,i){return b(2,arguments),e(t,function(t){function c(e){y(e)}function u(e){d(e)}var f,a,h,l,p,d,y,b,k,_;if(k=t.length>>>0,f=Math.max(0,Math.min(n,k)),h=[],a=k-f+1,l=[],p=s(),f)for(b=p.progress,y=function(e){l.push(e),--a||(d=y=g,p.reject(l))},d=function(e){h.push(e),--f||(d=y=g,p.resolve(h))},_=0;k>_;++_)_ in t&&e(t[_],u,c,b);else p.resolve(h);return p.then(o,r,i)})}function f(e,t,n,o){function r(e){return t?t(e[0]):e[0]}return u(e,1,r,n,o)}function a(e,t,n,o){return b(1,arguments),l(e,k).then(t,n,o)}function h(){return l(arguments,k)}function l(t,n){return e(t,function(t){var o,r,i,c,u,f,a;if(i=r=t.length>>>0,o=[],a=s(),i)for(u=a.reject,c=function(t,r){e(t,n).then(function(e){o[r]=e,--i||a.resolve(o)},u)},f=0;r>f;f++)f in t?c(t[f],f):--i;else a.resolve(o);return a.promise})}function p(t,n){var o=v.call(arguments,1);return e(t,function(t){var r;return r=t.length,o[0]=function(t,o,i){return e(t,function(t){return e(o,function(e){return n(t,e,i,r)})})},_.apply(t,o)})}function d(t,n,o){var r=arguments.length>2;return e(t,function(e){return n.resolve(r?o:e)},n.reject,n.progress)}function y(e,t){for(var n,o=0;n=e[o++];)n(t)}function b(e,t){for(var n,o=t.length;o>e;)if(n=t[--o],null!=n&&"function"!=typeof n)throw Error("arg "+o+" must be a function")}function g(){}function k(e){return e}var _,v,w;return e.defer=s,e.resolve=t,e.reject=n,e.join=h,e.all=a,e.some=u,e.any=f,e.map=l,e.reduce=p,e.chain=d,e.isPromise=c,o.prototype={always:function(e,t){return this.then(e,e,t)},otherwise:function(e){return this.then(w,e)}},v=[].slice,_=[].reduce||function(e){var t,n,o,r,i;if(i=0,t=Object(this),r=t.length>>>0,n=arguments,1>=n.length)for(;;){if(i in t){o=t[i++];break}if(++i>=r)throw new TypeError}else o=n[1];for(;r>i;++i)i in t&&(o=e(o,t[i],i,t));return o},e})}("function"==typeof define&&define.amd?define:function(e,t){"object"==typeof exports?module.exports=t():this.when=t()}),Mopidy.prototype._configure=function(e){return e.webSocketUrl=e.webSocketUrl||"ws://"+document.location.host+"/mopidy/ws/",e.autoConnect!==!1&&(e.autoConnect=!0),e.backoffDelayMin=e.backoffDelayMin||1e3,e.backoffDelayMax=e.backoffDelayMax||64e3,e},Mopidy.prototype._getConsole=function(){var e=window.console||{};return e.log=e.log||function(){},e.warn=e.warn||function(){},e.error=e.error||function(){},e},Mopidy.prototype._delegateEvents=function(){this.off("websocket:close"),this.off("websocket:error"),this.off("websocket:incomingMessage"),this.off("websocket:open"),this.off("state:offline"),this.on("websocket:close",this._cleanup),this.on("websocket:error",this._handleWebSocketError),this.on("websocket:incomingMessage",this._handleMessage),this.on("websocket:open",this._resetBackoffDelay),this.on("websocket:open",this._getApiSpec),this.on("state:offline",this._reconnect)},Mopidy.prototype.connect=function(){if(this._webSocket){if(this._webSocket.readyState===WebSocket.OPEN)return;this._webSocket.close()}this._webSocket=this._settings.webSocket||new WebSocket(this._settings.webSocketUrl),this._webSocket.onclose=function(e){this.emit("websocket:close",e)}.bind(this),this._webSocket.onerror=function(e){this.emit("websocket:error",e)}.bind(this),this._webSocket.onopen=function(){this.emit("websocket:open")}.bind(this),this._webSocket.onmessage=function(e){this.emit("websocket:incomingMessage",e)}.bind(this)},Mopidy.prototype._cleanup=function(e){Object.keys(this._pendingRequests).forEach(function(t){var n=this._pendingRequests[t];delete this._pendingRequests[t],n.reject({message:"WebSocket closed",closeEvent:e})}.bind(this)),this.emit("state:offline")},Mopidy.prototype._reconnect=function(){this.emit("reconnectionPending",{timeToAttempt:this._backoffDelay}),setTimeout(function(){this.emit("reconnecting"),this.connect()}.bind(this),this._backoffDelay),this._backoffDelay=2*this._backoffDelay,this._backoffDelay>this._settings.backoffDelayMax&&(this._backoffDelay=this._settings.backoffDelayMax)},Mopidy.prototype._resetBackoffDelay=function(){this._backoffDelay=this._settings.backoffDelayMin},Mopidy.prototype.close=function(){this.off("state:offline",this._reconnect),this._webSocket.close()},Mopidy.prototype._handleWebSocketError=function(e){this._console.warn("WebSocket error:",e.stack||e)},Mopidy.prototype._send=function(e){var t=when.defer();switch(this._webSocket.readyState){case WebSocket.CONNECTING:t.resolver.reject({message:"WebSocket is still connecting"});break;case WebSocket.CLOSING:t.resolver.reject({message:"WebSocket is closing"});break;case WebSocket.CLOSED:t.resolver.reject({message:"WebSocket is closed"});break;default:e.jsonrpc="2.0",e.id=this._nextRequestId(),this._pendingRequests[e.id]=t.resolver,this._webSocket.send(JSON.stringify(e)),this.emit("websocket:outgoingMessage",e)}return t.promise},Mopidy.prototype._nextRequestId=function(){var e=-1;return function(){return e+=1}}(),Mopidy.prototype._handleMessage=function(e){try{var t=JSON.parse(e.data);t.hasOwnProperty("id")?this._handleResponse(t):t.hasOwnProperty("event")?this._handleEvent(t):this._console.warn("Unknown message type received. Message was: "+e.data)}catch(n){if(!(n instanceof SyntaxError))throw n;this._console.warn("WebSocket message parsing failed. Message was: "+e.data)}},Mopidy.prototype._handleResponse=function(e){if(!this._pendingRequests.hasOwnProperty(e.id))return this._console.warn("Unexpected response received. Message was:",e),void 0;var t=this._pendingRequests[e.id];delete this._pendingRequests[e.id],e.hasOwnProperty("result")?t.resolve(e.result):e.hasOwnProperty("error")?(t.reject(e.error),this._console.warn("Server returned error:",e.error)):(t.reject({message:"Response without 'result' or 'error' received",data:{response:e}}),this._console.warn("Response without 'result' or 'error' received. Message was:",e))},Mopidy.prototype._handleEvent=function(e){var t=e.event,n=e;delete n.event,this.emit("event:"+this._snakeToCamel(t),n)},Mopidy.prototype._getApiSpec=function(){this._send({method:"core.describe"}).then(this._createApi.bind(this),this._handleWebSocketError).then(null,this._handleWebSocketError)},Mopidy.prototype._createApi=function(e){var t=function(e){return function(){var t=Array.prototype.slice.call(arguments);return this._send({method:e,params:t})}.bind(this)}.bind(this),n=function(e){var t=e.split(".");return t.length>=1&&"core"===t[0]&&(t=t.slice(1)),t},o=function(e){var t=this;return e.forEach(function(e){e=this._snakeToCamel(e),t[e]=t[e]||{},t=t[e]}.bind(this)),t}.bind(this),r=function(r){var i=n(r),s=this._snakeToCamel(i.slice(-1)[0]),c=o(i.slice(0,-1));c[s]=t(r),c[s].description=e[r].description,c[s].params=e[r].params}.bind(this);Object.keys(e).forEach(r),this.emit("state:online")},Mopidy.prototype._snakeToCamel=function(e){return e.replace(/(_[a-z])/g,function(e){return e.toUpperCase().replace("_","")})}; \ No newline at end of file +function Mopidy(e){return this instanceof Mopidy?(this._settings=this._configure(e||{}),this._console=this._getConsole(),this._backoffDelay=this._settings.backoffDelayMin,this._pendingRequests={},this._webSocket=null,bane.createEventEmitter(this),this._delegateEvents(),this._settings.autoConnect&&this.connect(),void 0):new Mopidy(e)}("function"==typeof define&&define.amd&&function(e){define(e)}||"object"==typeof module&&function(e){module.exports=e()}||function(e){this.bane=e()})(function(){"use strict";function e(e,t,n){var o,r=n.length;if(r>0)for(o=0;r>o;++o)n[o](e,t);else setTimeout(function(){throw t.message=e+" listener threw error: "+t.message,t},0)}function t(e){if("function"!=typeof e)throw new TypeError("Listener is not function");return e}function n(e){return e.supervisors||(e.supervisors=[]),e.supervisors}function o(e,t){return e.listeners||(e.listeners={}),t&&!e.listeners[t]&&(e.listeners[t]=[]),t?e.listeners[t]:e.listeners}function r(e){return e.errbacks||(e.errbacks=[]),e.errbacks}function i(i){function c(t,n,o){try{n.listener.apply(n.thisp||i,o)}catch(s){e(t,s,r(i))}}return i=i||{},i.on=function(e,r,i){return"function"==typeof e?n(this).push({listener:e,thisp:r}):(o(this,e).push({listener:t(r),thisp:i}),void 0)},i.off=function(e,t){var i,s,c,f;if(!e){i=n(this),i.splice(0,i.length),s=o(this);for(c in s)s.hasOwnProperty(c)&&(i=o(this,c),i.splice(0,i.length));return i=r(this),i.splice(0,i.length),void 0}if("function"==typeof e?(i=n(this),t=e):i=o(this,e),!t)return i.splice(0,i.length),void 0;for(c=0,f=i.length;f>c;++c)if(i[c].listener===t)return i.splice(c,1),void 0},i.once=function(e,t,n){var o=function(){i.off(e,o),t.apply(this,arguments)};i.on(e,o,n)},i.bind=function(e,t){var n,o,r;if(t)for(o=0,r=t.length;r>o;++o){if("function"!=typeof e[t[o]])throw Error("No such method "+t[o]);this.on(t[o],e[t[o]],e)}else for(n in e)"function"==typeof e[n]&&this.on(n,e[n],e);return e},i.emit=function(e){var t,r,i=n(this),f=s.call(arguments);for(t=0,r=i.length;r>t;++t)c(e,i[t],f);for(i=o(this,e).slice(),f=s.call(arguments,1),t=0,r=i.length;r>t;++t)c(e,i[t],f)},i.errback=function(e){this.errbacks||(this.errbacks=[]),this.errbacks.push(t(e))},i}var s=Array.prototype.slice;return{createEventEmitter:i}}),function(e){"use strict";e(function(){function e(e,n,o,r){return t(e).then(n,o,r)}function t(e){var t;return t=e instanceof r?e:f(e)?n(e):i(e)}function n(e){var t=c();try{e.then(function(e){t.resolve(e)},function(e){t.reject(e)},function(e){t.progress(e)})}catch(n){t.reject(n)}return t.promise}function o(t){return e(t,s)}function r(e){this.then=e}function i(e){var n=new r(function(n){try{return t("function"==typeof n?n(e):e)}catch(o){return s(o)}});return n}function s(e){var n=new r(function(n,o){try{return t("function"==typeof o?o(e):s(e))}catch(r){return s(r)}});return n}function c(){function e(e,t,n){return l(e,t,n)}function n(e){return d(t(e))}function o(e){return d(s(e))}function i(e){return p(e)}var f,u,a,h,l,p,d;return u=new r(e),f={then:e,resolve:n,reject:o,progress:i,notify:i,promise:u,resolver:{resolve:n,reject:o,progress:i,notify:i}},a=[],h=[],l=function(e,t,n){var o,r;return o=c(),r="function"==typeof n?function(e){try{o.notify(n(e))}catch(t){o.notify(t)}}:function(e){o.notify(e)},a.push(function(n){n.then(e,t).then(o.resolve,o.reject,r)}),h.push(r),o.promise},p=function(e){return b(h,e),e},d=function(e){return l=e.then,d=t,p=_,b(a,e),h=a=m,e},f}function f(e){return e&&"function"==typeof e.then}function u(t,n,o,r,i){return g(2,arguments),e(t,function(t){function s(e){y(e)}function f(e){d(e)}var u,a,h,l,p,d,y,b,g,_;if(g=t.length>>>0,u=Math.max(0,Math.min(n,g)),h=[],a=g-u+1,l=[],p=c(),u)for(b=p.notify,y=function(e){l.push(e),--a||(d=y=k,p.reject(l))},d=function(e){h.push(e),--u||(d=y=k,p.resolve(h))},_=0;g>_;++_)_ in t&&e(t[_],f,s,b);else p.resolve(h);return p.promise.then(o,r,i)})}function a(e,t,n,o){function r(e){return t?t(e[0]):e[0]}return u(e,1,r,n,o)}function h(e,t,n,o){return g(1,arguments),p(e,_).then(t,n,o)}function l(){return p(arguments,_)}function p(t,n){return e(t,function(t){var o,r,i,s,f,u;if(i=r=t.length>>>0,o=[],u=c(),i)for(s=function(t,r){e(t,n).then(function(e){o[r]=e,--i||u.resolve(o)},u.reject)},f=0;r>f;f++)f in t?s(t[f],f):--i;else u.resolve(o);return u.promise})}function d(t,n){var o=w.call(arguments,1);return e(t,function(t){var r;return r=t.length,o[0]=function(t,o,i){return e(t,function(t){return e(o,function(e){return n(t,e,i,r)})})},v.apply(t,o)})}function y(t,n,o){var r=arguments.length>2;return e(t,function(e){return e=r?o:e,n.resolve(e),e},function(e){return n.reject(e),s(e)},function(e){return"function"==typeof n.notify&&n.notify(e),e})}function b(e,t){for(var n,o=0;n=e[o++];)n(t)}function g(e,t){for(var n,o=t.length;o>e;)if(n=t[--o],null!=n&&"function"!=typeof n)throw Error("arg "+o+" must be a function")}function k(){}function _(e){return e}var v,w,m;return e.defer=c,e.resolve=t,e.reject=o,e.join=l,e.all=h,e.map=p,e.reduce=d,e.any=a,e.some=u,e.chain=y,e.isPromise=f,r.prototype={always:function(e,t){return this.then(e,e,t)},otherwise:function(e){return this.then(m,e)},yield:function(e){return this.then(function(){return e})},spread:function(e){return this.then(function(t){return h(t,function(t){return e.apply(m,t)})})}},w=[].slice,v=[].reduce||function(e){var t,n,o,r,i;if(i=0,t=Object(this),r=t.length>>>0,n=arguments,1>=n.length)for(;;){if(i in t){o=t[i++];break}if(++i>=r)throw new TypeError}else o=n[1];for(;r>i;++i)i in t&&(o=e(o,t[i],i,t));return o},e})}("function"==typeof define&&define.amd?define:function(e){"object"==typeof exports?module.exports=e():this.when=e()}),Mopidy.prototype._configure=function(e){return e.webSocketUrl=e.webSocketUrl||"ws://"+document.location.host+"/mopidy/ws/",e.autoConnect!==!1&&(e.autoConnect=!0),e.backoffDelayMin=e.backoffDelayMin||1e3,e.backoffDelayMax=e.backoffDelayMax||64e3,e},Mopidy.prototype._getConsole=function(){var e=window.console||{};return e.log=e.log||function(){},e.warn=e.warn||function(){},e.error=e.error||function(){},e},Mopidy.prototype._delegateEvents=function(){this.off("websocket:close"),this.off("websocket:error"),this.off("websocket:incomingMessage"),this.off("websocket:open"),this.off("state:offline"),this.on("websocket:close",this._cleanup),this.on("websocket:error",this._handleWebSocketError),this.on("websocket:incomingMessage",this._handleMessage),this.on("websocket:open",this._resetBackoffDelay),this.on("websocket:open",this._getApiSpec),this.on("state:offline",this._reconnect)},Mopidy.prototype.connect=function(){if(this._webSocket){if(this._webSocket.readyState===WebSocket.OPEN)return;this._webSocket.close()}this._webSocket=this._settings.webSocket||new WebSocket(this._settings.webSocketUrl),this._webSocket.onclose=function(e){this.emit("websocket:close",e)}.bind(this),this._webSocket.onerror=function(e){this.emit("websocket:error",e)}.bind(this),this._webSocket.onopen=function(){this.emit("websocket:open")}.bind(this),this._webSocket.onmessage=function(e){this.emit("websocket:incomingMessage",e)}.bind(this)},Mopidy.prototype._cleanup=function(e){Object.keys(this._pendingRequests).forEach(function(t){var n=this._pendingRequests[t];delete this._pendingRequests[t],n.reject({message:"WebSocket closed",closeEvent:e})}.bind(this)),this.emit("state:offline")},Mopidy.prototype._reconnect=function(){this.emit("reconnectionPending",{timeToAttempt:this._backoffDelay}),setTimeout(function(){this.emit("reconnecting"),this.connect()}.bind(this),this._backoffDelay),this._backoffDelay=2*this._backoffDelay,this._backoffDelay>this._settings.backoffDelayMax&&(this._backoffDelay=this._settings.backoffDelayMax)},Mopidy.prototype._resetBackoffDelay=function(){this._backoffDelay=this._settings.backoffDelayMin},Mopidy.prototype.close=function(){this.off("state:offline",this._reconnect),this._webSocket.close()},Mopidy.prototype._handleWebSocketError=function(e){this._console.warn("WebSocket error:",e.stack||e)},Mopidy.prototype._send=function(e){var t=when.defer();switch(this._webSocket.readyState){case WebSocket.CONNECTING:t.resolver.reject({message:"WebSocket is still connecting"});break;case WebSocket.CLOSING:t.resolver.reject({message:"WebSocket is closing"});break;case WebSocket.CLOSED:t.resolver.reject({message:"WebSocket is closed"});break;default:e.jsonrpc="2.0",e.id=this._nextRequestId(),this._pendingRequests[e.id]=t.resolver,this._webSocket.send(JSON.stringify(e)),this.emit("websocket:outgoingMessage",e)}return t.promise},Mopidy.prototype._nextRequestId=function(){var e=-1;return function(){return e+=1}}(),Mopidy.prototype._handleMessage=function(e){try{var t=JSON.parse(e.data);t.hasOwnProperty("id")?this._handleResponse(t):t.hasOwnProperty("event")?this._handleEvent(t):this._console.warn("Unknown message type received. Message was: "+e.data)}catch(n){if(!(n instanceof SyntaxError))throw n;this._console.warn("WebSocket message parsing failed. Message was: "+e.data)}},Mopidy.prototype._handleResponse=function(e){if(!this._pendingRequests.hasOwnProperty(e.id))return this._console.warn("Unexpected response received. Message was:",e),void 0;var t=this._pendingRequests[e.id];delete this._pendingRequests[e.id],e.hasOwnProperty("result")?t.resolve(e.result):e.hasOwnProperty("error")?(t.reject(e.error),this._console.warn("Server returned error:",e.error)):(t.reject({message:"Response without 'result' or 'error' received",data:{response:e}}),this._console.warn("Response without 'result' or 'error' received. Message was:",e))},Mopidy.prototype._handleEvent=function(e){var t=e.event,n=e;delete n.event,this.emit("event:"+this._snakeToCamel(t),n)},Mopidy.prototype._getApiSpec=function(){this._send({method:"core.describe"}).then(this._createApi.bind(this),this._handleWebSocketError).then(null,this._handleWebSocketError)},Mopidy.prototype._createApi=function(e){var t=function(e){return function(){var t=Array.prototype.slice.call(arguments);return this._send({method:e,params:t})}.bind(this)}.bind(this),n=function(e){var t=e.split(".");return t.length>=1&&"core"===t[0]&&(t=t.slice(1)),t},o=function(e){var t=this;return e.forEach(function(e){e=this._snakeToCamel(e),t[e]=t[e]||{},t=t[e]}.bind(this)),t}.bind(this),r=function(r){var i=n(r),s=this._snakeToCamel(i.slice(-1)[0]),c=o(i.slice(0,-1));c[s]=t(r),c[s].description=e[r].description,c[s].params=e[r].params}.bind(this);Object.keys(e).forEach(r),this.emit("state:online")},Mopidy.prototype._snakeToCamel=function(e){return e.replace(/(_[a-z])/g,function(e){return e.toUpperCase().replace("_","")})}; \ No newline at end of file