PageRenderTime 40ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/public/js/Tunes.js

https://github.com/jimmytang/Backbone-Music-Player
JavaScript | 374 lines | 301 code | 71 blank | 2 comment | 18 complexity | 3f3037d30ff4e1e1d6e7b5c608fb179b 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. reset: function() {
  35. this.set({
  36. 'currentAlbumIndex': 0,
  37. 'currentTrackIndex': 0,
  38. 'state': 'stop'
  39. });
  40. },
  41. initialize: function() {
  42. this.playlist = new Playlist();
  43. },
  44. play: function() {
  45. this.set({'state': 'play'});
  46. },
  47. pause: function() {
  48. this.set({'state': 'pause'});
  49. },
  50. isPlaying: function() {
  51. return (this.get('state') == 'play');
  52. },
  53. isStopped: function() {
  54. return (!this.isPlaying());
  55. },
  56. currentAlbum: function() {
  57. return this.playlist.at(this.get('currentAlbumIndex'));
  58. },
  59. currentTrack: function() {
  60. var album = this.currentAlbum();
  61. return album.get('tracks')[this.get('currentTrackIndex')];
  62. },
  63. currentTrackUrl: function() {
  64. var album = this.currentAlbum();
  65. if (album) {
  66. return album.trackUrlAtIndex(this.get('currentTrackIndex'));
  67. } else {
  68. return null;
  69. }
  70. },
  71. nextTrack: function() {
  72. var album = this.currentAlbum();
  73. currentTrackIndex = this.get('currentTrackIndex');
  74. currentAlbumIndex = this.get('currentAlbumIndex');
  75. if (album.isLastTrack(currentTrackIndex)) {
  76. if (this.playlist.isLastAlbum(currentAlbumIndex)) {
  77. console.log('setting currentAlbumIndex to 0');
  78. this.set({'currentAlbumIndex': 0});
  79. } else {
  80. console.log('setting currentAlbumIndex to 1');
  81. this.set({'currentAlbumIndex': currentAlbumIndex + 1});
  82. }
  83. this.set({'currentTrackIndex': 0});
  84. } else {
  85. this.set({'currentTrackIndex': currentTrackIndex + 1});
  86. }
  87. album = this.currentAlbum();
  88. console.log('Current Album', this.get('currentAlbumIndex'));
  89. console.log('Current Track', this.get('currentTrackIndex'));
  90. return album.get('tracks')[this.get('currentTrackIndex')];
  91. },
  92. prevTrack: function() {
  93. var album = this.currentAlbum();
  94. currentTrackIndex = this.get('currentTrackIndex');
  95. currentAlbumIndex = this.get('currentAlbumIndex');
  96. if (album.isFirstTrack(currentTrackIndex)) {
  97. if (this.playlist.isFirstAlbum(currentAlbumIndex)) {
  98. this.set({'currentAlbumIndex': this.playlist.models.length - 1});
  99. var album = this.currentAlbum();
  100. console.log('prevTrack changing album', album)
  101. this.set({'currentTrackIndex': album.get('tracks').length - 1});
  102. } else {
  103. this.set({'currentAlbumIndex': currentAlbumIndex - 1});
  104. var album = this.currentAlbum();
  105. this.set({'currentTrackIndex': album.get('tracks').length - 1});
  106. }
  107. } else {
  108. this.set({'currentTrackIndex': currentTrackIndex - 1});
  109. }
  110. console.log('Current Album', this.get('currentAlbumIndex'));
  111. console.log('Current Track', this.get('currentTrackIndex'));
  112. return album.get('tracks')[this.get('currentTrackIndex')];
  113. },
  114. });
  115. window.library = new Albums();
  116. window.player = new Player();
  117. $(document).ready(function() {
  118. window.AlbumView = Backbone.View.extend({
  119. template: _.template($("#album-template").html()),
  120. tag: 'li',
  121. className: 'album',
  122. initialize: function() {
  123. _.bindAll(this, 'render');
  124. },
  125. render: function() {
  126. $(this.el).html(this.template(this.model.toJSON()));
  127. return this;
  128. }
  129. });
  130. window.LibraryAlbumView = AlbumView.extend({
  131. events: {
  132. 'click .queue.add': 'select'
  133. },
  134. select: function() {
  135. this.collection.trigger('select', this.model);
  136. console.log("Triggered select", this.model)
  137. }
  138. });
  139. window.PlaylistAlbumView = AlbumView.extend({
  140. events: {
  141. 'click .queue.remove': 'removeFromPlaylist',
  142. },
  143. initialize: function() {
  144. _.bindAll(this, 'render', 'updateState', 'updateTrack', 'remove');
  145. this.player = this.options.player;
  146. this.player.bind('change:state', this.updateState);
  147. this.player.bind('change:currentTrackIndex', this.updateTrack);
  148. this.model.bind('remove', this.remove);
  149. },
  150. render: function() {
  151. $(this.el).html(this.template(this.model.toJSON()));
  152. this.updateTrack();
  153. return this;
  154. },
  155. updateState: function() {
  156. var isAlbumCurrent = (this.player.currentAlbum() === this.model);
  157. $(this.el).toggleClass('current', isAlbumCurrent);
  158. },
  159. updateTrack: function() {
  160. var isAlbumCurrent = (this.player.currentAlbum() === this.model);
  161. console.log('current album index when rendering', this.player.get('currentAlbumIndex'))
  162. console.log('current track index when rendering', this.player.get('currentTrackIndex'))
  163. if (isAlbumCurrent) {
  164. var currentTrackIndex = this.player.get('currentTrackIndex');
  165. this.$('li').each(function(index, el) {
  166. $(el).toggleClass('current', index == currentTrackIndex);
  167. });
  168. }
  169. this.updateState();
  170. },
  171. removeFromPlaylist: function() {
  172. this.options.playlist.remove(this.model);
  173. this.player.reset();
  174. },
  175. });
  176. window.PlaylistView = Backbone.View.extend({
  177. tagName: 'section',
  178. className: 'playlist',
  179. events: {
  180. 'click .play': 'play',
  181. 'click .pause': 'pause',
  182. 'click .next': 'nextTrack',
  183. 'click .prev': 'prevTrack',
  184. },
  185. initialize: function() {
  186. _.bindAll(this, 'render', 'renderAlbum', 'queueAlbum');
  187. this.template = _.template($('#playlist-template').html());
  188. this.collection.bind('reset', this.render);
  189. this.collection.bind('add', this.renderAlbum);
  190. this.player = this.options.player;
  191. this.createAudio();
  192. this.library = this.options.library;
  193. this.library.bind('select', this.queueAlbum);
  194. },
  195. createAudio: function() {
  196. this.audio = new Audio();
  197. },
  198. render: function() {
  199. $(this.el).html(this.template(this.player.toJSON()));
  200. this.$('button.play').toggle(this.player.isStopped());
  201. this.$('button.pause').toggle(this.player.isPlaying());
  202. return this;
  203. },
  204. updateState: function() {
  205. this.updateTrack();
  206. this.$('button.play').toggle(this.player.isStopped());
  207. this.$('button.pause').toggle(this.player.isPlaying());
  208. },
  209. updateTrack: function() {
  210. this.audio.src = this.player.currentTrackUrl();
  211. if (this.player.isPlaying()) {
  212. this.audio.play();
  213. } else {
  214. this.audio.pause();
  215. }
  216. },
  217. play: function() {
  218. console.log("play button clicked", this.player.get('state'));
  219. this.player.play();
  220. this.updateState();
  221. },
  222. pause: function() {
  223. console.log("pause button clicked", this.player.get('state'));
  224. this.player.pause();
  225. this.updateState();
  226. },
  227. nextTrack: function() {
  228. this.player.nextTrack();
  229. this.updateTrack();
  230. },
  231. prevTrack: function() {
  232. this.player.prevTrack();
  233. this.updateTrack();
  234. },
  235. setCurrentAlbum: function() {
  236. this.player.currentAlbum();
  237. },
  238. renderAlbum: function(album) {
  239. var view = new PlaylistAlbumView({
  240. model: album,
  241. player: this.player,
  242. playlist: this.collection
  243. });
  244. this.$('ul').append(view.render().el);
  245. },
  246. queueAlbum: function(album) {
  247. this.collection.add(album);
  248. },
  249. });
  250. window.LibraryView = Backbone.View.extend({
  251. tagName: 'section',
  252. className: 'library',
  253. template: _.template($('#library-template').html()),
  254. initialize: function() {
  255. _.bindAll(this, 'render');
  256. this.collection.bind('reset', this.render);
  257. },
  258. render: function() {
  259. var $albums,
  260. collection = this.collection;
  261. $(this.el).html(this.template({}));
  262. $albums = this.$(".albums");
  263. this.collection.each(function(album) {
  264. var view = new LibraryAlbumView({
  265. model: album,
  266. collection: collection
  267. });
  268. $albums.append(view.render().el);
  269. });
  270. return this;
  271. }
  272. });
  273. window.BackboneTunes = Backbone.Router.extend({
  274. routes: {
  275. '': 'home',
  276. 'blank': 'blank'
  277. },
  278. initialize: function() {
  279. this.playlistView = new PlaylistView({
  280. collection: window.player.playlist,
  281. player: window.player,
  282. library: window.library
  283. });
  284. this.libraryView = new LibraryView({
  285. collection: window.library
  286. });
  287. },
  288. home: function() {
  289. $('#container').empty();
  290. $("#container").append(this.playlistView.render().el);
  291. $("#container").append(this.libraryView.render().el);
  292. },
  293. blank: function() {
  294. $('#container').empty();
  295. $('#container').text('blank');
  296. }
  297. });
  298. // Kick off the application
  299. window.App = new BackboneTunes();
  300. Backbone.history.start();
  301. });
  302. })(jQuery);
  303. // vim:sw=4:ts=4:expandtab