PageRenderTime 64ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/js/recipes.js

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