UNPKG

silk-gui

Version:

GUI for developers and Node OS

394 lines (331 loc) 12.3 kB
SongView = Backbone.View.extend({ template: '#song_template', render: function() { var self = this; // cache scrolltop (incase this is the second render, see bottom of function) var tmp_scrolltop = this.scrollTop; // calculate the duration var totalDuration = 0; for (var song = 0; song < player.songs.length; song++) { totalDuration += parseInt(player.songs[song].attributes.duration) || 0; } // render the view this.$el.html(render(this.template, { title: player.playlist.title, editable: player.playlist.editable, is_youtube: player.playlist.is_youtube, _id: player.playlist._id, sort_col: player.sort_col, sort_asc: player.sort_asc, songs: player.songs, numSongs: player.songs.length, totalDuration: prettyPrintSecondsorNA(totalDuration), })); this.$el.addClass('custom_scrollbar'); // add scroll event handler this.scrollElem = player.onMobile ? $('#wrapper') : this.$el; this.scrollElem.scroll(function() { MusicApp.router.songview.renderSong(); }); // logic to manually order the songs in a playlist if (player.playlist.editable && // playlist is editable // it is sorted in a way that makes sense for sorting (player.sort_col === null && player.sort_asc === null)) { this.$el.find('.song_table tbody').sortable({ delay: 100, items: 'tr', helper: fixHelper, update: function(event, ui) { // get where the item has moved from - to var item = player.song_collection.findBy_Id(ui.item.attr('id')); var oldIndex = item.attributes.index - 1; var newIndex = self.lastmin + ui.item.index() - 1; // remove the item from it's old place item = player.playlist.songs.splice(oldIndex, 1)[0]; // add the item into it's new place player.playlist.songs.splice(newIndex, 0, item); // refresh the songs array from the playlist player.songs = player.song_collection.getByIds(player.playlist); // send the data back to the server socket.emit('song_moved_in_playlist', { playlist_id: player.playlist._id, oldIndex: oldIndex, newIndex: newIndex, }); }, }); } // set the defaults and start rendering songs // init the processing variable this.processing = false; // content height this.contentHeight = $('#content').height(); // get the spacers this.top_spacer = this.$el.find('#top-spacer'); this.bottom_spacer = this.$el.find('#bottom-spacer'); // height of one item this.individual_height = 42; // how many to show at a time this.how_many_drawn = Math.ceil(window.innerHeight / this.individual_height) * 2; if (this.how_many_drawn > player.songs.length) { this.how_many_drawn = player.songs.length; } // initialise the lastmin and lastmax this.lastmin = 0; if (player.songs.length > 0) { this.lastmin = 1; } this.lastmax = 0; // if the viewport has been drawn at least once self.drawn_full = false; // height of number drawn this.height_of_drawn = this.how_many_drawn * this.individual_height; // how high is the table in total this.total_table_height = player.songs.length * this.individual_height; // precompile the song rendering tempalte this.song_template = swig.compile($('#song_item').html()); // default meta height until the defered function below evaluates this.meta_height = 40; _.defer(function() { // get hight of elems above table body incl header row self.meta_height = $('.playlist_meta').height() + 40; if (player.onMobile) { // mobile view has the playlists section in the scrollable view, add it to the meta_height self.meta_height += $('#sidebar').height(); } // draw the songs self.renderSong(); }); // if they have already rendered this, scroll to last postition if (this.scrollTop) { _.defer(function() { self.scrollElem.animate({scrollTop: tmp_scrolltop + 'px'}, 0); }); } }, events: { 'click .colsearch': 'triggerSearch', 'click thead > tr': 'triggerSort', 'click tbody > tr': 'triggerSong', 'click .options': 'triggerOptions', 'contextmenu td': 'triggerOptions', 'click .cover': 'triggerCover', 'click .rename_playlist': 'renamePlaylist', 'click .delete_playlist': 'deletePlaylist', }, triggerSearch: function(ev) { var search = $(ev.target).text(); player.updateSearch(search); }, triggerSort: function(ev) { var column_name = $(ev.target).closest('th').attr('class').replace('_th', ''); console.log(column_name); player.sortSongs(column_name); this.render(); }, triggerSong: function(ev) { if ($(ev.target).hasClass('options') || $(ev.target).hasClass('colsearch')) { return; } id = $(ev.target).closest('tr').attr('id'); hideOptions(); if (ev.ctrlKey) { // ctrlKey pressed, add to selection addToSelection(id, true); } else if (ev.shiftKey) { // shiftkey pressed, add to selection selectBetween(id, lastSelection); } else { // just play the song clearSelection(); player.queue_pool = player.songs.slice(0); player.playSong(id, false); player.genShufflePool(); // add the song to the history and reset it to the top player.play_history.unshift(id); player.play_history_idx = 0; } }, triggerOptions: function(ev) { if (!optionsVisible) { id = $(ev.target).closest('tr').attr('id'); if ($.inArray(id, selectedItems) == -1) { // right click on non-selected item should select only that item clearSelection(); } addToSelection(id, false); createOptions(ev.clientX, ev.clientY); } else { hideOptions(); } return false; }, triggerCover: function(ev) { showCover($(ev.target).attr('src')); return false; }, // move the selection up and down moveSelection: function(direction) { var index_in_queue = player.findSongIndex(lastSelection); var new_index = index_in_queue || 0; if (direction == 'up' && index_in_queue - 1 >= 0) { new_index--; } else if (direction == 'down' && index_in_queue + 1 < player.queue_pool.length) { new_index++; } var newly_selected = player.queue_pool[new_index]; // get the offset var new_item = $('#' + newly_selected.attributes._id); var offset = new_item.offset(); // it is actually in the view if (offset !== undefined) { // do we need to scroll to show the full item? var doc_height = $(document).height(); // how far past the top minus navbar var top_diff = offset.top - 51; // how far past the bottom plus control bar and height of element var bottom_diff = offset.top - doc_height + 63 + this.individual_height; if (top_diff <= 0) { // move one item up this.$el.scrollTop(this.$el.scrollTop() - this.individual_height); } else if (bottom_diff > 0) { // move one item down this.$el.scrollTop(this.$el.scrollTop() + this.individual_height); } } // remove the old selection if (index_in_queue !== null) { delFromSelection(player.queue_pool[index_in_queue].attributes._id); } // select the new item addToSelection(newly_selected.attributes._id, false); }, renamePlaylist: function(ev) { bootbox.prompt({ title: 'What would you like to rename "' + player.playlist.title + '" to?', value: player.playlist.title, callback: function(result) { if (result !== null) { socket.emit('rename_playlist', { title: result, id: player.playlist._id, }); $('#playlist_header').text(result); } }, }); }, deletePlaylist: function(ev) { bootbox.dialog({ message: 'Do you really want to delete this playlist?', title: 'Delete Playlist', buttons: { cancel: { label: 'Cancel', className: 'btn-default', }, del: { label: 'Delete', className: 'btn-danger', callback: function() { socket.emit('delete_playlist', {del: player.playlist._id}); MusicApp.router.playlist('LIBRARY'); }, }, }, }); }, renderSong: function() { // simple block to make sure it is only processing a render once at any point in time if (!this.processing) { this.processing = true; // cache scroll top variable this.scrollTop = this.scrollElem.scrollTop(); // get the index of the item in the center of the screen // if contentHeight is 0, contentHeight / 2 is NaN, so default to 0 var middle_of_viewport = this.scrollTop + (this.contentHeight / 2 || 0) - this.meta_height; this.middle_item = Math.floor(middle_of_viewport / this.individual_height); // get the bounds of items to draw var min = Math.floor(this.middle_item - this.how_many_drawn / 2); var max = Math.floor(this.middle_item + this.how_many_drawn / 2); // if the min is less than 0, set it to 0 if (min < 0) { min = 0; max = Math.min(this.how_many_drawn, player.songs.length - 1); } else if (max > player.songs.length - 1) { // set the max item to the last item max = player.songs.length - 1; // only let it go as low as 0 min = Math.max(max - this.how_many_drawn, 0); } if (min != this.lastmin || max != this.lastmax) { // shortcut to remove all items easily if (min > this.lastmax || max < this.lastmin) { this.$el.find('.song_row').remove(); this.drawn_full = false; } // add the elements from the side they can be added from var index = 0; var diff; var render_item; if (max > this.lastmax) { // add them to the bottom diff = Math.min(max - this.lastmax, this.how_many_drawn) - 1; while (diff >= 0) { index = max - diff; render_item = this.song_template({ song: player.songs[index], selected: (player.songs[index] ? selectedItems.indexOf(player.songs[index].attributes._id) != -1 : false), index: index, }); // if we are redrawing, remove from the other side if (this.drawn_full) { this.top_spacer.next().remove(); } this.bottom_spacer.before(render_item); diff--; } } if (min < this.lastmin) { // add them to the top diff = Math.min(this.lastmin - min, this.how_many_drawn) - 1; while (diff >= 0) { index = min + diff; render_item = this.song_template({ song: player.songs[index], selected: (selectedItems.indexOf(player.songs[index].attributes._id) != -1), index: index, }); // if we are redrawing, remove from the other side if (this.drawn_full) { this.bottom_spacer.prev().remove(); } this.top_spacer.after(render_item); diff--; } } this.lastmax = max; this.lastmin = min; } // calculate the spacer heights based off what is missing var top = this.individual_height * this.lastmin; var bottom = this.individual_height * (player.songs.length - this.lastmax); // set the spacing heights this.top_spacer.css('height', top); this.bottom_spacer.css('height', bottom); // set some finish variables this.processing = false; this.drawn_full = true; } }, redrawSong: function(_id) { // check if the song is already visible var song_tr = this.$el.find('#' + _id); if (song_tr.length !== 0) { // now replace the item this.$el.find('#' + _id).replaceWith(this.song_template({ song: player.song_collection.findBy_Id(_id)})); } }, });