PageRenderTime 956ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

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

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