diff --git a/.gitignore b/.gitignore index 79230110..1ec12cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ node_modules/ nosetests.xml *~ *.orig +js/test/lib/ diff --git a/js/Gruntfile.js b/js/Gruntfile.js index c1e687c9..812ecec4 100644 --- a/js/Gruntfile.js +++ b/js/Gruntfile.js @@ -11,6 +11,7 @@ module.exports = function (grunt) { " * Licensed under the Apache License, Version 2.0 */\n", files: { own: ["Gruntfile.js", "src/**/*.js", "test/**/*-test.js"], + main: "src/mopidy.js", concat: "../mopidy/http/data/mopidy.js", minified: "../mopidy/http/data/mopidy.min.js" } @@ -18,19 +19,35 @@ module.exports = function (grunt) { buster: { all: {} }, - concat: { - options: { - banner: "<%= meta.banner %>", - stripBanners: true - }, - all: { + browserify: { + test_mopidy: { files: { - "<%= meta.files.concat %>": [ - "lib/bane-*.js", - "lib/when-define-shim.js", - "lib/when-*.js", - "src/mopidy.js" - ] + "test/lib/mopidy.js": "<%= meta.files.main %>" + }, + options: { + postBundleCB: function (err, src, next) { + next(null, grunt.template.process("<%= meta.banner %>") + src); + }, + standalone: "Mopidy" + } + }, + test_when: { + files: { + "test/lib/when.js": "node_modules/when/when.js" + }, + options: { + standalone: "when" + } + }, + dist: { + files: { + "<%= meta.files.concat %>": "<%= meta.files.main %>" + }, + options: { + postBundleCB: function (err, src, next) { + next(null, grunt.template.process("<%= meta.banner %>") + src); + }, + standalone: "Mopidy" } } }, @@ -70,12 +87,13 @@ module.exports = function (grunt) { } }); - grunt.registerTask("test", ["jshint", "buster"]); - grunt.registerTask("build", ["test", "concat", "uglify"]); + grunt.registerTask("test_build", ["browserify:test_when", "browserify:test_mopidy"]); + grunt.registerTask("test", ["jshint", "test_build", "buster"]); + grunt.registerTask("build", ["test", "browserify:dist", "uglify"]); grunt.registerTask("default", ["build"]); grunt.loadNpmTasks("grunt-buster"); - grunt.loadNpmTasks("grunt-contrib-concat"); + grunt.loadNpmTasks("grunt-browserify"); grunt.loadNpmTasks("grunt-contrib-jshint"); grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.loadNpmTasks("grunt-contrib-watch"); diff --git a/js/buster.js b/js/buster.js index 1cc517c8..c5dec850 100644 --- a/js/buster.js +++ b/js/buster.js @@ -2,23 +2,13 @@ var config = module.exports; config.browser_tests = { environment: "browser", - libs: [ - "lib/bane-*.js", - "lib/when-define-shim.js", - "lib/when-*.js" - ], - sources: ["src/**/*.js"], + libs: ["test/lib/*.js"], testHelpers: ["test/**/*-helper.js"], tests: ["test/**/*-test.js"] }; config.node_tests = { environment: "node", - libs: [ - "lib/bane-*.js", - "lib/when-define-shim.js", - "lib/when-*.js" - ], sources: ["src/**/*.js"], testHelpers: ["test/**/*-helper.js"], tests: ["test/**/*-test.js"] diff --git a/js/lib/bane-1.0.0.js b/js/lib/bane-1.0.0.js deleted file mode 100644 index 8051764d..00000000 --- a/js/lib/bane-1.0.0.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * BANE - Browser globals, AMD and Node Events - * - * https://github.com/busterjs/bane - * - * @version 1.0.0 - */ - -((typeof define === "function" && define.amd && function (m) { define("bane", m); }) || - (typeof module === "object" && function (m) { module.exports = m(); }) || - function (m) { this.bane = m(); } -)(function () { - "use strict"; - var slice = Array.prototype.slice; - - function handleError(event, error, errbacks) { - var i, l = errbacks.length; - if (l > 0) { - for (i = 0; i < l; ++i) { errbacks[i](event, error); } - return; - } - setTimeout(function () { - error.message = event + " listener threw error: " + error.message; - throw error; - }, 0); - } - - function assertFunction(fn) { - if (typeof fn !== "function") { - throw new TypeError("Listener is not function"); - } - return fn; - } - - function supervisors(object) { - if (!object.supervisors) { object.supervisors = []; } - return object.supervisors; - } - - function listeners(object, event) { - if (!object.listeners) { object.listeners = {}; } - if (event && !object.listeners[event]) { object.listeners[event] = []; } - return event ? object.listeners[event] : object.listeners; - } - - function errbacks(object) { - if (!object.errbacks) { object.errbacks = []; } - return object.errbacks; - } - - /** - * @signature var emitter = bane.createEmitter([object]); - * - * Create a new event emitter. If an object is passed, it will be modified - * by adding the event emitter methods (see below). - */ - function createEventEmitter(object) { - object = object || {}; - - function notifyListener(event, listener, args) { - try { - listener.listener.apply(listener.thisp || object, args); - } catch (e) { - handleError(event, e, errbacks(object)); - } - } - - object.on = function (event, listener, thisp) { - if (typeof event === "function") { - return supervisors(this).push({ - listener: event, - thisp: listener - }); - } - listeners(this, event).push({ - listener: assertFunction(listener), - thisp: thisp - }); - }; - - object.off = function (event, listener) { - var fns, events, i, l; - if (!event) { - fns = supervisors(this); - fns.splice(0, fns.length); - - events = listeners(this); - for (i in events) { - if (events.hasOwnProperty(i)) { - fns = listeners(this, i); - fns.splice(0, fns.length); - } - } - - fns = errbacks(this); - fns.splice(0, fns.length); - - return; - } - if (typeof event === "function") { - fns = supervisors(this); - listener = event; - } else { - fns = listeners(this, event); - } - if (!listener) { - fns.splice(0, fns.length); - return; - } - for (i = 0, l = fns.length; i < l; ++i) { - if (fns[i].listener === listener) { - fns.splice(i, 1); - return; - } - } - }; - - object.once = function (event, listener, thisp) { - var wrapper = function () { - object.off(event, wrapper); - listener.apply(this, arguments); - }; - - object.on(event, wrapper, thisp); - }; - - object.bind = function (object, events) { - var prop, i, l; - if (!events) { - for (prop in object) { - if (typeof object[prop] === "function") { - this.on(prop, object[prop], object); - } - } - } else { - for (i = 0, l = events.length; i < l; ++i) { - if (typeof object[events[i]] === "function") { - this.on(events[i], object[events[i]], object); - } else { - throw new Error("No such method " + events[i]); - } - } - } - return object; - }; - - object.emit = function (event) { - var toNotify = supervisors(this); - var args = slice.call(arguments), i, l; - - for (i = 0, l = toNotify.length; i < l; ++i) { - notifyListener(event, toNotify[i], args); - } - - toNotify = listeners(this, event).slice(); - args = slice.call(arguments, 1); - for (i = 0, l = toNotify.length; i < l; ++i) { - notifyListener(event, toNotify[i], args); - } - }; - - object.errback = function (listener) { - if (!this.errbacks) { this.errbacks = []; } - this.errbacks.push(assertFunction(listener)); - }; - - return object; - } - - return { createEventEmitter: createEventEmitter }; -}); diff --git a/js/lib/websocket/browser.js b/js/lib/websocket/browser.js new file mode 100644 index 00000000..e594246c --- /dev/null +++ b/js/lib/websocket/browser.js @@ -0,0 +1 @@ +module.exports = { Client: window.WebSocket }; diff --git a/js/lib/websocket/package.json b/js/lib/websocket/package.json new file mode 100644 index 00000000..d1e2ac63 --- /dev/null +++ b/js/lib/websocket/package.json @@ -0,0 +1,4 @@ +{ + "browser": "browser.js", + "main": "server.js" +} diff --git a/js/lib/websocket/server.js b/js/lib/websocket/server.js new file mode 100644 index 00000000..dd24f4be --- /dev/null +++ b/js/lib/websocket/server.js @@ -0,0 +1 @@ +module.exports = require('faye-websocket'); diff --git a/js/lib/when-2.4.0.js b/js/lib/when-2.4.0.js deleted file mode 100644 index aa386275..00000000 --- a/js/lib/when-2.4.0.js +++ /dev/null @@ -1,922 +0,0 @@ -/** @license MIT License (c) copyright 2011-2013 original author or authors */ - -/** - * A lightweight CommonJS Promises/A and when() implementation - * when is part of the cujo.js family of libraries (http://cujojs.com/) - * - * Licensed under the MIT License at: - * http://www.opensource.org/licenses/mit-license.php - * - * @author Brian Cavalier - * @author John Hann - * @version 2.4.0 - */ -(function(define, global) { 'use strict'; -define(function (require) { - - // Public API - - 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 = isPromiseLike; // DEPRECATED: use isPromiseLike - when.isPromiseLike = isPromiseLike; // Is something promise-like, aka thenable - - /** - * Register an observer for a promise or immediate value. - * - * @param {*} promiseOrValue - * @param {function?} [onFulfilled] callback to be called when promiseOrValue is - * successfully fulfilled. If promiseOrValue is an immediate value, callback - * will be invoked immediately. - * @param {function?} [onRejected] callback to be called when promiseOrValue is - * rejected. - * @param {function?} [onProgress] callback to be called when progress updates - * are issued for promiseOrValue. - * @returns {Promise} a new {@link Promise} that will complete with the return - * value of callback or errback or the completion value of promiseOrValue if - * callback and/or errback is not supplied. - */ - function when(promiseOrValue, onFulfilled, onRejected, onProgress) { - // Get a trusted promise for the input promiseOrValue, and then - // register promise handlers - return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); - } - - /** - * Trusted Promise constructor. A Promise created from this constructor is - * a trusted when.js promise. Any other duck-typed promise is considered - * untrusted. - * @constructor - * @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(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 - * @return {Promise} - */ - otherwise: function(onRejected) { - return this.then(undef, onRejected); - }, - - /** - * Ensures that onFulfilledOrRejected will be called regardless of whether - * this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT - * receive the promises' value or reason. Any returned value will be disregarded. - * onFulfilledOrRejected may throw or return a rejected promise to signal - * an additional error. - * @param {function} onFulfilledOrRejected handler to be called regardless of - * fulfillment or rejection - * @returns {Promise} - */ - ensure: function(onFulfilledOrRejected) { - return this.then(injectHandler, injectHandler)['yield'](this); - - function injectHandler() { - return resolve(onFulfilledOrRejected()); - } - }, - - /** - * Shortcut for .then(function() { return value; }) - * @param {*} value - * @return {Promise} a promise that: - * - is fulfilled if value is not a promise, or - * - if value is a promise, will fulfill with its value, or reject - * with its reason. - */ - 'yield': function(value) { - return this.then(function() { - return value; - }); - }, - - /** - * 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 - * i.e. onFulfilled.apply(undefined, array). - * @param {function} onFulfilled function to receive spread arguments - * @return {Promise} - */ - spread: function(onFulfilled) { - return this.then(function(array) { - // array may contain promises, so resolve its contents. - return all(array, function(array) { - return onFulfilled.apply(undef, array); - }); - }); - }, - - /** - * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected) - * @deprecated - */ - always: function(onFulfilledOrRejected, onProgress) { - return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); - } - }; - - /** - * Returns a resolved promise. The returned promise will be - * - fulfilled with promiseOrValue if it is a value, or - * - if promiseOrValue is a promise - * - fulfilled with promiseOrValue's value after it is fulfilled - * - rejected with promiseOrValue's reason after it is rejected - * @param {*} value - * @return {Promise} - */ - function resolve(value) { - return promise(function(resolve) { - resolve(value); - }); - } - - /** - * Returns a rejected promise for the supplied promiseOrValue. The returned - * promise will be rejected with: - * - promiseOrValue, if it is a value, or - * - if promiseOrValue is a promise - * - promiseOrValue's value after it is fulfilled - * - promiseOrValue's reason after it is rejected - * @param {*} promiseOrValue the rejected value of the returned {@link Promise} - * @return {Promise} rejected {@link Promise} - */ - function reject(promiseOrValue) { - return when(promiseOrValue, rejected); - } - - /** - * Creates a {promise, resolver} pair, either or both of which - * may be given out safely to consumers. - * The resolver has resolve, reject, and progress. The promise - * 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, - * notify: function:Promise - * }}} - */ - function defer() { - var deferred, pending, resolved; - - // Optimize object shape - deferred = { - promise: undef, resolve: undef, reject: undef, notify: undef, - resolver: { resolve: undef, reject: undef, notify: undef } - }; - - deferred.promise = pending = promise(makeDeferred); - - return deferred; - - function makeDeferred(resolvePending, rejectPending, notifyPending) { - deferred.resolve = deferred.resolver.resolve = function(value) { - if(resolved) { - return resolve(value); - } - resolved = true; - resolvePending(value); - return pending; - }; - - deferred.reject = deferred.resolver.reject = function(reason) { - if(resolved) { - return resolve(rejected(reason)); - } - resolved = true; - rejectPending(reason); - return pending; - }; - - deferred.notify = deferred.resolver.notify = function(update) { - notifyPending(update); - return update; - }; - } - } - - /** - * Creates a new promise whose fate is determined by resolver. - * @param {function} resolver function(resolve, reject, notify) - * @returns {Promise} promise whose fate is determine by resolver - */ - function promise(resolver) { - 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 { - resolver(promiseResolve, promiseReject, promiseNotify); - } catch(e) { - promiseReject(e); - } - - // Return the promise - return self; - - /** - * 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 _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(); - } - - /** - * Transition from pre-resolution state to post-resolution state, notifying - * all listeners of the ultimate fulfillment or rejection - * @param {*|Promise} val resolution value - */ - function promiseResolve(val) { - if(!consumers) { - return; - } - - value = coerce(val); - scheduleConsumers(consumers, value); - consumers = undef; - - if(status) { - updateStatus(value, status); - } - } - - /** - * Reject this promise with the supplied reason, which will be used verbatim. - * @param {*} reason reason for the rejection - */ - function promiseReject(reason) { - promiseResolve(rejected(reason)); - } - - /** - * Issue a progress event, notifying all progress listeners - * @param {*} update progress event payload to pass to all listeners - */ - function promiseNotify(update) { - if(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 {*} Guaranteed to return a trusted Promise. If x - * is trusted, returns x, otherwise, returns a new, trusted, already-resolved - * Promise whose resolution value is: - * * the resolution value of x if it's a foreign promise, or - * * x if it's a value - */ - function coerce(x) { - if (x instanceof Promise) { - return x; - } - - if (!(x === Object(x) && 'then' in x)) { - return fulfilled(x); - } - - return promise(function(resolve, reject, notify) { - enqueue(function() { - try { - // We must check and assimilate in the same tick, but not the - // current tick, careful only to access promiseOrValue.then once. - var untrustedThen = x.then; - - if(typeof untrustedThen === 'function') { - fcall(untrustedThen, x, resolve, reject, notify); - } else { - // It's a value, create a fulfilled wrapper - resolve(fulfilled(x)); - } - - } catch(e) { - // Something went wrong, reject - reject(e); - } - }); - }); - } - - /** - * Proxy for a near, fulfilled value - * @param {*} value - * @constructor - */ - function NearFulfilledProxy(value) { - this.value = value; - } - - NearFulfilledProxy.prototype.when = function(onResult) { - return typeof onResult === 'function' ? onResult(this.value) : this.value; - }; - - /** - * Proxy for a near rejection - * @param {*} reason - * @constructor - */ - function NearRejectedProxy(reason) { - this.reason = reason; - } - - 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 - * in the next queue drain run. - * @private - * @param {Array} handlers queue of handlers to execute - * @param {*} value passed as the only arg to each handler - */ - function scheduleConsumers(handlers, value) { - enqueue(function() { - var handler, i = 0; - while (handler = handlers[i++]) { - handler(value); - } - }); - } - - function updateStatus(value, status) { - value.then(statusFulfilled, statusRejected); - - function statusFulfilled() { status.fulfilled(); } - function statusRejected(r) { status.rejected(r); } - } - - /** - * 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 isPromiseLike(x) { - return x && typeof x.then === 'function'; - } - - /** - * Initiates a competitive race, returning a promise that will resolve when - * howMany of the supplied promisesOrValues have resolved, or will reject when - * it becomes impossible for howMany to resolve, for example, when - * (promisesOrValues.length - howMany) + 1 input promises reject. - * - * @param {Array} promisesOrValues array of anything, may contain a mix - * of promises and values - * @param howMany {number} number of promisesOrValues to resolve - * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then() - * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then() - * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then() - * @returns {Promise} promise that will resolve to an array of howMany values that - * resolved first, or will reject with an array of - * (promisesOrValues.length - howMany) + 1 rejection reasons. - */ - function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { - - return when(promisesOrValues, function(promisesOrValues) { - - return promise(resolveSome).then(onFulfilled, onRejected, onProgress); - - function resolveSome(resolve, reject, notify) { - var toResolve, toReject, values, reasons, fulfillOne, rejectOne, len, i; - - len = promisesOrValues.length >>> 0; - - toResolve = Math.max(0, Math.min(howMany, len)); - values = []; - - toReject = (len - toResolve) + 1; - reasons = []; - - // No items in the input, resolve immediately - if (!toResolve) { - resolve(values); - - } else { - rejectOne = function(reason) { - reasons.push(reason); - if(!--toReject) { - fulfillOne = rejectOne = identity; - reject(reasons); - } - }; - - fulfillOne = function(val) { - // This orders the values based on promise resolution order - values.push(val); - if (!--toResolve) { - fulfillOne = rejectOne = identity; - resolve(values); - } - }; - - for(i = 0; i < len; ++i) { - if(i in promisesOrValues) { - when(promisesOrValues[i], fulfiller, rejecter, notify); - } - } - } - - function rejecter(reason) { - rejectOne(reason); - } - - function fulfiller(val) { - fulfillOne(val); - } - } - }); - } - - /** - * Initiates a competitive race, returning a promise that will resolve when - * any one of the supplied promisesOrValues has resolved or will reject when - * *all* promisesOrValues have rejected. - * - * @param {Array|Promise} promisesOrValues array of anything, may contain a mix - * of {@link Promise}s and values - * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then() - * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then() - * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then() - * @returns {Promise} promise that will resolve to the value that resolved first, or - * will reject with an array of all rejected inputs. - */ - function any(promisesOrValues, onFulfilled, onRejected, onProgress) { - - function unwrapSingleResult(val) { - return onFulfilled ? onFulfilled(val[0]) : val[0]; - } - - return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); - } - - /** - * Return a promise that will resolve only once all the supplied promisesOrValues - * have resolved. The resolution value of the returned promise will be an array - * containing the resolution values of each of the promisesOrValues. - * @memberOf when - * - * @param {Array|Promise} promisesOrValues array of anything, may contain a mix - * of {@link Promise}s and values - * @param {function?} [onFulfilled] DEPRECATED, use returnedPromise.then() - * @param {function?} [onRejected] DEPRECATED, use returnedPromise.then() - * @param {function?} [onProgress] DEPRECATED, use returnedPromise.then() - * @returns {Promise} - */ - function all(promisesOrValues, onFulfilled, onRejected, onProgress) { - return _map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); - } - - /** - * Joins multiple promises into a single returned promise. - * @return {Promise} a promise that will fulfill when *all* the input promises - * have fulfilled, or will reject when *any one* of the input promises rejects. - */ - function join(/* ...promises */) { - return _map(arguments, identity); - } - - /** - * Settles all input promises such that they are guaranteed not to - * be pending once the returned promise fulfills. The returned promise - * will always fulfill, except in the case where `array` is a promise - * that rejects. - * @param {Array|Promise} array or promise for array of promises to settle - * @returns {Promise} promise that always fulfills with an array of - * outcome snapshots for each input promise. - */ - function settle(array) { - return _map(array, toFulfilledState, toRejectedState); - } - - /** - * Promise-aware array map function, similar to `Array.prototype.map()`, - * but input array may contain promises or values. - * @param {Array|Promise} array array of anything, may contain promises and values - * @param {function} mapFunc map function which may return a promise or value - * @returns {Promise} promise that will fulfill with an array of mapped values - * or reject if any input promise rejects. - */ - function map(array, mapFunc) { - return _map(array, mapFunc); - } - - /** - * Internal map that allows a fallback to handle rejections - * @param {Array|Promise} array array of anything, may contain promises and values - * @param {function} mapFunc map function which may return a promise or value - * @param {function?} fallback function to handle rejected promises - * @returns {Promise} promise that will fulfill with an array of mapped values - * or reject if any input promise rejects. - */ - function _map(array, mapFunc, fallback) { - return when(array, function(array) { - - return _promise(resolveMap); - - function resolveMap(resolve, reject, notify) { - var results, len, toResolve, i; - - // Since we know the resulting length, we can preallocate the results - // array to avoid array expansions. - toResolve = len = array.length >>> 0; - results = []; - - if(!toResolve) { - resolve(results); - return; - } - - // 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); - } - } - }); - } - - /** - * Traditional reduce function, similar to `Array.prototype.reduce()`, but - * input may contain promises and/or values, and reduceFunc - * may return either a value or a promise, *and* initialValue may - * be a promise for the starting value. - * - * @param {Array|Promise} promise array or promise for an array of anything, - * may contain a mix of promises and values. - * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total), - * where total is the total number of items being reduced, and will be the same - * in each call to reduceFunc. - * @returns {Promise} that will resolve to the final reduced value - */ - function reduce(promise, reduceFunc /*, initialValue */) { - var args = fcall(slice, arguments, 1); - - return when(promise, function(array) { - var total; - - total = array.length; - - // Wrap the supplied reduceFunc with one that handles promises and then - // delegates to the supplied. - args[0] = function (current, val, i) { - return when(current, function (c) { - return when(val, function (value) { - return reduceFunc(c, value, i, total); - }); - }); - }; - - return reduceArray.apply(array, args); - }); - } - - // Snapshot states - - /** - * Creates a fulfilled state snapshot - * @private - * @param {*} x any value - * @returns {{state:'fulfilled',value:*}} - */ - function toFulfilledState(x) { - return { state: 'fulfilled', value: x }; - } - - /** - * Creates a rejected state snapshot - * @private - * @param {*} x any reason - * @returns {{state:'rejected',reason:*}} - */ - function toRejectedState(x) { - return { state: 'rejected', reason: x }; - } - - /** - * Creates a pending state snapshot - * @private - * @returns {{state:'pending'}} - */ - function toPendingState() { - return { state: 'pending' }; - } - - // - // Internals, utilities, etc. - // - - var reduceArray, slice, fcall, nextTick, handlerQueue, - setTimeout, funcProto, call, arrayProto, monitorApi, - cjsRequire, undef; - - cjsRequire = require; - - // - // Shared handler queue processing - // - // Credit to Twisol (https://github.com/Twisol) for suggesting - // this type of extensible queue + trampoline approach for - // next-tick conflation. - - handlerQueue = []; - - /** - * Enqueue a task. If the queue is not currently scheduled to be - * drained, schedule it. - * @param {function} task - */ - function enqueue(task) { - if(handlerQueue.push(task) === 1) { - nextTick(drainQueue); - } - } - - /** - * Drain the handler queue entirely, being careful to allow the - * queue to be extended while it is being processed, and to continue - * processing until it is truly empty. - */ - function drainQueue() { - var task, i = 0; - - while(task = handlerQueue[i++]) { - task(); - } - - handlerQueue = []; - } - - // capture setTimeout to avoid being caught by fake timers - // used in time based tests - setTimeout = global.setTimeout; - - // 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; - call = funcProto.call; - fcall = funcProto.bind - ? call.bind(call) - : function(f, context) { - return f.apply(context, slice.call(arguments, 2)); - }; - - // Safe array ops - arrayProto = []; - slice = arrayProto.slice; - - // ES5 reduce implementation if native not available - // See: http://es5.github.com/#x15.4.4.21 as there are many - // specifics and edge cases. ES5 dictates that reduce.length === 1 - // This implementation deviates from ES5 spec in the following ways: - // 1. It does not check if reduceFunc is a Callable - reduceArray = arrayProto.reduce || - function(reduceFunc /*, initialValue */) { - /*jshint maxcomplexity: 7*/ - var arr, args, reduced, len, i; - - i = 0; - arr = Object(this); - len = arr.length >>> 0; - args = arguments; - - // If no initialValue, use first item of array (we know length !== 0 here) - // and adjust i to start at second item - if(args.length <= 1) { - // Skip to the first real element in the array - for(;;) { - if(i in arr) { - reduced = arr[i++]; - break; - } - - // If we reached the end of the array without finding any real - // elements, it's a TypeError - if(++i >= len) { - throw new TypeError(); - } - } - } else { - // If initialValue provided, use it - reduced = args[1]; - } - - // Do the actual reduce - for(;i < len; ++i) { - if(i in arr) { - reduced = reduceFunc(reduced, arr[i], i, arr); - } - } - - return reduced; - }; - - function identity(x) { - return x; - } - - return when; -}); -})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }, this); diff --git a/js/package.json b/js/package.json index 5b8e46d8..b278c08b 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "mopidy", - "version": "0.1.1", + "version": "0.2.0", "description": "Client lib for controlling a Mopidy music server over a WebSocket", "homepage": "http://www.mopidy.com/", "author": { @@ -16,13 +16,13 @@ "dependencies": { "bane": "~1.0.0", "faye-websocket": "~0.7.0", - "when": "~2.4.0" + "when": "~2.7.0" }, "devDependencies": { "buster": "~0.6.13", "grunt": "~0.4.1", "grunt-buster": "~0.2.1", - "grunt-contrib-concat": "~0.3.0", + "grunt-browserify": "~1.3.0", "grunt-contrib-jshint": "~0.6.4", "grunt-contrib-uglify": "~0.2.4", "grunt-contrib-watch": "~0.5.3", diff --git a/js/src/mopidy.js b/js/src/mopidy.js index 980256b5..1667f9b1 100644 --- a/js/src/mopidy.js +++ b/js/src/mopidy.js @@ -1,10 +1,8 @@ -/*global exports:false, require:false*/ +/*global module:true, require:false*/ -if (typeof module === "object" && typeof require === "function") { - var bane = require("bane"); - var websocket = require("faye-websocket"); - var when = require("when"); -} +var bane = require("bane"); +var websocket = require("../lib/websocket/"); +var when = require("when"); function Mopidy(settings) { if (!(this instanceof Mopidy)) { @@ -26,11 +24,7 @@ function Mopidy(settings) { } } -if (typeof module === "object" && typeof require === "function") { - Mopidy.WebSocket = websocket.Client; -} else { - Mopidy.WebSocket = window.WebSocket; -} +Mopidy.WebSocket = websocket.Client; Mopidy.prototype._configure = function (settings) { var currentHost = (typeof document !== "undefined" && @@ -295,6 +289,4 @@ Mopidy.prototype._snakeToCamel = function (name) { }); }; -if (typeof exports === "object") { - exports.Mopidy = Mopidy; -} +module.exports = Mopidy; diff --git a/js/test/mopidy-test.js b/js/test/mopidy-test.js index 0bf97f60..ee34d845 100644 --- a/js/test/mopidy-test.js +++ b/js/test/mopidy-test.js @@ -2,7 +2,7 @@ if (typeof module === "object" && typeof require === "function") { var buster = require("buster"); - var Mopidy = require("../src/mopidy").Mopidy; + var Mopidy = require("../src/mopidy"); var when = require("when"); } diff --git a/mopidy/http/data/mopidy.js b/mopidy/http/data/mopidy.js index 3e4e832e..857d826b 100644 --- a/mopidy/http/data/mopidy.js +++ b/mopidy/http/data/mopidy.js @@ -1,7 +1,11 @@ -/*! Mopidy.js - built 2013-09-17 +/*! Mopidy.js - built 2013-12-15 * http://www.mopidy.com/ * Copyright (c) 2013 Stein Magnus Jodal and contributors * Licensed under the Apache License, Version 2.0 */ +!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.Mopidy=e():"undefined"!=typeof global?global.Mopidy=e():"undefined"!=typeof self&&(self.Mopidy=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); }; - window.define.amd = {}; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); } +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +},{}],4:[function(require,module,exports){ +var process=require("__browserify_process");/** @license MIT License (c) copyright 2011-2013 original author or authors */ + /** * A lightweight CommonJS Promises/A and when() implementation * when is part of the cujo.js family of libraries (http://cujojs.com/) @@ -187,9 +236,9 @@ if (typeof window !== "undefined") { * * @author Brian Cavalier * @author John Hann - * @version 2.4.0 + * @version 2.7.0 */ -(function(define, global) { 'use strict'; +(function(define) { 'use strict'; define(function (require) { // Public API @@ -230,7 +279,11 @@ define(function (require) { function when(promiseOrValue, onFulfilled, onRejected, onProgress) { // Get a trusted promise for the input promiseOrValue, and then // register promise handlers - return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); + return cast(promiseOrValue).then(onFulfilled, onRejected, onProgress); + } + + function cast(x) { + return x instanceof Promise ? x : resolve(x); } /** @@ -247,102 +300,118 @@ define(function (require) { 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; + var promisePrototype = Promise.prototype; - args = arguments; - sendMessage = this._message; + /** + * Register handlers for this promise. + * @param [onFulfilled] {Function} fulfillment handler + * @param [onRejected] {Function} rejection handler + * @param [onProgress] {Function} progress handler + * @return {Promise} new Promise + */ + promisePrototype.then = function(onFulfilled, onRejected, onProgress) { + /*jshint unused:false*/ + var args, sendMessage; - return _promise(function(resolve, reject, notify) { - sendMessage('when', args, resolve, notify); - }, this._status && this._status.observed()); - }, + args = arguments; + sendMessage = this._message; - /** - * Register a rejection handler. Shortcut for .then(undefined, onRejected) - * @param {function?} onRejected - * @return {Promise} - */ - otherwise: function(onRejected) { - return this.then(undef, onRejected); - }, + return _promise(function(resolve, reject, notify) { + sendMessage('when', args, resolve, notify); + }, this._status && this._status.observed()); + }; - /** - * Ensures that onFulfilledOrRejected will be called regardless of whether - * this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT - * receive the promises' value or reason. Any returned value will be disregarded. - * onFulfilledOrRejected may throw or return a rejected promise to signal - * an additional error. - * @param {function} onFulfilledOrRejected handler to be called regardless of - * fulfillment or rejection - * @returns {Promise} - */ - ensure: function(onFulfilledOrRejected) { - return this.then(injectHandler, injectHandler)['yield'](this); + /** + * Register a rejection handler. Shortcut for .then(undefined, onRejected) + * @param {function?} onRejected + * @return {Promise} + */ + promisePrototype['catch'] = promisePrototype.otherwise = function(onRejected) { + return this.then(undef, onRejected); + }; - function injectHandler() { - return resolve(onFulfilledOrRejected()); - } - }, + /** + * Ensures that onFulfilledOrRejected will be called regardless of whether + * this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT + * receive the promises' value or reason. Any returned value will be disregarded. + * onFulfilledOrRejected may throw or return a rejected promise to signal + * an additional error. + * @param {function} onFulfilledOrRejected handler to be called regardless of + * fulfillment or rejection + * @returns {Promise} + */ + promisePrototype['finally'] = promisePrototype.ensure = function(onFulfilledOrRejected) { + return typeof onFulfilledOrRejected === 'function' + ? this.then(injectHandler, injectHandler)['yield'](this) + : this; - /** - * 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; - }); - }, - - /** - * 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 - * i.e. onFulfilled.apply(undefined, array). - * @param {function} onFulfilled function to receive spread arguments - * @return {Promise} - */ - spread: function(onFulfilled) { - return this.then(function(array) { - // array may contain promises, so resolve its contents. - return all(array, function(array) { - return onFulfilled.apply(undef, array); - }); - }); - }, - - /** - * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected) - * @deprecated - */ - always: function(onFulfilledOrRejected, onProgress) { - return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); + function injectHandler() { + return resolve(onFulfilledOrRejected()); } }; + /** + * Terminate a promise chain by handling the ultimate fulfillment value or + * rejection reason, and assuming responsibility for all errors. if an + * error propagates out of handleResult or handleFatalError, it will be + * rethrown to the host, resulting in a loud stack track on most platforms + * and a crash on some. + * @param {function?} handleResult + * @param {function?} handleError + * @returns {undefined} + */ + promisePrototype.done = function(handleResult, handleError) { + this.then(handleResult, handleError).otherwise(crash); + }; + + /** + * 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. + */ + promisePrototype['yield'] = function(value) { + return this.then(function() { + return value; + }); + }; + + /** + * Runs a side effect when this promise fulfills, without changing the + * fulfillment value. + * @param {function} onFulfilledSideEffect + * @returns {Promise} + */ + promisePrototype.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 + * i.e. onFulfilled.apply(undefined, array). + * @param {function} onFulfilled function to receive spread arguments + * @return {Promise} + */ + promisePrototype.spread = function(onFulfilled) { + return this.then(function(array) { + // array may contain promises, so resolve its contents. + return all(array, function(array) { + return onFulfilled.apply(undef, array); + }); + }); + }; + + /** + * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected) + * @deprecated + */ + promisePrototype.always = function(onFulfilledOrRejected, onProgress) { + return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); + }; + /** * Returns a resolved promise. The returned promise will be * - fulfilled with promiseOrValue if it is a value, or @@ -499,13 +568,17 @@ define(function (require) { return; } - value = coerce(val); - scheduleConsumers(consumers, value); + var queue = consumers; consumers = undef; - if(status) { - updateStatus(value, status); - } + enqueue(function () { + value = coerce(self, val); + if(status) { + updateStatus(value, status); + } + runHandlers(queue, value); + }); + } /** @@ -522,11 +595,24 @@ define(function (require) { */ function promiseNotify(update) { if(consumers) { - scheduleConsumers(consumers, progressed(update)); + var queue = consumers; + enqueue(function () { + runHandlers(queue, progressed(update)); + }); } } } + /** + * Run a queue of functions as quickly as possible, passing + * value to each. + */ + function runHandlers(queue, value) { + for (var i = 0; i < queue.length; i++) { + queue[i](value); + } + } + /** * Creates a fulfilled, local promise as a proxy for a value * NOTE: must never be exposed @@ -590,8 +676,6 @@ define(function (require) { /** * Coerces x to a trusted Promise - * - * @private * @param {*} x thing to coerce * @returns {*} Guaranteed to return a trusted Promise. If x * is trusted, returns x, otherwise, returns a new, trusted, already-resolved @@ -599,34 +683,35 @@ define(function (require) { * * the resolution value of x if it's a foreign promise, or * * x if it's a value */ - function coerce(x) { + function coerce(self, x) { + if (x === self) { + return rejected(new TypeError()); + } + if (x instanceof Promise) { return x; } - if (!(x === Object(x) && 'then' in x)) { - return fulfilled(x); + try { + var untrustedThen = x === Object(x) && x.then; + + return typeof untrustedThen === 'function' + ? assimilate(untrustedThen, x) + : fulfilled(x); + } catch(e) { + return rejected(e); } + } - return promise(function(resolve, reject, notify) { - enqueue(function() { - try { - // We must check and assimilate in the same tick, but not the - // current tick, careful only to access promiseOrValue.then once. - var untrustedThen = x.then; - - if(typeof untrustedThen === 'function') { - fcall(untrustedThen, x, resolve, reject, notify); - } else { - // It's a value, create a fulfilled wrapper - resolve(fulfilled(x)); - } - - } catch(e) { - // Something went wrong, reject - reject(e); - } - }); + /** + * Safely assimilates a foreign thenable by wrapping it in a trusted promise + * @param {function} untrustedThen x's then() method + * @param {object|function} x thenable + * @returns {Promise} + */ + function assimilate(untrustedThen, x) { + return promise(function (resolve, reject) { + fcall(untrustedThen, x, resolve, reject); }); } @@ -660,22 +745,6 @@ define(function (require) { } }; - /** - * Schedule a task that will process a list of handlers - * in the next queue drain run. - * @private - * @param {Array} handlers queue of handlers to execute - * @param {*} value passed as the only arg to each handler - */ - function scheduleConsumers(handlers, value) { - enqueue(function() { - var handler, i = 0; - while (handler = handlers[i++]) { - handler(value); - } - }); - } - function updateStatus(value, status) { value.then(statusFulfilled, statusRejected); @@ -879,12 +948,11 @@ define(function (require) { function resolveOne(item, i) { when(item, mapFunc, fallback).then(function(mapped) { results[i] = mapped; - notify(mapped); if(!--toResolve) { resolve(results); } - }, reject); + }, reject, notify); } } }); @@ -961,8 +1029,8 @@ define(function (require) { // var reduceArray, slice, fcall, nextTick, handlerQueue, - setTimeout, funcProto, call, arrayProto, monitorApi, - cjsRequire, undef; + funcProto, call, arrayProto, monitorApi, + capturedSetTimeout, cjsRequire, MutationObs, undef; cjsRequire = require; @@ -992,39 +1060,39 @@ define(function (require) { * processing until it is truly empty. */ function drainQueue() { - var task, i = 0; - - while(task = handlerQueue[i++]) { - task(); - } - + runHandlers(handlerQueue); handlerQueue = []; } - // capture setTimeout to avoid being caught by fake timers - // used in time based tests - setTimeout = global.setTimeout; - // Allow attaching the monitor to when() if env has no console - monitorApi = typeof console != 'undefined' ? console : when; + 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) { + // Sniff "best" async scheduling option + // Prefer process.nextTick or MutationObserver, then check for + // vertx and finally fall back to setTimeout + /*global process,document,setTimeout,MutationObserver,WebKitMutationObserver*/ + if (typeof process === 'object' && process.nextTick) { nextTick = process.nextTick; + } else if(MutationObs = + (typeof MutationObserver === 'function' && MutationObserver) || + (typeof WebKitMutationObserver === 'function' && WebKitMutationObserver)) { + nextTick = (function(document, MutationObserver, drainQueue) { + var el = document.createElement('div'); + new MutationObserver(drainQueue).observe(el, { attributes: true }); + + return function() { + el.setAttribute('x', 'x'); + }; + }(document, MutationObs, drainQueue)); } else { try { // vert.x 1.x || 2.x nextTick = cjsRequire('vertx').runOnLoop || cjsRequire('vertx').runOnContext; } catch(ignore) { - nextTick = function(t) { setTimeout(t, 0); }; + // capture setTimeout to avoid being caught by fake timers + // used in time based tests + capturedSetTimeout = setTimeout; + nextTick = function(t) { capturedSetTimeout(t, 0); }; } } @@ -1095,15 +1163,28 @@ define(function (require) { return x; } + function crash(fatalError) { + if(typeof monitorApi.reportUnhandled === 'function') { + monitorApi.reportUnhandled(); + } else { + enqueue(function() { + throw fatalError; + }); + } + + throw fatalError; + } + return when; }); -})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }, this); +})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }); -if (typeof module === "object" && typeof require === "function") { - var bane = require("bane"); - var websocket = require("faye-websocket"); - var when = require("when"); -} +},{"__browserify_process":3}],5:[function(require,module,exports){ +/*global module:true, require:false*/ + +var bane = require("bane"); +var websocket = require("../lib/websocket/"); +var when = require("when"); function Mopidy(settings) { if (!(this instanceof Mopidy)) { @@ -1125,11 +1206,7 @@ function Mopidy(settings) { } } -if (typeof module === "object" && typeof require === "function") { - Mopidy.WebSocket = websocket.Client; -} else { - Mopidy.WebSocket = window.WebSocket; -} +Mopidy.WebSocket = websocket.Client; Mopidy.prototype._configure = function (settings) { var currentHost = (typeof document !== "undefined" && @@ -1394,6 +1471,9 @@ Mopidy.prototype._snakeToCamel = function (name) { }); }; -if (typeof exports === "object") { - exports.Mopidy = Mopidy; -} +module.exports = Mopidy; + +},{"../lib/websocket/":1,"bane":2,"when":4}]},{},[5]) +(5) +}); +; \ No newline at end of file diff --git a/mopidy/http/data/mopidy.min.js b/mopidy/http/data/mopidy.min.js index 75d9fff1..5e61a3f6 100644 --- a/mopidy/http/data/mopidy.min.js +++ b/mopidy/http/data/mopidy.min.js @@ -1,5 +1,5 @@ -/*! Mopidy.js - built 2013-09-17 +/*! Mopidy.js - built 2013-12-15 * http://www.mopidy.com/ * Copyright (c) 2013 Stein Magnus Jodal and contributors * Licensed under the Apache License, Version 2.0 */ -function Mopidy(a){return this instanceof Mopidy?(this._settings=this._configure(a||{}),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(a)}if(("function"==typeof define&&define.amd&&function(a){define("bane",a)}||"object"==typeof module&&function(a){module.exports=a()}||function(a){this.bane=a()})(function(){"use strict";function a(a,b,c){var d,e=c.length;if(e>0)for(d=0;e>d;++d)c[d](a,b);else setTimeout(function(){throw b.message=a+" listener threw error: "+b.message,b},0)}function b(a){if("function"!=typeof a)throw new TypeError("Listener is not function");return a}function c(a){return a.supervisors||(a.supervisors=[]),a.supervisors}function d(a,b){return a.listeners||(a.listeners={}),b&&!a.listeners[b]&&(a.listeners[b]=[]),b?a.listeners[b]:a.listeners}function e(a){return a.errbacks||(a.errbacks=[]),a.errbacks}function f(f){function h(b,c,d){try{c.listener.apply(c.thisp||f,d)}catch(g){a(b,g,e(f))}}return f=f||{},f.on=function(a,e,f){return"function"==typeof a?c(this).push({listener:a,thisp:e}):(d(this,a).push({listener:b(e),thisp:f}),void 0)},f.off=function(a,b){var f,g,h,i;if(!a){f=c(this),f.splice(0,f.length),g=d(this);for(h in g)g.hasOwnProperty(h)&&(f=d(this,h),f.splice(0,f.length));return f=e(this),f.splice(0,f.length),void 0}if("function"==typeof a?(f=c(this),b=a):f=d(this,a),!b)return f.splice(0,f.length),void 0;for(h=0,i=f.length;i>h;++h)if(f[h].listener===b)return f.splice(h,1),void 0},f.once=function(a,b,c){var d=function(){f.off(a,d),b.apply(this,arguments)};f.on(a,d,c)},f.bind=function(a,b){var c,d,e;if(b)for(d=0,e=b.length;e>d;++d){if("function"!=typeof a[b[d]])throw new Error("No such method "+b[d]);this.on(b[d],a[b[d]],a)}else for(c in a)"function"==typeof a[c]&&this.on(c,a[c],a);return a},f.emit=function(a){var b,e,f=c(this),i=g.call(arguments);for(b=0,e=f.length;e>b;++b)h(a,f[b],i);for(f=d(this,a).slice(),i=g.call(arguments,1),b=0,e=f.length;e>b;++b)h(a,f[b],i)},f.errback=function(a){this.errbacks||(this.errbacks=[]),this.errbacks.push(b(a))},f}var g=Array.prototype.slice;return{createEventEmitter:f}}),"undefined"!=typeof window&&(window.define=function(a){try{delete window.define}catch(b){window.define=void 0}window.when=a()},window.define.amd={}),function(a,b){"use strict";a(function(a){function c(a,b,c,d){return e(a).then(b,c,d)}function d(a,b){this._message=a,this.inspect=b}function e(a){return h(function(b){b(a)})}function f(a){return c(a,k)}function g(){function a(a,f,g){b.resolve=b.resolver.resolve=function(b){return d?e(b):(d=!0,a(b),c)},b.reject=b.resolver.reject=function(a){return d?e(k(a)):(d=!0,f(a),c)},b.notify=b.resolver.notify=function(a){return g(a),a}}var b,c,d;return b={promise:S,resolve:S,reject:S,notify:S,resolver:{resolve:S,reject:S,notify:S}},b.promise=c=h(a),b}function h(a){return i(a,Q.PromiseStatus&&Q.PromiseStatus())}function i(a,b){function c(a,b,c,d){function e(e){e._message(a,b,c,d)}l?l.push(e):E(function(){e(j)})}function e(){return j?j.inspect():D()}function f(a){l&&(j=n(a),q(l,j),l=S,b&&r(j,b))}function g(a){f(k(a))}function h(a){l&&q(l,m(a))}var i,j,l=[];i=new d(c,e),i._status=b;try{a(f,g,h)}catch(o){g(o)}return i}function j(a){return l(new o(a),function(){return B(a)})}function k(a){return l(new p(a),function(){return C(a)})}function l(a,b){return new d(function(b,c,d){try{d(a[b].apply(a,c))}catch(e){d(k(e))}},b)}function m(a){return new d(function(b,c,d,e){var f=c[2];try{e("function"==typeof f?f(a):a)}catch(g){e(g)}})}function n(a){return a instanceof d?a:a===Object(a)&&"then"in a?h(function(b,c,d){E(function(){try{var e=a.then;"function"==typeof e?J(e,a,b,c,d):b(j(a))}catch(f){c(f)}})}):j(a)}function o(a){this.value=a}function p(a){this.reason=a}function q(a,b){E(function(){for(var c,d=0;c=a[d++];)c(b)})}function r(a,b){function c(){b.fulfilled()}function d(a){b.rejected(a)}a.then(c,d)}function s(a){return a&&"function"==typeof a.then}function t(a,b,d,e,f){return c(a,function(a){function g(d,e,f){function g(a){n(a)}function h(a){m(a)}var i,j,k,l,m,n,o,p;if(o=a.length>>>0,i=Math.max(0,Math.min(b,o)),k=[],j=o-i+1,l=[],i)for(n=function(a){l.push(a),--j||(m=n=G,e(l))},m=function(a){k.push(a),--i||(m=n=G,d(k))},p=0;o>p;++p)p in a&&c(a[p],h,g,f);else d(k)}return h(g).then(d,e,f)})}function u(a,b,c,d){function e(a){return b?b(a[0]):a[0]}return t(a,1,e,c,d)}function v(a,b,c,d){return z(a,G).then(b,c,d)}function w(){return z(arguments,G)}function x(a){return z(a,B,C)}function y(a,b){return z(a,b)}function z(a,b,d){return c(a,function(a){function e(e,f,g){function h(a,h){c(a,b,d).then(function(a){i[h]=a,g(a),--k||e(i)},f)}var i,j,k,l;if(k=j=a.length>>>0,i=[],!k)return e(i),void 0;for(l=0;j>l;l++)l in a?h(a[l],l):--k}return i(e)})}function A(a,b){var d=J(I,arguments,1);return c(a,function(a){var e;return e=a.length,d[0]=function(a,d,f){return c(a,function(a){return c(d,function(c){return b(a,c,f,e)})})},H.apply(a,d)})}function B(a){return{state:"fulfilled",value:a}}function C(a){return{state:"rejected",reason:a}}function D(){return{state:"pending"}}function E(a){1===L.push(a)&&K(F)}function F(){for(var a,b=0;a=L[b++];)a();L=[]}function G(a){return a}c.promise=h,c.resolve=e,c.reject=f,c.defer=g,c.join=w,c.all=v,c.map=y,c.reduce=A,c.settle=x,c.any=u,c.some=t,c.isPromise=s,c.isPromiseLike=s,d.prototype={then:function(){var a,b;return a=arguments,b=this._message,i(function(c,d,e){b("when",a,c,e)},this._status&&this._status.observed())},otherwise:function(a){return this.then(S,a)},ensure:function(a){function b(){return e(a())}return this.then(b,b).yield(this)},yield:function(a){return this.then(function(){return a})},tap:function(a){return this.then(a).yield(this)},spread:function(a){return this.then(function(b){return v(b,function(b){return a.apply(S,b)})})},always:function(a,b){return this.then(a,a,b)}},o.prototype.when=function(a){return"function"==typeof a?a(this.value):this.value},p.prototype.when=function(a,b){if("function"==typeof b)return b(this.reason);throw this.reason};var H,I,J,K,L,M,N,O,P,Q,R,S;if(R=a,L=[],M=b.setTimeout,Q="undefined"!=typeof console?console:c,"function"==typeof setImmediate)K=setImmediate.bind(b);else if("undefined"!=typeof MessageChannel){var T=new MessageChannel;T.port1.onmessage=F,K=function(){T.port2.postMessage(0)}}else if("object"==typeof process&&process.nextTick)K=process.nextTick;else try{K=R("vertx").runOnLoop||R("vertx").runOnContext}catch(U){K=function(a){M(a,0)}}return N=Function.prototype,O=N.call,J=N.bind?O.bind(O):function(a,b){return a.apply(b,I.call(arguments,2))},P=[],I=P.slice,H=P.reduce||function(a){var b,c,d,e,f;if(f=0,b=Object(this),e=b.length>>>0,c=arguments,c.length<=1)for(;;){if(f in b){d=b[f++];break}if(++f>=e)throw new TypeError}else d=c[1];for(;e>f;++f)f in b&&(d=a(d,b[f],f,b));return d},c})}("function"==typeof define&&define.amd?define:function(a){module.exports=a(require)},this),"object"==typeof module&&"function"==typeof require)var bane=require("bane"),websocket=require("faye-websocket"),when=require("when");Mopidy.WebSocket="object"==typeof module&&"function"==typeof require?websocket.Client:window.WebSocket,Mopidy.prototype._configure=function(a){var b="undefined"!=typeof document&&document.location.host||"localhost";return a.webSocketUrl=a.webSocketUrl||"ws://"+b+"/mopidy/ws/",a.autoConnect!==!1&&(a.autoConnect=!0),a.backoffDelayMin=a.backoffDelayMin||1e3,a.backoffDelayMax=a.backoffDelayMax||64e3,a},Mopidy.prototype._getConsole=function(){var a="undefined"!=typeof a&&a||{};return a.log=a.log||function(){},a.warn=a.warn||function(){},a.error=a.error||function(){},a},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===Mopidy.WebSocket.OPEN)return;this._webSocket.close()}this._webSocket=this._settings.webSocket||new Mopidy.WebSocket(this._settings.webSocketUrl),this._webSocket.onclose=function(a){this.emit("websocket:close",a)}.bind(this),this._webSocket.onerror=function(a){this.emit("websocket:error",a)}.bind(this),this._webSocket.onopen=function(){this.emit("websocket:open")}.bind(this),this._webSocket.onmessage=function(a){this.emit("websocket:incomingMessage",a)}.bind(this)},Mopidy.prototype._cleanup=function(a){Object.keys(this._pendingRequests).forEach(function(b){var c=this._pendingRequests[b];delete this._pendingRequests[b],c.reject({message:"WebSocket closed",closeEvent:a})}.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(a){this._console.warn("WebSocket error:",a.stack||a)},Mopidy.prototype._send=function(a){var b=when.defer();switch(this._webSocket.readyState){case Mopidy.WebSocket.CONNECTING:b.resolver.reject({message:"WebSocket is still connecting"});break;case Mopidy.WebSocket.CLOSING:b.resolver.reject({message:"WebSocket is closing"});break;case Mopidy.WebSocket.CLOSED:b.resolver.reject({message:"WebSocket is closed"});break;default:a.jsonrpc="2.0",a.id=this._nextRequestId(),this._pendingRequests[a.id]=b.resolver,this._webSocket.send(JSON.stringify(a)),this.emit("websocket:outgoingMessage",a)}return b.promise},Mopidy.prototype._nextRequestId=function(){var a=-1;return function(){return a+=1}}(),Mopidy.prototype._handleMessage=function(a){try{var b=JSON.parse(a.data);b.hasOwnProperty("id")?this._handleResponse(b):b.hasOwnProperty("event")?this._handleEvent(b):this._console.warn("Unknown message type received. Message was: "+a.data)}catch(c){if(!(c instanceof SyntaxError))throw c;this._console.warn("WebSocket message parsing failed. Message was: "+a.data)}},Mopidy.prototype._handleResponse=function(a){if(!this._pendingRequests.hasOwnProperty(a.id))return this._console.warn("Unexpected response received. Message was:",a),void 0;var b=this._pendingRequests[a.id];delete this._pendingRequests[a.id],a.hasOwnProperty("result")?b.resolve(a.result):a.hasOwnProperty("error")?(b.reject(a.error),this._console.warn("Server returned error:",a.error)):(b.reject({message:"Response without 'result' or 'error' received",data:{response:a}}),this._console.warn("Response without 'result' or 'error' received. Message was:",a))},Mopidy.prototype._handleEvent=function(a){var b=a.event,c=a;delete c.event,this.emit("event:"+this._snakeToCamel(b),c)},Mopidy.prototype._getApiSpec=function(){return this._send({method:"core.describe"}).then(this._createApi.bind(this),this._handleWebSocketError).then(null,this._handleWebSocketError)},Mopidy.prototype._createApi=function(a){var b=function(a){return function(){var b=Array.prototype.slice.call(arguments);return this._send({method:a,params:b})}.bind(this)}.bind(this),c=function(a){var b=a.split(".");return b.length>=1&&"core"===b[0]&&(b=b.slice(1)),b},d=function(a){var b=this;return a.forEach(function(a){a=this._snakeToCamel(a),b[a]=b[a]||{},b=b[a]}.bind(this)),b}.bind(this),e=function(e){var f=c(e),g=this._snakeToCamel(f.slice(-1)[0]),h=d(f.slice(0,-1));h[g]=b(e),h[g].description=a[e].description,h[g].params=a[e].params}.bind(this);Object.keys(a).forEach(e),this.emit("state:online")},Mopidy.prototype._snakeToCamel=function(a){return a.replace(/(_[a-z])/g,function(a){return a.toUpperCase().replace("_","")})},"object"==typeof exports&&(exports.Mopidy=Mopidy); \ No newline at end of file +!function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.Mopidy=a():"undefined"!=typeof global?global.Mopidy=a():"undefined"!=typeof self&&(self.Mopidy=a())}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};a[g][0].call(j.exports,function(b){var c=a[g][1][b];return e(c?c:b)},j,j.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g0)for(d=0;e>d;++d)c[d](a,b);else setTimeout(function(){throw b.message=a+" listener threw error: "+b.message,b},0)}function b(a){if("function"!=typeof a)throw new TypeError("Listener is not function");return a}function c(a){return a.supervisors||(a.supervisors=[]),a.supervisors}function d(a,b){return a.listeners||(a.listeners={}),b&&!a.listeners[b]&&(a.listeners[b]=[]),b?a.listeners[b]:a.listeners}function e(a){return a.errbacks||(a.errbacks=[]),a.errbacks}function f(f){function h(b,c,d){try{c.listener.apply(c.thisp||f,d)}catch(g){a(b,g,e(f))}}return f=f||{},f.on=function(a,e,f){return"function"==typeof a?c(this).push({listener:a,thisp:e}):(d(this,a).push({listener:b(e),thisp:f}),void 0)},f.off=function(a,b){var f,g,h,i;if(!a){f=c(this),f.splice(0,f.length),g=d(this);for(h in g)g.hasOwnProperty(h)&&(f=d(this,h),f.splice(0,f.length));return f=e(this),f.splice(0,f.length),void 0}if("function"==typeof a?(f=c(this),b=a):f=d(this,a),!b)return f.splice(0,f.length),void 0;for(h=0,i=f.length;i>h;++h)if(f[h].listener===b)return f.splice(h,1),void 0},f.once=function(a,b,c){var d=function(){f.off(a,d),b.apply(this,arguments)};f.on(a,d,c)},f.bind=function(a,b){var c,d,e;if(b)for(d=0,e=b.length;e>d;++d){if("function"!=typeof a[b[d]])throw new Error("No such method "+b[d]);this.on(b[d],a[b[d]],a)}else for(c in a)"function"==typeof a[c]&&this.on(c,a[c],a);return a},f.emit=function(a){var b,e,f=c(this),i=g.call(arguments);for(b=0,e=f.length;e>b;++b)h(a,f[b],i);for(f=d(this,a).slice(),i=g.call(arguments,1),b=0,e=f.length;e>b;++b)h(a,f[b],i)},f.errback=function(a){this.errbacks||(this.errbacks=[]),this.errbacks.push(b(a))},f}var g=Array.prototype.slice;return{createEventEmitter:f}})},{}],3:[function(a,b){var c=b.exports={};c.nextTick=function(){var a="undefined"!=typeof window&&window.setImmediate,b="undefined"!=typeof window&&window.postMessage&&window.addEventListener;if(a)return function(a){return window.setImmediate(a)};if(b){var c=[];return window.addEventListener("message",function(a){if(a.source===window&&"process-tick"===a.data&&(a.stopPropagation(),c.length>0)){var b=c.shift();b()}},!0),function(a){c.push(a),window.postMessage("process-tick","*")}}return function(a){setTimeout(a,0)}}(),c.title="browser",c.browser=!0,c.env={},c.argv=[],c.binding=function(){throw new Error("process.binding is not supported")},c.cwd=function(){return"/"},c.chdir=function(){throw new Error("process.chdir is not supported")}},{}],4:[function(b,c){var d=b("__browserify_process");!function(a){"use strict";a(function(a){function b(a,b,d,e){return c(a).then(b,d,e)}function c(a){return a instanceof e?a:f(a)}function e(a,b){this._message=a,this.inspect=b}function f(a){return i(function(b){b(a)})}function g(a){return b(a,m)}function h(){function a(a,e,g){b.resolve=b.resolver.resolve=function(b){return d?f(b):(d=!0,a(b),c)},b.reject=b.resolver.reject=function(a){return d?f(m(a)):(d=!0,e(a),c)},b.notify=b.resolver.notify=function(a){return g(a),a}}var b,c,d;return b={promise:X,resolve:X,reject:X,notify:X,resolver:{resolve:X,reject:X,notify:X}},b.promise=c=i(a),b}function i(a){return j(a,T.PromiseStatus&&T.PromiseStatus())}function j(a,b){function c(a,b,c,d){function e(e){e._message(a,b,c,d)}l?l.push(e):G(function(){e(j)})}function d(){return j?j.inspect():F()}function f(a){if(l){var c=l;l=X,G(function(){j=p(i,a),b&&t(j,b),k(c,j)})}}function g(a){f(m(a))}function h(a){if(l){var b=l;G(function(){k(b,o(a))})}}var i,j,l=[];i=new e(c,d),i._status=b;try{a(f,g,h)}catch(n){g(n)}return i}function k(a,b){for(var c=0;c>>0,i=Math.max(0,Math.min(c,o)),k=[],j=o-i+1,l=[],i)for(n=function(a){l.push(a),--j||(m=n=I,e(l))},m=function(a){k.push(a),--i||(m=n=I,d(k))},p=0;o>p;++p)p in a&&b(a[p],h,g,f);else d(k)}return i(g).then(d,e,f)})}function w(a,b,c,d){function e(a){return b?b(a[0]):a[0]}return v(a,1,e,c,d)}function x(a,b,c,d){return B(a,I).then(b,c,d)}function y(){return B(arguments,I)}function z(a){return B(a,D,E)}function A(a,b){return B(a,b)}function B(a,c,d){return b(a,function(a){function e(e,f,g){function h(a,h){b(a,c,d).then(function(a){i[h]=a,--k||e(i)},f,g)}var i,j,k,l;if(k=j=a.length>>>0,i=[],!k)return e(i),void 0;for(l=0;j>l;l++)l in a?h(a[l],l):--k}return j(e)})}function C(a,c){var d=N(M,arguments,1);return b(a,function(a){var e;return e=a.length,d[0]=function(a,d,f){return b(a,function(a){return b(d,function(b){return c(a,b,f,e)})})},L.apply(a,d)})}function D(a){return{state:"fulfilled",value:a}}function E(a){return{state:"rejected",reason:a}}function F(){return{state:"pending"}}function G(a){1===P.push(a)&&O(H)}function H(){k(P),P=[]}function I(a){return a}function J(a){throw"function"==typeof T.reportUnhandled?T.reportUnhandled():G(function(){throw a}),a}b.promise=i,b.resolve=f,b.reject=g,b.defer=h,b.join=y,b.all=x,b.map=A,b.reduce=C,b.settle=z,b.any=w,b.some=v,b.isPromise=u,b.isPromiseLike=u;var K=e.prototype;K.then=function(){var a,b;return a=arguments,b=this._message,j(function(c,d,e){b("when",a,c,e)},this._status&&this._status.observed())},K["catch"]=K.otherwise=function(a){return this.then(X,a)},K["finally"]=K.ensure=function(a){function b(){return f(a())}return"function"==typeof a?this.then(b,b).yield(this):this},K.done=function(a,b){this.then(a,b).otherwise(J)},K.yield=function(a){return this.then(function(){return a})},K.tap=function(a){return this.then(a).yield(this)},K.spread=function(a){return this.then(function(b){return x(b,function(b){return a.apply(X,b)})})},K.always=function(a,b){return this.then(a,a,b)},r.prototype.when=function(a){return"function"==typeof a?a(this.value):this.value},s.prototype.when=function(a,b){if("function"==typeof b)return b(this.reason);throw this.reason};var L,M,N,O,P,Q,R,S,T,U,V,W,X;if(V=a,P=[],T="undefined"!=typeof console?console:b,"object"==typeof d&&d.nextTick)O=d.nextTick;else if(W="function"==typeof MutationObserver&&MutationObserver||"function"==typeof WebKitMutationObserver&&WebKitMutationObserver)O=function(a,b,c){var d=a.createElement("div");return new b(c).observe(d,{attributes:!0}),function(){d.setAttribute("x","x")}}(document,W,H);else try{O=V("vertx").runOnLoop||V("vertx").runOnContext}catch(Y){U=setTimeout,O=function(a){U(a,0)}}return Q=Function.prototype,R=Q.call,N=Q.bind?R.bind(R):function(a,b){return a.apply(b,M.call(arguments,2))},S=[],M=S.slice,L=S.reduce||function(a){var b,c,d,e,f;if(f=0,b=Object(this),e=b.length>>>0,c=arguments,c.length<=1)for(;;){if(f in b){d=b[f++];break}if(++f>=e)throw new TypeError}else d=c[1];for(;e>f;++f)f in b&&(d=a(d,b[f],f,b));return d},b})}("function"==typeof a&&a.amd?a:function(a){c.exports=a(b)})},{__browserify_process:3}],5:[function(a,b){function c(a){return this instanceof c?(this._settings=this._configure(a||{}),this._console=this._getConsole(),this._backoffDelay=this._settings.backoffDelayMin,this._pendingRequests={},this._webSocket=null,d.createEventEmitter(this),this._delegateEvents(),this._settings.autoConnect&&this.connect(),void 0):new c(a)}var d=a("bane"),e=a("../lib/websocket/"),f=a("when");c.WebSocket=e.Client,c.prototype._configure=function(a){var b="undefined"!=typeof document&&document.location.host||"localhost";return a.webSocketUrl=a.webSocketUrl||"ws://"+b+"/mopidy/ws/",a.autoConnect!==!1&&(a.autoConnect=!0),a.backoffDelayMin=a.backoffDelayMin||1e3,a.backoffDelayMax=a.backoffDelayMax||64e3,a},c.prototype._getConsole=function(){var a="undefined"!=typeof a&&a||{};return a.log=a.log||function(){},a.warn=a.warn||function(){},a.error=a.error||function(){},a},c.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)},c.prototype.connect=function(){if(this._webSocket){if(this._webSocket.readyState===c.WebSocket.OPEN)return;this._webSocket.close()}this._webSocket=this._settings.webSocket||new c.WebSocket(this._settings.webSocketUrl),this._webSocket.onclose=function(a){this.emit("websocket:close",a)}.bind(this),this._webSocket.onerror=function(a){this.emit("websocket:error",a)}.bind(this),this._webSocket.onopen=function(){this.emit("websocket:open")}.bind(this),this._webSocket.onmessage=function(a){this.emit("websocket:incomingMessage",a)}.bind(this)},c.prototype._cleanup=function(a){Object.keys(this._pendingRequests).forEach(function(b){var c=this._pendingRequests[b];delete this._pendingRequests[b],c.reject({message:"WebSocket closed",closeEvent:a})}.bind(this)),this.emit("state:offline")},c.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)},c.prototype._resetBackoffDelay=function(){this._backoffDelay=this._settings.backoffDelayMin},c.prototype.close=function(){this.off("state:offline",this._reconnect),this._webSocket.close()},c.prototype._handleWebSocketError=function(a){this._console.warn("WebSocket error:",a.stack||a)},c.prototype._send=function(a){var b=f.defer();switch(this._webSocket.readyState){case c.WebSocket.CONNECTING:b.resolver.reject({message:"WebSocket is still connecting"});break;case c.WebSocket.CLOSING:b.resolver.reject({message:"WebSocket is closing"});break;case c.WebSocket.CLOSED:b.resolver.reject({message:"WebSocket is closed"});break;default:a.jsonrpc="2.0",a.id=this._nextRequestId(),this._pendingRequests[a.id]=b.resolver,this._webSocket.send(JSON.stringify(a)),this.emit("websocket:outgoingMessage",a)}return b.promise},c.prototype._nextRequestId=function(){var a=-1;return function(){return a+=1}}(),c.prototype._handleMessage=function(a){try{var b=JSON.parse(a.data);b.hasOwnProperty("id")?this._handleResponse(b):b.hasOwnProperty("event")?this._handleEvent(b):this._console.warn("Unknown message type received. Message was: "+a.data)}catch(c){if(!(c instanceof SyntaxError))throw c;this._console.warn("WebSocket message parsing failed. Message was: "+a.data)}},c.prototype._handleResponse=function(a){if(!this._pendingRequests.hasOwnProperty(a.id))return this._console.warn("Unexpected response received. Message was:",a),void 0;var b=this._pendingRequests[a.id];delete this._pendingRequests[a.id],a.hasOwnProperty("result")?b.resolve(a.result):a.hasOwnProperty("error")?(b.reject(a.error),this._console.warn("Server returned error:",a.error)):(b.reject({message:"Response without 'result' or 'error' received",data:{response:a}}),this._console.warn("Response without 'result' or 'error' received. Message was:",a))},c.prototype._handleEvent=function(a){var b=a.event,c=a;delete c.event,this.emit("event:"+this._snakeToCamel(b),c)},c.prototype._getApiSpec=function(){return this._send({method:"core.describe"}).then(this._createApi.bind(this),this._handleWebSocketError).then(null,this._handleWebSocketError)},c.prototype._createApi=function(a){var b=function(a){return function(){var b=Array.prototype.slice.call(arguments);return this._send({method:a,params:b})}.bind(this)}.bind(this),c=function(a){var b=a.split(".");return b.length>=1&&"core"===b[0]&&(b=b.slice(1)),b},d=function(a){var b=this;return a.forEach(function(a){a=this._snakeToCamel(a),b[a]=b[a]||{},b=b[a]}.bind(this)),b}.bind(this),e=function(e){var f=c(e),g=this._snakeToCamel(f.slice(-1)[0]),h=d(f.slice(0,-1));h[g]=b(e),h[g].description=a[e].description,h[g].params=a[e].params}.bind(this);Object.keys(a).forEach(e),this.emit("state:online")},c.prototype._snakeToCamel=function(a){return a.replace(/(_[a-z])/g,function(a){return a.toUpperCase().replace("_","")})},b.exports=c},{"../lib/websocket/":1,bane:2,when:4}]},{},[5])(5)}); \ No newline at end of file