PageRenderTime 19ms CodeModel.GetById 1ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/9-final/public/js/Tunes.js

https://bitbucket.org/edwardstlouis/backbonepeep1
JavaScript | 346 lines | 278 code | 65 blank | 3 comment | 17 complexity | d0aea196ae1bdcadd9d9c3eb3b86fc86 MD5 | raw file
  1(function($) {
  2    window.Album = Backbone.Model.extend({
  3
  4        isFirstTrack: function(index) {
  5            return index == 0;
  6        },
  7
  8        isLastTrack: function(index) {
  9            return index >= this.get('tracks').length - 1;
 10        },
 11
 12        trackUrlAtIndex: function(index) {
 13            if (this.get('tracks').length >= index) { 
 14                return this.get('tracks')[index].url;
 15            }
 16            return null;
 17        }
 18
 19    });
 20
 21    window.Albums = Backbone.Collection.extend({
 22        model: Album,
 23        url: "/albums"
 24    });
 25
 26    window.Playlist = Albums.extend({
 27
 28        isFirstAlbum: function(index) {
 29            return (index == 0)
 30        },
 31        
 32        isLastAlbum: function(index) {
 33            return (index == (this.models.length - 1))
 34        }
 35
 36    });
 37
 38    window.Player = Backbone.Model.extend({
 39        defaults: {
 40            'currentAlbumIndex': 0,
 41            'currentTrackIndex': 0,
 42            'state': 'stop'
 43        },
 44        
 45        initialize: function() {
 46            this.playlist = new Playlist();
 47        },
 48        
 49        play: function() {
 50            this.set({'state': 'play'});
 51            this.trigger('change:currentTrackIndex');
 52            this.logCurrentAlbumAndTrack();
 53        },
 54
 55        pause: function() {
 56            this.set({'state': 'pause'});
 57        },
 58
 59        isPlaying: function() {
 60            return (this.get('state') == 'play');
 61        },
 62        
 63        isStopped: function() {
 64            return (!this.isPlaying());
 65        },
 66
 67        currentAlbum: function() {
 68            return this.playlist.at(this.get('currentAlbumIndex'));
 69        },
 70
 71        currentTrackUrl: function() {
 72            var album = this.currentAlbum();
 73            return album.trackUrlAtIndex(this.get('currentTrackIndex'));
 74        },
 75
 76        nextTrack: function() {
 77            var currentTrackIndex = this.get('currentTrackIndex'),
 78                currentAlbumIndex = this.get('currentAlbumIndex');
 79            if (this.currentAlbum().isLastTrack(currentTrackIndex)) {
 80                if (this.playlist.isLastAlbum(currentAlbumIndex)) {
 81                    this.set({'currentAlbumIndex': 0});
 82                    this.set({'currentTrackIndex': 0});
 83                } else {
 84                    this.set({'currentAlbumIndex': currentAlbumIndex + 1});
 85                    this.set({'currentTrackIndex': 0});
 86                }
 87            } else {
 88                this.set({'currentTrackIndex': currentTrackIndex + 1});
 89            }
 90            this.logCurrentAlbumAndTrack();
 91        },
 92
 93        prevTrack: function() {
 94            var currentTrackIndex = this.get('currentTrackIndex'),
 95                currentAlbumIndex = this.get('currentAlbumIndex'),
 96                lastModelIndex = 0;
 97            if (this.currentAlbum().isFirstTrack(currentTrackIndex)) {
 98                if (this.playlist.isFirstAlbum(currentAlbumIndex)) {
 99                    lastModelIndex = this.playlist.models.length - 1;
100                    this.set({'currentAlbumIndex': lastModelIndex});
101                } else {
102                    this.set({'currentAlbumIndex': currentAlbumIndex - 1});
103                }
104                // In either case, go to last track on album
105                var lastTrackIndex = 
106                    this.currentAlbum().get('tracks').length - 1;
107                this.set({'currentTrackIndex': lastTrackIndex});                
108            } else {
109                this.set({'currentTrackIndex': currentTrackIndex - 1});
110            }
111            this.logCurrentAlbumAndTrack();
112        },
113        
114        logCurrentAlbumAndTrack: function() {
115            console.log("Player " + 
116                        this.get('currentAlbumIndex') + ':' + 
117                        this.get('currentTrackIndex'), this);
118        }
119        
120    });
121
122    window.library  = new Albums();
123    window.player   = new Player();
124
125    window.AlbumView = Backbone.View.extend({
126        template: "#album-template",
127        tag: 'li',
128        className: 'album',
129
130        initialize: function() {
131            _.bindAll(this, 'render');
132            this.initializeTemplate();
133        },
134        
135        initializeTemplate: function() {
136            this.template = _.template($(this.template).html());
137        },
138
139        render: function() {
140            $(this.el).html(this.template(this.model.toJSON()));
141            return this;
142        }
143    });
144
145    window.PlaylistAlbumView = AlbumView.extend({
146        events: {
147            'click .queue.remove': 'removeFromPlaylist'
148        },
149
150        initialize: function() {
151            _.bindAll(this, 'render',
152                            'updateState',
153                            'updateTrack',
154                            'remove');
155            this.initializeTemplate();
156
157            this.player = this.options.player;
158            this.player.bind('change:state', this.updateState);
159            this.player.bind('change:currentTrackIndex', this.updateTrack);
160
161            this.model.bind('remove', this.remove);
162        },
163
164        render: function() {
165            $(this.el).html(this.template(this.model.toJSON()));
166            this.updateTrack();
167            return this;
168        },
169
170        updateState: function() {
171            var isAlbumCurrent = (this.player.currentAlbum() === this.model);
172            $(this.el).toggleClass('current', isAlbumCurrent);
173        },
174
175        updateTrack: function() {
176            var isAlbumCurrent = (this.player.currentAlbum() === this.model);
177            if (isAlbumCurrent) {
178                var currentTrackIndex = this.player.get('currentTrackIndex');
179                this.$("li").each(function(index, el) {
180                    $(el).toggleClass('current', index == currentTrackIndex);
181                });
182            }
183            this.updateState();
184        },
185
186        removeFromPlaylist: function() {
187            this.options.playlist.remove(this.model);
188        }
189    });
190
191    window.LibraryAlbumView = AlbumView.extend({
192        events: {
193            'click .queue.add': 'select'
194        },
195
196        select: function() {
197            this.collection.trigger('select', this.model);
198        }
199    });
200
201    window.PlaylistView = Backbone.View.extend({
202        tag: 'section',
203        className: 'playlist',
204
205        events: {
206            'click .play':  'play',
207            'click .pause': 'pause',
208            'click .next':  'nextTrack',
209            'click .prev':  'prevTrack'
210        },
211
212        initialize: function() {
213            _.bindAll(this, 'render', 
214                            'renderAlbum', 
215                            'updateTrack', 
216                            'queueAlbum');
217            this.template = _.template($("#playlist-template").html());
218            this.collection.bind('reset', this.render);
219            this.collection.bind('add', this.renderAlbum);
220
221            // TODO: May need to bind to currentAlbumIndex too
222            this.player = this.options.player;
223            this.player.bind('change:currentTrackIndex', this.updateTrack);
224            this.createAudio();
225
226            this.library = this.options.library;
227            this.library.bind('select', this.queueAlbum);
228        },
229
230        createAudio: function() {
231            this.audio = new Audio();
232        },
233
234        render: function() {
235            $(this.el).html(this.template(this.player.toJSON()));
236            this.collection.each(this.renderAlbum);
237
238            this.$("button.play").toggle(this.player.isStopped());
239            this.$("button.pause").toggle(this.player.isPlaying());
240
241            return this;
242        },
243
244        renderAlbum: function(album) {
245            var view = new PlaylistAlbumView({
246                model: album,
247                player: this.player,
248                playlist: this.collection
249            });
250            this.$("ul").append(view.render().el);
251        },
252
253        updateTrack: function() {
254            this.audio.src = this.player.currentTrackUrl();
255            if (this.player.get('state') == 'play') { this.audio.play(); }
256        },
257
258        queueAlbum: function(album) {
259            this.collection.add(album);
260        },
261
262        play: function() {
263            this.player.play();            
264            this.audio.play();
265            this.$("button.play").hide();
266            this.$("button.pause").show();
267        },
268
269        pause: function() {
270            this.player.pause();
271            this.audio.pause();
272            this.$("button.pause").hide();
273            this.$("button.play").show();
274        },
275
276        nextTrack: function() {
277            this.player.nextTrack();
278        },
279
280        prevTrack: function() {
281            this.player.prevTrack();
282        }
283    });
284
285    window.LibraryView = Backbone.View.extend({
286        tagName: 'section',
287        className: 'library',
288
289        initialize: function() {
290            _.bindAll(this, 'render');
291            this.template = _.template($('#library-template').html());
292            this.collection.bind('reset', this.render);
293        },
294
295        render: function() {
296            var $albums,
297                collection = this.collection;
298
299            $(this.el).html(this.template({}));
300            $albums = this.$(".albums");
301            this.collection.each(function(album) {
302                var view = new LibraryAlbumView({ model: album, 
303                                                  collection: collection });
304                $albums.append(view.render().el);
305            });
306
307            return this;
308        }
309    });
310
311    window.BackboneTunes = Backbone.Router.extend({
312        routes: {
313            '':       'home',
314            'blank': 'blank'
315        },
316
317        initialize: function() {
318            this.playlistView = new PlaylistView({
319                collection:   window.player.playlist,
320                player:       window.player,
321                library:      window.library });
322
323            this.libraryView = new LibraryView({
324                collection:   window.library });
325        },
326
327        home: function() {
328            $('#container').empty();
329            $("#container").append(this.playlistView.render().el);
330            $("#container").append(this.libraryView.render().el);
331        },
332
333        blank: function() {
334            $('#container').empty();
335            $('#container').text('blank');
336        }
337    });
338
339    $(document).ready(function() {
340        window.App = new BackboneTunes();
341        Backbone.history.start({pushState: true});
342    });
343
344})(jQuery);
345
346// vim:sw=4:ts=4:expandtab