You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
11 KiB
452 lines
11 KiB
/*jslint laxbreak:true */
|
|
/*jslint laxcomma:true */
|
|
/*jslint loopfunc:true */
|
|
/*jslint strict:true */
|
|
/*jslint browser:true */
|
|
/*jslint devel:true */
|
|
|
|
// Filename: app.js
|
|
define([
|
|
"underscore"
|
|
, "async"
|
|
, "backbone"
|
|
, "jquery"
|
|
, "bootstrap"
|
|
|
|
, "config"
|
|
|
|
, "views/app"
|
|
, "views/footer"
|
|
, "views/sidebar"
|
|
, "views/player"
|
|
, "views/main"
|
|
|
|
, "models/client"
|
|
// , "models/dmap"
|
|
|
|
, "collections/server"
|
|
, "collections/playlist"
|
|
|
|
, "views/client"
|
|
, "views/list"
|
|
|
|
, "text!../templates/app/modal-loading.html"
|
|
, "text!../templates/app/modal-login.html"
|
|
, "text!../templates/app/modal-about.html"
|
|
],
|
|
function(
|
|
_
|
|
, async
|
|
, Backbone
|
|
, $
|
|
, Bootstrap
|
|
|
|
, Config
|
|
|
|
, AppView
|
|
, FooterView
|
|
, SideBarView
|
|
, PlayerView
|
|
, MainView
|
|
|
|
, ClientModel
|
|
//, DMAPModel
|
|
, ServerCollection
|
|
, PlaylistCollection
|
|
|
|
, ClientView
|
|
, ListView
|
|
|
|
, tmplModalLoading
|
|
, tmplModalLogin
|
|
, tmplModalAbout
|
|
) {
|
|
"use strict";
|
|
|
|
var App = Backbone.Router.extend({
|
|
routes: {
|
|
"about": "_routerActionAbout"
|
|
, "playlist": "_routerActionPlaylist"
|
|
, "servers/:server": "_routerActionClient"
|
|
, "playlist/:item": "_routerActionPlaylistItem"
|
|
, ":server/items/:item": "_routerActionServerItem"
|
|
, "*actions": "_routerDefaultAction" // matches http://example.com/#anything-here
|
|
}
|
|
|
|
, historyStart: window.history.length
|
|
|
|
, playlist: null
|
|
|
|
, servers: null
|
|
|
|
, initialize: function() {
|
|
_.bindAll(this
|
|
, "_serversInited"
|
|
|
|
, "_routerActionAbout"
|
|
, "_routerActionPlaylist"
|
|
, "_routerActionClient"
|
|
, "_routerActionItem"
|
|
, "_routerActionServerItem"
|
|
, "_routerDefaultAction"
|
|
|
|
, "_serverInit"
|
|
|
|
, "__showModalAbout"
|
|
);
|
|
|
|
var that = this
|
|
;
|
|
|
|
console.log("app.js::init()");
|
|
|
|
// Setup underscore templates with mustache styles
|
|
_.templateSettings.interpolate = /\{\{(.+?)\}\}/g;
|
|
|
|
this.servers = new ServerCollection();
|
|
|
|
this.playlist = new PlaylistCollection();
|
|
|
|
var serverLength = 0
|
|
, serverCbInit = function (server) {
|
|
if (serverLength > 1) {
|
|
--serverLength;
|
|
} else {
|
|
that._serversInited();
|
|
}
|
|
};
|
|
|
|
async.waterfall([
|
|
function __asyncAppFetchServerCollection(callback) {
|
|
that.servers.fetch({
|
|
success: function __fnApEventServersFetched(collection, modelsAttributes) {
|
|
if (modelsAttributes.length === 0) {
|
|
console.info("Adding default daap server");
|
|
collection.add(Config.daap);
|
|
}
|
|
|
|
callback(null, collection);
|
|
}
|
|
|
|
, error: callback
|
|
});
|
|
}
|
|
|
|
, function __asyncAppAttachStateEvents(servers, callback) {
|
|
serverLength = servers.length;
|
|
|
|
async.forEach(that.servers.models, function __fnAppCbServerInit(server) {
|
|
that._serverInit(server, serverCbInit);
|
|
});
|
|
|
|
that.servers.on("add", function __fnAppCbServerAdded(server) {
|
|
server.save();
|
|
that._serverInit(server);
|
|
});
|
|
|
|
callback(null, servers);
|
|
}
|
|
]
|
|
, function __asyncAppDependenciesReady(err, result) {
|
|
that.Views = {};
|
|
|
|
that.Views.App = new AppView();
|
|
|
|
that.Views.SideBar = new SideBarView({
|
|
el: that.Views.App.Layout.panes.west
|
|
, servers: that.servers
|
|
});
|
|
|
|
that.Views.Player = new PlayerView({
|
|
el: that.Views.App.Layout.panes.north
|
|
, servers: that.servers
|
|
});
|
|
|
|
that.Views.Footer = new FooterView({
|
|
el: that.Views.App.Layout.panes.south
|
|
});
|
|
|
|
that.Views.Main = new MainView({
|
|
el: that.Views.App.Layout.panes.center
|
|
});
|
|
|
|
that.Views.Playlist = new ListView({
|
|
collection: that.playlist
|
|
, type: "playlist"
|
|
});
|
|
|
|
that.Views.Main.setView("playlist", that.Views.Playlist);
|
|
|
|
that.Views.Main.on("action:item", function __fnAppEventMainViewActionItem (event) {
|
|
|
|
});
|
|
|
|
that.Views.Main.on("action:playorder", function __fnAppEventMainViewActionItem (event) {
|
|
that.Views.Player.setPlayIndex(event);
|
|
});
|
|
|
|
that.Views.Footer.on("toggle:sidebar", function __fnAppEventTogglerSiderBar (event) {
|
|
that.Views.App.toggleSideBar();
|
|
that.Views.Main.resizeView();
|
|
});
|
|
|
|
that.Views.Footer.on("toggle:random", function __fnAppEventTogglerRandom (event) {
|
|
that.Views.Player.setRandomState(event);
|
|
});
|
|
|
|
that.Views.Footer.on("toggle:repeat", function __fnAppEventTogglerRepeater (event) {
|
|
that.Views.Player.setRepeatState(event);
|
|
});
|
|
|
|
that.Views.Footer.on("show:about", that.__showModalAbout);
|
|
|
|
that.Views.SideBar.on("add:playlist-item", function __fnAppEventPlaylistAddItem (event) {
|
|
var listItem = that.servers.get(event.hostname).client.collections.databases[event.dbId].get("dmap_listing").get(event.item).attributes
|
|
;
|
|
|
|
delete listItem._id;
|
|
|
|
listItem.id = event.hostname + ":" + event.dbId + ":" + event.item;
|
|
|
|
listItem = _.extend({}, listItem, {
|
|
server: event.hostname
|
|
, database: event.dbId
|
|
, order: new Date().toString()
|
|
});
|
|
|
|
that.playlist.on("add", function __fnAppSavePlaylistModel (model) {
|
|
model.save();
|
|
});
|
|
|
|
that.playlist.add(listItem);
|
|
});
|
|
|
|
that.Views.App.$el.delegate("div.modal", "hidden", function __fnAppEventRemoveModals (event) {
|
|
$(event.target).remove();
|
|
});
|
|
|
|
Backbone.history.start({
|
|
pushState: true
|
|
, root: Config.docRoot
|
|
});
|
|
|
|
// For now let's reset the state when we start up
|
|
//that.navigate("", false);
|
|
|
|
App.__super__.initialize.apply(that, that.routes);
|
|
});
|
|
|
|
return this;
|
|
}
|
|
|
|
, _serversInited: function () {
|
|
var that = this
|
|
;
|
|
|
|
console.debug("All Base Servers Inited!");
|
|
|
|
that.Views.Player.init();
|
|
|
|
that.Views.Player.playAtIndex(0);//Math.floor(Math.random() * that.Views.Player._playIndex.length));
|
|
}
|
|
|
|
, _routerDefaultAction: function () {
|
|
var that = this
|
|
;
|
|
|
|
if (!that.servers || that.servers.length === 0) {
|
|
Backbone.Router.prototype.navigate("about", true);
|
|
} else {
|
|
Backbone.Router.prototype.navigate("playlist", true);
|
|
}
|
|
}
|
|
|
|
, _routerActionAbout: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.__showModalAbout();
|
|
}
|
|
|
|
, _routerActionPlaylist: function () {
|
|
var that = this
|
|
;
|
|
|
|
that.Views.SideBar.select("> ul > li:first");
|
|
that.Views.Main.showView("playlist");
|
|
}
|
|
|
|
, _routerActionClient: function (hostname) {
|
|
var that = this
|
|
, server = this.servers.get(hostname)
|
|
, client = server && server.client || undefined
|
|
;
|
|
|
|
if (server && client) {
|
|
that.Views.SideBar.select(server);
|
|
// FIXME: This needs to take client states into account
|
|
var __fnAppEventClientLoaded = function () {
|
|
if (!that.Views.Main.getView(server.get("hostname"))) {
|
|
that.Views.Main.setView(server.client.attributes.hostname, new ClientView({
|
|
/*el: that.Views.App.Layout.panes.center
|
|
, */server: server
|
|
, appendEl: that.Views.App.Layout.panes.center
|
|
}));
|
|
}
|
|
|
|
that.Views.Main.showView(server.get("hostname"));
|
|
}
|
|
, __fnAppEventClientInitited = function () {
|
|
client.fetchDatabases(function __fnAppCbFetchDatabases() {
|
|
client.fetchDatabase(function __fnAppCbFetchDatabase() {
|
|
client.fetchGenres(function __fnAppCbFetchGenres() {
|
|
client.fetchArtists(function __fnAppCbFetchArtists() {
|
|
client.fetchAlbums(function __fnAppCbFetchAlbums() {
|
|
__fnAppEventClientLoaded();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
if (client.state < 4) {
|
|
client.on("inited", __fnAppEventClientInitited);
|
|
|
|
if (client.state <= 0) {
|
|
client.init();
|
|
}
|
|
} else if (client.state === 4) {
|
|
__fnAppEventClientInitited();
|
|
} else if (client.state > 4) {
|
|
__fnAppEventClientLoaded();
|
|
}
|
|
}
|
|
}
|
|
|
|
, _routerActionPlaylistItem: function (item) {
|
|
var that = this
|
|
;
|
|
|
|
that.Views.SideBar.select("> ul > li:first");
|
|
}
|
|
|
|
, _routerActionServerItem: function (server, item) {
|
|
var that = this
|
|
;
|
|
|
|
that.Views.SideBar.select(server);
|
|
that.Views.Main.showView(server.get("hostname"));
|
|
}
|
|
|
|
, _serverInit: function(server, cbInit) {
|
|
var that = this
|
|
;
|
|
|
|
that.loadingStates = 0;
|
|
that.loadingModal = $(tmplModalLoading);
|
|
that.loadingTimeout = null;
|
|
|
|
server.client.off("all");
|
|
|
|
server.client.on("inited", function __fnAppEventClientInited(client) {
|
|
cbInit(null, client);
|
|
});
|
|
|
|
server.client.on("state", function __fnAppEventClientStateChange(client, state) {
|
|
if (state === ClientModel.defaults._states.loading) {
|
|
if (that.loadingStates === 0) {
|
|
that.loadingTimeout = setTimeout(function __fnAppTimeoutLoadingStat() {
|
|
that.loadingModal.modal();
|
|
that.loadingTimeout = null;
|
|
}, 800);
|
|
}
|
|
|
|
that.loadingStates++;
|
|
} else if (state === ClientModel.defaults._states.loaded) {
|
|
that.loadingStates--;
|
|
|
|
if (that.loadingStates === 0) {
|
|
clearTimeout(that.loadingTimeout);
|
|
if (that.loadingModal.modal) {
|
|
that.loadingModal.modal("hide");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
server.client.on("unauthorized", function __fnAppEventClientUnauthorized(client, xhr) {
|
|
var tmplHtml = _.template(tmplModalLogin)({ dmap_itemname: client.url() })
|
|
, serverModel = that.servers.get(client.attributes.hostname)
|
|
;
|
|
|
|
that._modalLogin = $(tmplHtml);
|
|
|
|
that._modalLogin.find("form").on("submit", function __fnAppEventModalLogin(event) {
|
|
var username = $(event.target).find("input[type='text']").val()
|
|
, password = $(event.target).find("input[type='password']").val()
|
|
, saveCredentials = $(event.target).find("input[type='checkbox']").is(':checked')
|
|
;
|
|
|
|
client.setAuth(username, password);
|
|
|
|
if (saveCredentials) {
|
|
var newModel = (_.clone({}, serverModel.attributes, {username: username, password: password}));
|
|
|
|
serverModel.set("username", username, {silent: true});
|
|
serverModel.set("password", password, {silent: true});
|
|
|
|
serverModel.save(newModel, {silence: true});
|
|
}
|
|
|
|
that._modalLogin.modal("hide");
|
|
|
|
setTimeout(client.init, 250);
|
|
|
|
event.preventDefault();
|
|
});
|
|
|
|
that._modalLogin.delegate("input[type='checkbox']", "click", function __fnEventModalRemember(event) {
|
|
if (event.target.checked === true) {
|
|
if (!confirm("Using this feature will save the login and password locally in clear text, are you sure?")) {
|
|
event.target.checked = false;
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
|
|
setTimeout(function __fnEventAppUnauthorizedDisplayModal() {
|
|
that._modalLogin.modal();
|
|
}, 50);
|
|
});
|
|
|
|
server.client.init();
|
|
}
|
|
|
|
, __showModalAbout: function () {
|
|
var that = this
|
|
;
|
|
|
|
that._modalAbout = $(tmplModalAbout);
|
|
|
|
that._modalAbout.on("hidden", function __fnEventAppModalAboutClosed(event) {
|
|
// FIXME: Doesn't seem to work after 50 >_<
|
|
if (window.history.length === 50) {
|
|
window.history.length = 0;
|
|
that.historyStart = 1;
|
|
}
|
|
console.log(window.history.length, that.historyStart);
|
|
if (window.history.length === that.historyStart) {
|
|
that.navigate("", false);
|
|
} else {
|
|
window.history.back();
|
|
}
|
|
});
|
|
|
|
that._modalAbout.modal();
|
|
}
|
|
});
|
|
|
|
return App;
|
|
});
|