diff --git a/js/Gruntfile.js b/js/Gruntfile.js new file mode 100644 index 00000000..f290250a --- /dev/null +++ b/js/Gruntfile.js @@ -0,0 +1,83 @@ +/*global module:false*/ +module.exports = function (grunt) { + + grunt.initConfig({ + meta: { + banner: "/*! Mopidy.js - built " + + "<%= grunt.template.today('yyyy-mm-dd') %>\n" + + " * http://www.mopidy.com/\n" + + " * Copyright (c) <%= grunt.template.today('yyyy') %> " + + "Stein Magnus Jodal and contributors\n" + + " * Licensed under the Apache License, Version 2.0 */\n", + files: { + own: ["Gruntfile.js", "src/**/*.js", "test/**/*-test.js"], + concat: "../mopidy/frontends/http/data/mopidy.js", + minified: "../mopidy/frontends/http/data/mopidy.min.js" + } + }, + buster: { + test: { + config: "buster.js" + } + }, + concat: { + options: { + banner: "<%= meta.banner %>", + stripBanners: true + }, + all: { + files: { + "<%= meta.files.concat %>": [ + "lib/bane-*.js", + "lib/when-*.js", + "src/mopidy.js" + ] + } + } + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + indent: 4, + latedef: true, + newcap: true, + noarg: true, + sub: true, + quotmark: "double", + undef: true, + unused: true, + eqnull: true, + browser: true, + devel: true, + globals: {} + }, + files: "<%= meta.files.own %>" + }, + uglify: { + options: { + banner: "<%= meta.banner %>" + }, + all: { + files: { + "<%= meta.files.minified %>": ["<%= meta.files.concat %>"] + } + } + }, + watch: { + files: "<%= meta.files.own %>", + tasks: ["default"] + } + }); + + grunt.registerTask("test", ["jshint", "buster"]); + grunt.registerTask("build", ["test", "concat", "uglify"]); + grunt.registerTask("default", ["build"]); + + grunt.loadNpmTasks("grunt-buster"); + grunt.loadNpmTasks("grunt-contrib-concat"); + grunt.loadNpmTasks("grunt-contrib-jshint"); + grunt.loadNpmTasks("grunt-contrib-uglify"); + grunt.loadNpmTasks("grunt-contrib-watch"); +}; diff --git a/js/grunt.js b/js/grunt.js deleted file mode 100644 index 46afc8af..00000000 --- a/js/grunt.js +++ /dev/null @@ -1,72 +0,0 @@ -/*global module:false*/ -module.exports = function (grunt) { - - grunt.initConfig({ - meta: { - banner: "/*! Mopidy.js - built " + - "<%= grunt.template.today('yyyy-mm-dd') %>\n" + - " * http://www.mopidy.com/\n" + - " * Copyright (c) <%= grunt.template.today('yyyy') %> " + - "Stein Magnus Jodal and contributors\n" + - " * Licensed under the Apache License, Version 2.0 */" - }, - dirs: { - dest: "../mopidy/frontends/http/data" - }, - lint: { - files: ["grunt.js", "src/**/*.js", "test/**/*-test.js"] - }, - buster: { - test: { - config: "buster.js" - } - }, - concat: { - dist: { - src: [ - "", - "lib/bane-*.js", - "lib/when-*.js", - "src/mopidy.js" - ], - dest: "<%= dirs.dest %>/mopidy.js" - } - }, - min: { - dist: { - src: ["", ""], - dest: "<%= dirs.dest %>/mopidy.min.js" - } - }, - watch: { - files: "", - tasks: "lint buster concat min" - }, - jshint: { - options: { - curly: true, - eqeqeq: true, - immed: true, - indent: 4, - latedef: true, - newcap: true, - noarg: true, - sub: true, - quotmark: "double", - undef: true, - unused: true, - eqnull: true, - browser: true, - devel: true - }, - globals: {} - }, - uglify: {} - }); - - grunt.registerTask("test", "lint buster"); - grunt.registerTask("build", "lint buster concat min"); - grunt.registerTask("default", "build"); - - grunt.loadNpmTasks("grunt-buster"); -}; diff --git a/js/package.json b/js/package.json index a8737cfb..6638c705 100644 --- a/js/package.json +++ b/js/package.json @@ -1,15 +1,19 @@ { - "name": "mopidy", - "version": "0.0.0", - "devDependencies": { - "buster": "*", - "grunt": "*", - "grunt-buster": "*", - "phantomjs": "*" - }, - "scripts": { - "test": "grunt test", - "build": "grunt build", - "watch": "grunt watch" - } + "name": "mopidy", + "version": "0.0.0", + "devDependencies": { + "buster": "~0.6.12", + "grunt": "~0.4.0", + "grunt-buster": "~0.1.2", + "grunt-contrib-concat": "~0.1.3", + "grunt-contrib-jshint": "~0.2.0", + "grunt-contrib-uglify": "~0.1.2", + "grunt-contrib-watch": "~0.3.1", + "phantomjs": "~1.8.2" + }, + "scripts": { + "test": "grunt test", + "build": "grunt build", + "watch": "grunt watch" + } } diff --git a/mopidy/frontends/http/data/mopidy.js b/mopidy/frontends/http/data/mopidy.js index f41695e6..449e43b3 100644 --- a/mopidy/frontends/http/data/mopidy.js +++ b/mopidy/frontends/http/data/mopidy.js @@ -1,16 +1,7 @@ -/*! Mopidy.js - built 2013-01-16 +/*! Mopidy.js - built 2013-03-12 * http://www.mopidy.com/ * Copyright (c) 2013 Stein Magnus Jodal and contributors * Licensed under the Apache License, Version 2.0 */ - -/** - * BANE - Browser globals, AMD and Node Events - * - * https://github.com/busterjs/bane - * - * @version 0.4.0 - */ - ((typeof define === "function" && define.amd && function (m) { define(m); }) || (typeof module === "object" && function (m) { module.exports = m(); }) || function (m) { this.bane = m(); } @@ -175,8 +166,6 @@ return { createEventEmitter: createEventEmitter }; }); -/** @license MIT License (c) copyright B Cavalier & J Hann */ - /** * A lightweight CommonJS Promises/A and when() implementation * when is part of the cujo.js family of libraries (http://cujojs.com/) @@ -907,8 +896,6 @@ define(['module'], function () { // Boilerplate for AMD, Node, and browser global ); -/*global bane:false, when:false*/ - function Mopidy(settings) { if (!(this instanceof Mopidy)) { return new Mopidy(settings); diff --git a/mopidy/frontends/http/data/mopidy.min.js b/mopidy/frontends/http/data/mopidy.min.js index e727cefd..7bb82336 100644 --- a/mopidy/frontends/http/data/mopidy.min.js +++ b/mopidy/frontends/http/data/mopidy.min.js @@ -1,5 +1,5 @@ -/*! Mopidy.js - built 2013-01-16 +/*! Mopidy.js - built 2013-03-12 * http://www.mopidy.com/ * Copyright (c) 2013 Stein Magnus Jodal and contributors * Licensed under the Apache License, Version 2.0 */ -function Mopidy(e){if(!(this instanceof Mopidy))return new Mopidy(e);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()}(typeof define=="function"&&define.amd&&function(e){define(e)}||typeof module=="object"&&function(e){module.exports=e()}||function(e){this.bane=e()})(function(){"use strict";function t(e,t,n){var r,i=n.length;if(i>0){for(r=0;r>>0,o=Math.max(0,Math.min(t,v)),a=[],u=v-o+1,l=[],c=f();if(!o)c.resolve(a);else{d=c.progress,p=function(e){l.push(e),--u||(h=p=w,c.reject(l))},h=function(e){a.push(e),--o||(h=p=w,c.resolve(a))};for(m=0;m>>0,n=[],l=f();if(!s)l.resolve(n);else{u=l.reject,o=function(i,o){r(i,t).then(function(e){n[o]=e,--s||l.resolve(n)},u)};for(a=0;a2;return r(e,function(e){return t.resolve(i?n:e)},t.reject,t.progress)}function y(e,t){var n,r=0;while(n=e[r++])n(t)}function b(e,t){var n,r=t.length;while(r>e){n=t[--r];if(n!=null&&typeof n!="function")throw new Error("arg "+r+" must be a function")}}function w(){}function E(e){return e}var e,t,n;return r.defer=f,r.resolve=i,r.reject=s,r.join=d,r.all=p,r.some=c,r.any=h,r.map=v,r.reduce=m,r.chain=g,r.isPromise=l,o.prototype={always:function(e,t){return this.then(e,e,t)},otherwise:function(e){return this.then(n,e)}},t=[].slice,e=[].reduce||function(e){var t,n,r,i,s;s=0,t=Object(this),i=t.length>>>0,n=arguments;if(n.length<=1)for(;;){if(s in t){r=t[s++];break}if(++s>=i)throw new TypeError}else r=n[1];for(;sthis._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,e}}(),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)){this._console.warn("Unexpected response received. Message was:",e);return}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&&t[0]==="core"&&(t=t.slice(1)),t},r=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),i=function(i){var s=n(i),o=this._snakeToCamel(s.slice(-1)[0]),u=r(s.slice(0,-1));u[o]=t(i),u[o].description=e[i].description,u[o].params=e[i].params}.bind(this);Object.keys(e).forEach(i),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,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