diff --git a/docs/api/http.rst b/docs/api/http.rst
index 6eaebae2..88206c86 100644
--- a/docs/api/http.rst
+++ b/docs/api/http.rst
@@ -91,362 +91,3 @@ Example JSON-RPC response::
The JSON-RPC method ``core.describe`` returns a data structure describing all
available methods. If you're unsure how the core API maps to JSON-RPC, having a
look at the ``core.describe`` response can be helpful.
-
-
-.. _mopidy-js:
-
-Mopidy.js JavaScript library
-============================
-
-We've made a JavaScript library, Mopidy.js, which wraps the WebSocket and gets
-you quickly started with working on your client instead of figuring out how to
-communicate with Mopidy.
-
-
-Getting the library for browser use
------------------------------------
-
-Regular and minified versions of Mopidy.js, ready for use, is installed
-together with Mopidy. When the HTTP extension is enabled, the files are
-available at:
-
-- http://localhost:6680/mopidy/mopidy.js
-- http://localhost:6680/mopidy/mopidy.min.js
-
-You may need to adjust hostname and port for your local setup.
-
-Thus, if you use Mopidy to host your web client, like described above, you can
-load the latest version of Mopidy.js by adding the following script tag to your
-HTML file:
-
-.. code-block:: html
-
-
-
-If you don't use Mopidy to host your web client, you can find the JS files in
-the Git repo at:
-
-- ``mopidy/http/data/mopidy.js``
-- ``mopidy/http/data/mopidy.min.js``
-
-
-Getting the library for Node.js use
------------------------------------
-
-If you want to use Mopidy.js from Node.js instead of a browser, you can install
-Mopidy.js using npm::
-
- npm install mopidy
-
-After npm completes, you can import Mopidy.js using ``require()``:
-
-.. code-block:: js
-
- var Mopidy = require("mopidy");
-
-
-Getting the library for development on the library
---------------------------------------------------
-
-If you want to work on the Mopidy.js library itself, you'll find a complete
-development setup in the ``js/`` dir in our repo. The instructions in
-``js/README.md`` will guide you on your way.
-
-
-Creating an instance
---------------------
-
-Once you got Mopidy.js loaded, you need to create an instance of the wrapper:
-
-.. code-block:: js
-
- var mopidy = new Mopidy();
-
-When you instantiate ``Mopidy()`` without arguments, it will connect to
-the WebSocket at ``/mopidy/ws/`` on the current host. Thus, if you don't host
-your web client using Mopidy's web server, or if you use Mopidy.js from a
-Node.js environment, you'll need to pass the URL to the WebSocket end point:
-
-.. code-block:: js
-
- var mopidy = new Mopidy({
- webSocketUrl: "ws://localhost:6680/mopidy/ws/"
- });
-
-It is also possible to create an instance first and connect to the WebSocket
-later:
-
-.. code-block:: js
-
- var mopidy = new Mopidy({autoConnect: false});
- // ... do other stuff, like hooking up events ...
- mopidy.connect();
-
-
-Hooking up to events
---------------------
-
-Once you have a Mopidy.js object, you can hook up to the events it emits. To
-explore your possibilities, it can be useful to subscribe to all events and log
-them:
-
-.. code-block:: js
-
- mopidy.on(console.log.bind(console));
-
-Several types of events are emitted:
-
-- You can get notified about when the Mopidy.js object is connected to the
- server and ready for method calls, when it's offline, and when it's trying to
- reconnect to the server by looking at the events ``state:online``,
- ``state:offline``, ``reconnectionPending``, and ``reconnecting``.
-
-- You can get events sent from the Mopidy server by looking at the events with
- the name prefix ``event:``, like ``event:trackPlaybackStarted``.
-
-- You can introspect what happens internally on the WebSocket by looking at the
- events emitted with the name prefix ``websocket:``.
-
-Mopidy.js uses the event emitter library `BANE
-`_, so you should refer to BANE's
-short API documentation to see how you can hook up your listeners to the
-different events.
-
-
-Calling core API methods
-------------------------
-
-Once your Mopidy.js object has connected to the Mopidy server and emits the
-``state:online`` event, it is ready to accept core API method calls:
-
-.. code-block:: js
-
- mopidy.on("state:online", function () {
- mopidy.playback.next();
- });
-
-Any calls you make before the ``state:online`` event is emitted will fail. If
-you've hooked up an errback (more on that a bit later) to the promise returned
-from the call, the errback will be called with an error message.
-
-All methods in Mopidy's :ref:`core-api` is available via Mopidy.js. The core
-API attributes is *not* available, but that shouldn't be a problem as we've
-added (undocumented) getters and setters for all of them, so you can access the
-attributes as well from JavaScript.
-
-Both the WebSocket API and the JavaScript API are based on introspection of the
-core Python API. Thus, they will always be up to date and immediately reflect
-any changes we do to the core API.
-
-The best way to explore the JavaScript API, is probably by opening your
-browser's console, and using its tab completion to navigate the API. You'll
-find the Mopidy core API exposed under ``mopidy.playback``,
-``mopidy.tracklist``, ``mopidy.playlists``, and ``mopidy.library``.
-
-All methods in the JavaScript API have an associated data structure describing
-the Python params it expects, and most methods also have the Python API
-documentation available. This is available right there in the browser console,
-by looking at the method's ``description`` and ``params`` attributes:
-
-.. code-block:: js
-
- console.log(mopidy.playback.next.params);
- console.log(mopidy.playback.next.description);
-
-JSON-RPC 2.0 limits method parameters to be sent *either* by-position or
-by-name. Combinations of both, like we're used to from Python, isn't supported
-by JSON-RPC 2.0. To further limit this, Mopidy.js currently only supports
-passing parameters by-position.
-
-Obviously, you'll want to get a return value from many of your method calls.
-Since everything is happening across the WebSocket and maybe even across the
-network, you'll get the results asynchronously. Instead of having to pass
-callbacks and errbacks to every method you call, the methods return "promise"
-objects, which you can use to pipe the future result as input to another
-method, or to hook up callback and errback functions.
-
-.. code-block:: js
-
- var track = mopidy.playback.getCurrentTrack();
- // => ``track`` isn't a track, but a "promise" object
-
-Instead, typical usage will look like this:
-
-.. code-block:: js
-
- var printCurrentTrack = function (track) {
- if (track) {
- console.log("Currently playing:", track.name, "by",
- track.artists[0].name, "from", track.album.name);
- } else {
- console.log("No current track");
- }
- };
-
- mopidy.playback.getCurrentTrack().then(
- printCurrentTrack, console.error.bind(console));
-
-The first function passed to ``then()``, ``printCurrentTrack``, is the callback
-that will be called if the method call succeeds. The second function,
-``console.error``, is the errback that will be called if anything goes wrong.
-If you don't hook up an errback, debugging will be hard as errors will silently
-go missing.
-
-For debugging, you may be interested in errors from function without
-interesting return values as well. In that case, you can pass ``null`` as the
-callback:
-
-.. code-block:: js
-
- mopidy.playback.next().then(null, console.error.bind(console));
-
-The promise objects returned by Mopidy.js adheres to the `CommonJS Promises/A
-`_ standard. We use the
-implementation known as `when.js `_. Please
-refer to when.js' documentation or the standard for further details on how to
-work with promise objects.
-
-
-Cleaning up
------------
-
-If you for some reason want to clean up after Mopidy.js before the web page is
-closed or navigated away from, you can close the WebSocket, unregister all
-event listeners, and delete the object like this:
-
-.. code-block:: js
-
- // Close the WebSocket without reconnecting. Letting the object be garbage
- // collected will have the same effect, so this isn't strictly necessary.
- mopidy.close();
-
- // Unregister all event listeners. If you don't do this, you may have
- // lingering references to the object causing the garbage collector to not
- // clean up after it.
- mopidy.off();
-
- // Delete your reference to the object, so it can be garbage collected.
- mopidy = null;
-
-
-Example to get started with
----------------------------
-
-1. Make sure that you've installed all dependencies required by
- :ref:`ext-http`.
-
-2. Create an empty directory for your web client.
-
-3. Change the :confval:`http/static_dir` config value to point to your new
- directory.
-
-4. Start/restart Mopidy.
-
-5. Create a file in the directory named ``index.html`` containing e.g. "Hello,
- world!".
-
-6. Visit http://localhost:6680/ to confirm that you can view your new HTML file
- there.
-
-7. Include Mopidy.js in your web page:
-
- .. code-block:: html
-
-
-
-8. Add one of the following Mopidy.js examples of how to queue and start
- playback of your first playlist either to your web page or a JavaScript file
- that you include in your web page.
-
- "Imperative" style:
-
- .. code-block:: js
-
- var consoleError = console.error.bind(console);
-
- var trackDesc = function (track) {
- return track.name + " by " + track.artists[0].name +
- " from " + track.album.name;
- };
-
- var queueAndPlayFirstPlaylist = function () {
- mopidy.playlists.getPlaylists().then(function (playlists) {
- var playlist = playlists[0];
- console.log("Loading playlist:", playlist.name);
- mopidy.tracklist.add(playlist.tracks).then(function (tlTracks) {
- mopidy.playback.play(tlTracks[0]).then(function () {
- mopidy.playback.getCurrentTrack().then(function (track) {
- console.log("Now playing:", trackDesc(track));
- }, consoleError);
- }, consoleError);
- }, consoleError);
- }, consoleError);
- };
-
- var mopidy = new Mopidy(); // Connect to server
- mopidy.on(console.log.bind(console)); // Log all events
- mopidy.on("state:online", queueAndPlayFirstPlaylist);
-
- Approximately the same behavior in a more functional style, using chaining
- of promisies.
-
- .. code-block:: js
-
- var consoleError = console.error.bind(console);
-
- var getFirst = function (list) {
- return list[0];
- };
-
- var extractTracks = function (playlist) {
- return playlist.tracks;
- };
-
- var printTypeAndName = function (model) {
- console.log(model.__model__ + ": " + model.name);
- // By returning the playlist, this function can be inserted
- // anywhere a model with a name is piped in the chain.
- return model;
- };
-
- var trackDesc = function (track) {
- return track.name + " by " + track.artists[0].name +
- " from " + track.album.name;
- };
-
- var printNowPlaying = function () {
- // By returning any arguments we get, the function can be inserted
- // anywhere in the chain.
- var args = arguments;
- return mopidy.playback.getCurrentTrack().then(function (track) {
- console.log("Now playing:", trackDesc(track));
- return args;
- });
- };
-
- var queueAndPlayFirstPlaylist = function () {
- mopidy.playlists.getPlaylists()
- // => list of Playlists
- .then(getFirst, consoleError)
- // => Playlist
- .then(printTypeAndName, consoleError)
- // => Playlist
- .then(extractTracks, consoleError)
- // => list of Tracks
- .then(mopidy.tracklist.add, consoleError)
- // => list of TlTracks
- .then(getFirst, consoleError)
- // => TlTrack
- .then(mopidy.playback.play, consoleError)
- // => null
- .then(printNowPlaying, consoleError);
- };
-
- var mopidy = new Mopidy(); // Connect to server
- mopidy.on(console.log.bind(console)); // Log all events
- mopidy.on("state:online", queueAndPlayFirstPlaylist);
-
-9. The web page should now queue and play your first playlist every time your
- load it. See the browser's console for output from the function, any errors,
- and all events that are emitted.
-
diff --git a/docs/api/index.rst b/docs/api/index.rst
index bede978b..291b0e7c 100644
--- a/docs/api/index.rst
+++ b/docs/api/index.rst
@@ -27,3 +27,4 @@ API reference
config
zeroconf
http
+ js
diff --git a/docs/api/js.rst b/docs/api/js.rst
new file mode 100644
index 00000000..c887f3c0
--- /dev/null
+++ b/docs/api/js.rst
@@ -0,0 +1,369 @@
+.. _mopidy-js:
+
+****************************
+Mopidy.js JavaScript library
+****************************
+
+We've made a JavaScript library, Mopidy.js, which wraps the
+:ref:`websocket-api` and gets you quickly started with working on your client
+instead of figuring out how to communicate with Mopidy.
+
+.. warning:: API stability
+
+ Since the Mopidy.js API exposes our internal core API directly it is to be
+ regarded as **experimental**. We cannot promise to keep any form of
+ backwards compatibility between releases as we will need to change the core
+ API while working out how to support new use cases. Thus, if you use this
+ API, you must expect to do small adjustments to your client for every
+ release of Mopidy.
+
+ From Mopidy 1.0 and onwards, we intend to keep the core API far more
+ stable.
+
+
+Getting the library for browser use
+===================================
+
+Regular and minified versions of Mopidy.js, ready for use, is installed
+together with Mopidy. When the HTTP extension is enabled, the files are
+available at:
+
+- http://localhost:6680/mopidy/mopidy.js
+- http://localhost:6680/mopidy/mopidy.min.js
+
+You may need to adjust hostname and port for your local setup.
+
+Thus, if you use Mopidy to host your web client, like described above, you can
+load the latest version of Mopidy.js by adding the following script tag to your
+HTML file:
+
+.. code-block:: html
+
+
+
+If you don't use Mopidy to host your web client, you can find the JS files in
+the Git repo at:
+
+- ``mopidy/http/data/mopidy.js``
+- ``mopidy/http/data/mopidy.min.js``
+
+
+Getting the library for Node.js use
+===================================
+
+If you want to use Mopidy.js from Node.js instead of a browser, you can install
+Mopidy.js using npm::
+
+ npm install mopidy
+
+After npm completes, you can import Mopidy.js using ``require()``:
+
+.. code-block:: js
+
+ var Mopidy = require("mopidy");
+
+
+Getting the library for development on the library
+==================================================
+
+If you want to work on the Mopidy.js library itself, you'll find a complete
+development setup in the ``js/`` dir in our repo. The instructions in
+``js/README.md`` will guide you on your way.
+
+
+Creating an instance
+====================
+
+Once you got Mopidy.js loaded, you need to create an instance of the wrapper:
+
+.. code-block:: js
+
+ var mopidy = new Mopidy();
+
+When you instantiate ``Mopidy()`` without arguments, it will connect to
+the WebSocket at ``/mopidy/ws/`` on the current host. Thus, if you don't host
+your web client using Mopidy's web server, or if you use Mopidy.js from a
+Node.js environment, you'll need to pass the URL to the WebSocket end point:
+
+.. code-block:: js
+
+ var mopidy = new Mopidy({
+ webSocketUrl: "ws://localhost:6680/mopidy/ws/"
+ });
+
+It is also possible to create an instance first and connect to the WebSocket
+later:
+
+.. code-block:: js
+
+ var mopidy = new Mopidy({autoConnect: false});
+ // ... do other stuff, like hooking up events ...
+ mopidy.connect();
+
+
+Hooking up to events
+====================
+
+Once you have a Mopidy.js object, you can hook up to the events it emits. To
+explore your possibilities, it can be useful to subscribe to all events and log
+them:
+
+.. code-block:: js
+
+ mopidy.on(console.log.bind(console));
+
+Several types of events are emitted:
+
+- You can get notified about when the Mopidy.js object is connected to the
+ server and ready for method calls, when it's offline, and when it's trying to
+ reconnect to the server by looking at the events ``state:online``,
+ ``state:offline``, ``reconnectionPending``, and ``reconnecting``.
+
+- You can get events sent from the Mopidy server by looking at the events with
+ the name prefix ``event:``, like ``event:trackPlaybackStarted``.
+
+- You can introspect what happens internally on the WebSocket by looking at the
+ events emitted with the name prefix ``websocket:``.
+
+Mopidy.js uses the event emitter library `BANE
+`_, so you should refer to BANE's
+short API documentation to see how you can hook up your listeners to the
+different events.
+
+
+Calling core API methods
+========================
+
+Once your Mopidy.js object has connected to the Mopidy server and emits the
+``state:online`` event, it is ready to accept core API method calls:
+
+.. code-block:: js
+
+ mopidy.on("state:online", function () {
+ mopidy.playback.next();
+ });
+
+Any calls you make before the ``state:online`` event is emitted will fail. If
+you've hooked up an errback (more on that a bit later) to the promise returned
+from the call, the errback will be called with an error message.
+
+All methods in Mopidy's :ref:`core-api` is available via Mopidy.js. The core
+API attributes is *not* available, but that shouldn't be a problem as we've
+added (undocumented) getters and setters for all of them, so you can access the
+attributes as well from JavaScript.
+
+Both the WebSocket API and the JavaScript API are based on introspection of the
+core Python API. Thus, they will always be up to date and immediately reflect
+any changes we do to the core API.
+
+The best way to explore the JavaScript API, is probably by opening your
+browser's console, and using its tab completion to navigate the API. You'll
+find the Mopidy core API exposed under ``mopidy.playback``,
+``mopidy.tracklist``, ``mopidy.playlists``, and ``mopidy.library``.
+
+All methods in the JavaScript API have an associated data structure describing
+the Python params it expects, and most methods also have the Python API
+documentation available. This is available right there in the browser console,
+by looking at the method's ``description`` and ``params`` attributes:
+
+.. code-block:: js
+
+ console.log(mopidy.playback.next.params);
+ console.log(mopidy.playback.next.description);
+
+JSON-RPC 2.0 limits method parameters to be sent *either* by-position or
+by-name. Combinations of both, like we're used to from Python, isn't supported
+by JSON-RPC 2.0. To further limit this, Mopidy.js currently only supports
+passing parameters by-position.
+
+Obviously, you'll want to get a return value from many of your method calls.
+Since everything is happening across the WebSocket and maybe even across the
+network, you'll get the results asynchronously. Instead of having to pass
+callbacks and errbacks to every method you call, the methods return "promise"
+objects, which you can use to pipe the future result as input to another
+method, or to hook up callback and errback functions.
+
+.. code-block:: js
+
+ var track = mopidy.playback.getCurrentTrack();
+ // => ``track`` isn't a track, but a "promise" object
+
+Instead, typical usage will look like this:
+
+.. code-block:: js
+
+ var printCurrentTrack = function (track) {
+ if (track) {
+ console.log("Currently playing:", track.name, "by",
+ track.artists[0].name, "from", track.album.name);
+ } else {
+ console.log("No current track");
+ }
+ };
+
+ mopidy.playback.getCurrentTrack().then(
+ printCurrentTrack, console.error.bind(console));
+
+The first function passed to ``then()``, ``printCurrentTrack``, is the callback
+that will be called if the method call succeeds. The second function,
+``console.error``, is the errback that will be called if anything goes wrong.
+If you don't hook up an errback, debugging will be hard as errors will silently
+go missing.
+
+For debugging, you may be interested in errors from function without
+interesting return values as well. In that case, you can pass ``null`` as the
+callback:
+
+.. code-block:: js
+
+ mopidy.playback.next().then(null, console.error.bind(console));
+
+The promise objects returned by Mopidy.js adheres to the `CommonJS Promises/A
+`_ standard. We use the
+implementation known as `when.js `_. Please
+refer to when.js' documentation or the standard for further details on how to
+work with promise objects.
+
+
+Cleaning up
+===========
+
+If you for some reason want to clean up after Mopidy.js before the web page is
+closed or navigated away from, you can close the WebSocket, unregister all
+event listeners, and delete the object like this:
+
+.. code-block:: js
+
+ // Close the WebSocket without reconnecting. Letting the object be garbage
+ // collected will have the same effect, so this isn't strictly necessary.
+ mopidy.close();
+
+ // Unregister all event listeners. If you don't do this, you may have
+ // lingering references to the object causing the garbage collector to not
+ // clean up after it.
+ mopidy.off();
+
+ // Delete your reference to the object, so it can be garbage collected.
+ mopidy = null;
+
+
+Example to get started with
+===========================
+
+1. Make sure that you've installed all dependencies required by
+ :ref:`ext-http`.
+
+2. Create an empty directory for your web client.
+
+3. Change the :confval:`http/static_dir` config value to point to your new
+ directory.
+
+4. Start/restart Mopidy.
+
+5. Create a file in the directory named ``index.html`` containing e.g. "Hello,
+ world!".
+
+6. Visit http://localhost:6680/ to confirm that you can view your new HTML file
+ there.
+
+7. Include Mopidy.js in your web page:
+
+ .. code-block:: html
+
+
+
+8. Add one of the following Mopidy.js examples of how to queue and start
+ playback of your first playlist either to your web page or a JavaScript file
+ that you include in your web page.
+
+ "Imperative" style:
+
+ .. code-block:: js
+
+ var consoleError = console.error.bind(console);
+
+ var trackDesc = function (track) {
+ return track.name + " by " + track.artists[0].name +
+ " from " + track.album.name;
+ };
+
+ var queueAndPlayFirstPlaylist = function () {
+ mopidy.playlists.getPlaylists().then(function (playlists) {
+ var playlist = playlists[0];
+ console.log("Loading playlist:", playlist.name);
+ mopidy.tracklist.add(playlist.tracks).then(function (tlTracks) {
+ mopidy.playback.play(tlTracks[0]).then(function () {
+ mopidy.playback.getCurrentTrack().then(function (track) {
+ console.log("Now playing:", trackDesc(track));
+ }, consoleError);
+ }, consoleError);
+ }, consoleError);
+ }, consoleError);
+ };
+
+ var mopidy = new Mopidy(); // Connect to server
+ mopidy.on(console.log.bind(console)); // Log all events
+ mopidy.on("state:online", queueAndPlayFirstPlaylist);
+
+ Approximately the same behavior in a more functional style, using chaining
+ of promisies.
+
+ .. code-block:: js
+
+ var consoleError = console.error.bind(console);
+
+ var getFirst = function (list) {
+ return list[0];
+ };
+
+ var extractTracks = function (playlist) {
+ return playlist.tracks;
+ };
+
+ var printTypeAndName = function (model) {
+ console.log(model.__model__ + ": " + model.name);
+ // By returning the playlist, this function can be inserted
+ // anywhere a model with a name is piped in the chain.
+ return model;
+ };
+
+ var trackDesc = function (track) {
+ return track.name + " by " + track.artists[0].name +
+ " from " + track.album.name;
+ };
+
+ var printNowPlaying = function () {
+ // By returning any arguments we get, the function can be inserted
+ // anywhere in the chain.
+ var args = arguments;
+ return mopidy.playback.getCurrentTrack().then(function (track) {
+ console.log("Now playing:", trackDesc(track));
+ return args;
+ });
+ };
+
+ var queueAndPlayFirstPlaylist = function () {
+ mopidy.playlists.getPlaylists()
+ // => list of Playlists
+ .then(getFirst, consoleError)
+ // => Playlist
+ .then(printTypeAndName, consoleError)
+ // => Playlist
+ .then(extractTracks, consoleError)
+ // => list of Tracks
+ .then(mopidy.tracklist.add, consoleError)
+ // => list of TlTracks
+ .then(getFirst, consoleError)
+ // => TlTrack
+ .then(mopidy.playback.play, consoleError)
+ // => null
+ .then(printNowPlaying, consoleError);
+ };
+
+ var mopidy = new Mopidy(); // Connect to server
+ mopidy.on(console.log.bind(console)); // Log all events
+ mopidy.on("state:online", queueAndPlayFirstPlaylist);
+
+9. The web page should now queue and play your first playlist every time your
+ load it. See the browser's console for output from the function, any errors,
+ and all events that are emitted.
diff --git a/js/README.md b/js/README.md
index 5a04cd66..54bdc502 100644
--- a/js/README.md
+++ b/js/README.md
@@ -41,8 +41,7 @@ After npm completes, you can import Mopidy.js using ``require()``:
Using the library
-----------------
-See Mopidy's [HTTP API
-documentation](http://docs.mopidy.com/en/latest/api/http/).
+See the [Mopidy.js documentation](http://docs.mopidy.com/en/latest/api/js/).
Building from source