(function($) { window.Album = Backbone.Model.extend({ isFirstTrack: function(index) { return index == 0; }, isLastTrack: function(index) { return index >= this.get('tracks').length - 1; }, trackUrlAtIndex: function(index) { if (this.get('tracks').length >= index) { return this.get('tracks')[index].url; } return null; } }); window.Albums = Backbone.Collection.extend({ model: Album, url: "/albums" }); window.Playlist = Albums.extend({ isFirstAlbum: function(index) { return (index == 0) }, isLastAlbum: function(index) { return (index == (this.models.length - 1)) } }); window.Player = Backbone.Model.extend({ defaults: { 'currentAlbumIndex': 0, 'currentTrackIndex': 0, 'state': 'stop' }, initialize: function() { this.playlist = new Playlist(); }, play: function() { this.set({'state': 'play'}); this.trigger('change:currentTrackIndex'); this.logCurrentAlbumAndTrack(); }, pause: function() { this.set({'state': 'pause'}); }, isPlaying: function() { return (this.get('state') == 'play'); }, isStopped: function() { return (!this.isPlaying()); }, currentAlbum: function() { return this.playlist.at(this.get('currentAlbumIndex')); }, currentTrackUrl: function() { var album = this.currentAlbum(); return album.trackUrlAtIndex(this.get('currentTrackIndex')); }, nextTrack: function() { var currentTrackIndex = this.get('currentTrackIndex'), currentAlbumIndex = this.get('currentAlbumIndex'); if (this.currentAlbum().isLastTrack(currentTrackIndex)) { if (this.playlist.isLastAlbum(currentAlbumIndex)) { this.set({'currentAlbumIndex': 0}); this.set({'currentTrackIndex': 0}); } else { this.set({'currentAlbumIndex': currentAlbumIndex + 1}); this.set({'currentTrackIndex': 0}); } } else { this.set({'currentTrackIndex': currentTrackIndex + 1}); } this.logCurrentAlbumAndTrack(); }, prevTrack: function() { var currentTrackIndex = this.get('currentTrackIndex'), currentAlbumIndex = this.get('currentAlbumIndex'), lastModelIndex = 0; if (this.currentAlbum().isFirstTrack(currentTrackIndex)) { if (this.playlist.isFirstAlbum(currentAlbumIndex)) { lastModelIndex = this.playlist.models.length - 1; this.set({'currentAlbumIndex': lastModelIndex}); } else { this.set({'currentAlbumIndex': currentAlbumIndex - 1}); } // In either case, go to last track on album var lastTrackIndex = this.currentAlbum().get('tracks').length - 1; this.set({'currentTrackIndex': lastTrackIndex}); } else { this.set({'currentTrackIndex': currentTrackIndex - 1}); } this.logCurrentAlbumAndTrack(); }, logCurrentAlbumAndTrack: function() { console.log("Player " + this.get('currentAlbumIndex') + ':' + this.get('currentTrackIndex'), this); } }); window.library = new Albums(); window.player = new Player(); window.AlbumView = Backbone.View.extend({ template: "#album-template", tag: 'li', className: 'album', initialize: function() { _.bindAll(this, 'render'); this.initializeTemplate(); }, initializeTemplate: function() { this.template = _.template($(this.template).html()); }, render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; } }); window.PlaylistAlbumView = AlbumView.extend({ events: { 'click .queue.remove': 'removeFromPlaylist' }, initialize: function() { _.bindAll(this, 'render', 'updateState', 'updateTrack', 'remove'); this.initializeTemplate(); this.player = this.options.player; this.player.bind('change:state', this.updateState); this.player.bind('change:currentTrackIndex', this.updateTrack); this.model.bind('remove', this.remove); }, render: function() { $(this.el).html(this.template(this.model.toJSON())); this.updateTrack(); return this; }, updateState: function() { var isAlbumCurrent = (this.player.currentAlbum() === this.model); $(this.el).toggleClass('current', isAlbumCurrent); }, updateTrack: function() { var isAlbumCurrent = (this.player.currentAlbum() === this.model); if (isAlbumCurrent) { var currentTrackIndex = this.player.get('currentTrackIndex'); this.$("li").each(function(index, el) { $(el).toggleClass('current', index == currentTrackIndex); }); } this.updateState(); }, removeFromPlaylist: function() { this.options.playlist.remove(this.model); } }); window.LibraryAlbumView = AlbumView.extend({ events: { 'click .queue.add': 'select' }, select: function() { this.collection.trigger('select', this.model); } }); window.PlaylistView = Backbone.View.extend({ tag: 'section', className: 'playlist', events: { 'click .play': 'play', 'click .pause': 'pause', 'click .next': 'nextTrack', 'click .prev': 'prevTrack' }, initialize: function() { _.bindAll(this, 'render', 'renderAlbum', 'updateTrack', 'queueAlbum'); this.template = _.template($("#playlist-template").html()); this.collection.bind('reset', this.render); this.collection.bind('add', this.renderAlbum); // TODO: May need to bind to currentAlbumIndex too this.player = this.options.player; this.player.bind('change:currentTrackIndex', this.updateTrack); this.createAudio(); this.library = this.options.library; this.library.bind('select', this.queueAlbum); }, createAudio: function() { this.audio = new Audio(); }, render: function() { $(this.el).html(this.template(this.player.toJSON())); this.collection.each(this.renderAlbum); this.$("button.play").toggle(this.player.isStopped()); this.$("button.pause").toggle(this.player.isPlaying()); return this; }, renderAlbum: function(album) { var view = new PlaylistAlbumView({ model: album, player: this.player, playlist: this.collection }); this.$("ul").append(view.render().el); }, updateTrack: function() { this.audio.src = this.player.currentTrackUrl(); if (this.player.get('state') == 'play') { this.audio.play(); } }, queueAlbum: function(album) { this.collection.add(album); }, play: function() { this.player.play(); this.audio.play(); this.$("button.play").hide(); this.$("button.pause").show(); }, pause: function() { this.player.pause(); this.audio.pause(); this.$("button.pause").hide(); this.$("button.play").show(); }, nextTrack: function() { this.player.nextTrack(); }, prevTrack: function() { this.player.prevTrack(); } }); window.LibraryView = Backbone.View.extend({ tagName: 'section', className: 'library', initialize: function() { _.bindAll(this, 'render'); this.template = _.template($('#library-template').html()); this.collection.bind('reset', this.render); }, render: function() { var $albums, collection = this.collection; $(this.el).html(this.template({})); $albums = this.$(".albums"); this.collection.each(function(album) { var view = new LibraryAlbumView({ model: album, collection: collection }); $albums.append(view.render().el); }); return this; } }); window.BackboneTunes = Backbone.Router.extend({ routes: { '': 'home', 'blank': 'blank' }, initialize: function() { this.playlistView = new PlaylistView({ collection: window.player.playlist, player: window.player, library: window.library }); this.libraryView = new LibraryView({ collection: window.library }); }, home: function() { $('#container').empty(); $("#container").append(this.playlistView.render().el); $("#container").append(this.libraryView.render().el); }, blank: function() { $('#container').empty(); $('#container').text('blank'); } }); $(document).ready(function() { window.App = new BackboneTunes(); Backbone.history.start({pushState: true}); window.App.home(); }); })(jQuery); // vim:sw=4:ts=4:expandtab