diff --git a/src/UI/Series/Delete/DeleteSeriesView.js b/src/UI/Series/Delete/DeleteSeriesView.js index 016f2d281..de6640b5e 100644 --- a/src/UI/Series/Delete/DeleteSeriesView.js +++ b/src/UI/Series/Delete/DeleteSeriesView.js @@ -2,34 +2,39 @@ var vent = require('vent'); var Marionette = require('marionette'); module.exports = Marionette.ItemView.extend({ - template : 'Series/Delete/DeleteSeriesTemplate', - events : { - "click .x-confirm-delete" : 'removeSeries', - "change .x-delete-files" : 'changeDeletedFiles' + template : 'Series/Delete/DeleteSeriesTemplate', + + events : { + 'click .x-confirm-delete' : 'removeSeries', + 'change .x-delete-files' : 'changeDeletedFiles' }, - ui : { + + ui : { deleteFiles : '.x-delete-files', deleteFilesInfo : '.x-delete-files-info', indicator : '.x-indicator' }, - removeSeries : function(){ + + removeSeries : function() { var self = this; var deleteFiles = this.ui.deleteFiles.prop('checked'); this.ui.indicator.show(); + this.model.destroy({ - data : {"deleteFiles" : deleteFiles}, + data : { 'deleteFiles' : deleteFiles }, wait : true - }).done(function(){ - vent.trigger(vent.Events.SeriesDeleted, {series : self.model}); + }).done(function() { + vent.trigger(vent.Events.SeriesDeleted, { series : self.model }); vent.trigger(vent.Commands.CloseModalCommand); }); }, - changeDeletedFiles : function(){ + + changeDeletedFiles : function() { var deleteFiles = this.ui.deleteFiles.prop('checked'); - if(deleteFiles) { + + if (deleteFiles) { this.ui.deleteFilesInfo.show(); - } - else { + } else { this.ui.deleteFilesInfo.hide(); } } diff --git a/src/UI/Series/Details/EpisodeNumberCell.js b/src/UI/Series/Details/EpisodeNumberCell.js index 11db9233a..b1b5b826c 100644 --- a/src/UI/Series/Details/EpisodeNumberCell.js +++ b/src/UI/Series/Details/EpisodeNumberCell.js @@ -6,27 +6,38 @@ var SeriesCollection = require('../SeriesCollection'); module.exports = NzbDroneCell.extend({ className : 'episode-number-cell', template : 'Series/Details/EpisodeNumberCellTemplate', - render : function(){ + + render : function() { this.$el.empty(); this.$el.html(this.model.get('episodeNumber')); + var series = SeriesCollection.get(this.model.get('seriesId')); - if(series.get('seriesType') === 'anime' && this.model.has('absoluteEpisodeNumber')) { + + if (series.get('seriesType') === 'anime' && this.model.has('absoluteEpisodeNumber')) { this.$el.html('{0} ({1})'.format(this.model.get('episodeNumber'), this.model.get('absoluteEpisodeNumber'))); } + var alternateTitles = []; - if(reqres.hasHandler(reqres.Requests.GetAlternateNameBySeasonNumber)) { - if(this.model.get('sceneSeasonNumber') > 0) { + + if (reqres.hasHandler(reqres.Requests.GetAlternateNameBySeasonNumber)) { + + if (this.model.get('sceneSeasonNumber') > 0) { alternateTitles = reqres.request(reqres.Requests.GetAlternateNameBySeasonNumber, this.model.get('seriesId'), this.model.get('sceneSeasonNumber')); } - if(alternateTitles.length === 0) { + + if (alternateTitles.length === 0) { alternateTitles = reqres.request(reqres.Requests.GetAlternateNameBySeasonNumber, this.model.get('seriesId'), this.model.get('seasonNumber')); } } - if(this.model.get('sceneSeasonNumber') > 0 || this.model.get('sceneEpisodeNumber') > 0 || this.model.has('sceneAbsoluteEpisodeNumber') || alternateTitles.length > 0) { + + if (this.model.get('sceneSeasonNumber') > 0 || this.model.get('sceneEpisodeNumber') > 0 || this.model.has('sceneAbsoluteEpisodeNumber') || alternateTitles.length > 0) { this.templateFunction = Marionette.TemplateCache.get(this.template); + var json = this.model.toJSON(); json.alternateTitles = alternateTitles; + var html = this.templateFunction(json); + this.$el.popover({ content : html, html : true, @@ -36,6 +47,7 @@ module.exports = NzbDroneCell.extend({ container : this.$el }); } + this.delegateEvents(); return this; } diff --git a/src/UI/Series/Details/EpisodeWarningCell.js b/src/UI/Series/Details/EpisodeWarningCell.js index 214b3620d..e27593019 100644 --- a/src/UI/Series/Details/EpisodeWarningCell.js +++ b/src/UI/Series/Details/EpisodeWarningCell.js @@ -3,13 +3,16 @@ var SeriesCollection = require('../SeriesCollection'); module.exports = NzbDroneCell.extend({ className : 'episode-warning-cell', - render : function(){ + + render : function() { this.$el.empty(); - if(SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime') { - if(this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) { + + if (SeriesCollection.get(this.model.get('seriesId')).get('seriesType') === 'anime') { + if (this.model.get('seasonNumber') > 0 && !this.model.has('absoluteEpisodeNumber')) { this.$el.html(''); } } + this.delegateEvents(); return this; } diff --git a/src/UI/Series/Details/InfoView.js b/src/UI/Series/Details/InfoView.js index d8a02e90f..c7fab9fc4 100644 --- a/src/UI/Series/Details/InfoView.js +++ b/src/UI/Series/Details/InfoView.js @@ -1,13 +1,18 @@ var Marionette = require('marionette'); module.exports = Marionette.ItemView.extend({ - template : 'Series/Details/InfoViewTemplate', - initialize : function(options){ + template : 'Series/Details/InfoViewTemplate', + + initialize : function(options) { this.episodeFileCollection = options.episodeFileCollection; + this.listenTo(this.model, 'change', this.render); this.listenTo(this.episodeFileCollection, 'sync', this.render); }, - templateHelpers : function(){ - return {fileCount : this.episodeFileCollection.length}; + + templateHelpers : function() { + return { + fileCount : this.episodeFileCollection.length + }; } }); \ No newline at end of file diff --git a/src/UI/Series/Details/SeasonCollectionView.js b/src/UI/Series/Details/SeasonCollectionView.js index ffab5c3c7..24da6171c 100644 --- a/src/UI/Series/Details/SeasonCollectionView.js +++ b/src/UI/Series/Details/SeasonCollectionView.js @@ -3,34 +3,42 @@ var Marionette = require('marionette'); var SeasonLayout = require('./SeasonLayout'); var AsSortedCollectionView = require('../../Mixins/AsSortedCollectionView'); -module.exports = (function(){ - var view = Marionette.CollectionView.extend({ - itemView : SeasonLayout, - initialize : function(options){ - if(!options.episodeCollection) { - throw 'episodeCollection is needed'; - } - this.episodeCollection = options.episodeCollection; - this.series = options.series; - }, - itemViewOptions : function(){ - return { - episodeCollection : this.episodeCollection, - series : this.series - }; - }, - onEpisodeGrabbed : function(message){ - if(message.episode.series.id !== this.episodeCollection.seriesId) { - return; - } - var self = this; - _.each(message.episode.episodes, function(episode){ - var ep = self.episodeCollection.get(episode.id); - ep.set('downloading', true); - }); - this.render(); +var view = Marionette.CollectionView.extend({ + + itemView : SeasonLayout, + + initialize : function(options) { + if (!options.episodeCollection) { + throw 'episodeCollection is needed'; + } + + this.episodeCollection = options.episodeCollection; + this.series = options.series; + }, + + itemViewOptions : function() { + return { + episodeCollection : this.episodeCollection, + series : this.series + }; + }, + + onEpisodeGrabbed : function(message) { + if (message.episode.series.id !== this.episodeCollection.seriesId) { + return; } - }); - AsSortedCollectionView.call(view); - return view; -}).call(this); \ No newline at end of file + + var self = this; + + _.each(message.episode.episodes, function(episode) { + var ep = self.episodeCollection.get(episode.id); + ep.set('downloading', true); + }); + + this.render(); + } +}); + +AsSortedCollectionView.call(view); + +module.exports = view; \ No newline at end of file diff --git a/src/UI/Series/Details/SeasonLayout.js b/src/UI/Series/Details/SeasonLayout.js index 90484ffb6..0598c44a5 100644 --- a/src/UI/Series/Details/SeasonLayout.js +++ b/src/UI/Series/Details/SeasonLayout.js @@ -1,4 +1,4 @@ -var vent = require('vent'); +var vent = require('vent'); var Marionette = require('marionette'); var Backgrid = require('backgrid'); var ToggleCell = require('../../Cells/EpisodeMonitoredCell'); @@ -14,68 +14,86 @@ var _ = require('underscore'); var Messenger = require('../../Shared/Messenger'); module.exports = Marionette.Layout.extend({ - template : 'Series/Details/SeasonLayoutTemplate', - ui : { + template : 'Series/Details/SeasonLayoutTemplate', + + ui : { seasonSearch : '.x-season-search', seasonMonitored : '.x-season-monitored', seasonRename : '.x-season-rename' }, - events : { - "click .x-season-monitored" : '_seasonMonitored', - "click .x-season-search" : '_seasonSearch', - "click .x-season-rename" : '_seasonRename', - "click .x-show-hide-episodes" : '_showHideEpisodes', - "dblclick .series-season h2" : '_showHideEpisodes' - }, - regions : {episodeGrid : '.x-episode-grid'}, - columns : [{ - name : 'monitored', - label : '', - cell : ToggleCell, - trueClass : 'icon-bookmark', - falseClass : 'icon-bookmark-empty', - tooltip : 'Toggle monitored status', - sortable : false - }, { - name : 'episodeNumber', - label : '#', - cell : EpisodeNumberCell - }, { - name : 'this', - label : '', - cell : EpisodeWarningCell, - sortable : false, - className : 'episode-warning-cell' - }, { - name : 'this', - label : 'Title', - hideSeriesLink : true, - cell : EpisodeTitleCell, - sortable : false - }, { - name : 'airDateUtc', - label : 'Air Date', - cell : RelativeDateCell - }, { - name : 'status', - label : 'Status', - cell : EpisodeStatusCell, - sortable : false - }, { - name : 'this', - label : '', - cell : EpisodeActionsCell, - sortable : false - }], - templateHelpers : function(){ - var episodeCount = this.episodeCollection.filter(function(episode){ + + events : { + 'click .x-season-monitored' : '_seasonMonitored', + 'click .x-season-search' : '_seasonSearch', + 'click .x-season-rename' : '_seasonRename', + 'click .x-show-hide-episodes' : '_showHideEpisodes', + 'dblclick .series-season h2' : '_showHideEpisodes' + }, + + regions : { + episodeGrid : '.x-episode-grid' + }, + + columns : [ + { + name : 'monitored', + label : '', + cell : ToggleCell, + trueClass : 'icon-bookmark', + falseClass : 'icon-bookmark-empty', + tooltip : 'Toggle monitored status', + sortable : false + }, + { + name : 'episodeNumber', + label : '#', + cell : EpisodeNumberCell + }, + { + name : 'this', + label : '', + cell : EpisodeWarningCell, + sortable : false, + className : 'episode-warning-cell' + }, + { + name : 'this', + label : 'Title', + hideSeriesLink : true, + cell : EpisodeTitleCell, + sortable : false + }, + { + name : 'airDateUtc', + label : 'Air Date', + cell : RelativeDateCell + }, + { + name : 'status', + label : 'Status', + cell : EpisodeStatusCell, + sortable : false + }, + { + name : 'this', + label : '', + cell : EpisodeActionsCell, + sortable : false + } + ], + + templateHelpers : function() { + var episodeCount = this.episodeCollection.filter(function(episode) { return episode.get('hasFile') || episode.get('monitored') && moment(episode.get('airDateUtc')).isBefore(moment()); }).length; - var episodeFileCount = this.episodeCollection.where({hasFile : true}).length; + + var episodeFileCount = this.episodeCollection.where({ hasFile : true }).length; var percentOfEpisodes = 100; - if(episodeCount > 0) { + + if (episodeCount > 0) { percentOfEpisodes = episodeFileCount / episodeCount * 100; } + return { showingEpisodes : this.showingEpisodes, episodeCount : episodeCount, @@ -83,24 +101,32 @@ module.exports = Marionette.Layout.extend({ percentOfEpisodes : percentOfEpisodes }; }, - initialize : function(options){ - if(!options.episodeCollection) { + + initialize : function(options) { + if (!options.episodeCollection) { throw 'episodeCollection is needed'; } + this.series = options.series; this.fullEpisodeCollection = options.episodeCollection; this.episodeCollection = this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber')); this._updateEpisodeCollection(); + this.showingEpisodes = this._shouldShowEpisodes(); + this.listenTo(this.model, 'sync', this._afterSeasonMonitored); this.listenTo(this.episodeCollection, 'sync', this.render); + this.listenTo(this.fullEpisodeCollection, 'sync', this._refreshEpsiodes); }, - onRender : function(){ - if(this.showingEpisodes) { + + onRender : function() { + if (this.showingEpisodes) { this._showEpisodes(); } + this._setSeasonMonitoredState(); + CommandController.bindToCommand({ element : this.ui.seasonSearch, command : { @@ -109,6 +135,7 @@ module.exports = Marionette.Layout.extend({ seasonNumber : this.model.get('seasonNumber') } }); + CommandController.bindToCommand({ element : this.ui.seasonRename, command : { @@ -118,112 +145,143 @@ module.exports = Marionette.Layout.extend({ } }); }, - _seasonSearch : function(){ + + _seasonSearch : function() { CommandController.Execute('seasonSearch', { name : 'seasonSearch', seriesId : this.series.id, seasonNumber : this.model.get('seasonNumber') }); }, - _seasonRename : function(){ + + _seasonRename : function() { vent.trigger(vent.Commands.ShowRenamePreview, { series : this.series, seasonNumber : this.model.get('seasonNumber') }); }, - _seasonMonitored : function(){ - if(!this.series.get('monitored')) { + + _seasonMonitored : function() { + if (!this.series.get('monitored')) { + Messenger.show({ message : 'Unable to change monitored state when series is not monitored', type : 'error' }); + return; } + var name = 'monitored'; this.model.set(name, !this.model.get(name)); this.series.setSeasonMonitored(this.model.get('seasonNumber')); + var savePromise = this.series.save().always(this._afterSeasonMonitored.bind(this)); + this.ui.seasonMonitored.spinForPromise(savePromise); }, - _afterSeasonMonitored : function(){ + + _afterSeasonMonitored : function() { var self = this; - _.each(this.episodeCollection.models, function(episode){ - episode.set({monitored : self.model.get('monitored')}); + + _.each(this.episodeCollection.models, function(episode) { + episode.set({ monitored : self.model.get('monitored') }); }); + this.render(); }, - _setSeasonMonitoredState : function(){ + + _setSeasonMonitoredState : function() { this.ui.seasonMonitored.removeClass('icon-spinner icon-spin'); - if(this.model.get('monitored')) { + + if (this.model.get('monitored')) { this.ui.seasonMonitored.addClass('icon-bookmark'); this.ui.seasonMonitored.removeClass('icon-bookmark-empty'); - } - else { + } else { this.ui.seasonMonitored.addClass('icon-bookmark-empty'); this.ui.seasonMonitored.removeClass('icon-bookmark'); } }, - _showEpisodes : function(){ + + _showEpisodes : function() { this.episodeGrid.show(new Backgrid.Grid({ columns : this.columns, collection : this.episodeCollection, className : 'table table-hover season-grid' })); }, - _shouldShowEpisodes : function(){ + + _shouldShowEpisodes : function() { var startDate = moment().add('month', -1); var endDate = moment().add('year', 1); - return this.episodeCollection.some(function(episode){ + + return this.episodeCollection.some(function(episode) { var airDate = episode.get('airDateUtc'); - if(airDate) { + + if (airDate) { var airDateMoment = moment(airDate); - if(airDateMoment.isAfter(startDate) && airDateMoment.isBefore(endDate)) { + + if (airDateMoment.isAfter(startDate) && airDateMoment.isBefore(endDate)) { return true; } } + return false; }); }, - _showHideEpisodes : function(){ - if(this.showingEpisodes) { + + _showHideEpisodes : function() { + if (this.showingEpisodes) { this.showingEpisodes = false; this.episodeGrid.close(); - } - else { + } else { this.showingEpisodes = true; this._showEpisodes(); } + this.templateHelpers.showingEpisodes = this.showingEpisodes; this.render(); }, - _episodeMonitoredToggled : function(options){ + + _episodeMonitoredToggled : function(options) { var model = options.model; var shiftKey = options.shiftKey; - if(!this.episodeCollection.get(model.get('id'))) { + + if (!this.episodeCollection.get(model.get('id'))) { return; } - if(!shiftKey) { + + if (!shiftKey) { return; } + var lastToggled = this.episodeCollection.lastToggled; - if(!lastToggled) { + + if (!lastToggled) { return; } + var currentIndex = this.episodeCollection.indexOf(model); var lastIndex = this.episodeCollection.indexOf(lastToggled); + var low = Math.min(currentIndex, lastIndex); var high = Math.max(currentIndex, lastIndex); var range = _.range(low + 1, high); + this.episodeCollection.lastToggled = model; }, - _updateEpisodeCollection : function(){ + + _updateEpisodeCollection : function() { var self = this; - this.episodeCollection.add(this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber')).models, {merge : true}); - this.episodeCollection.each(function(model){ + + this.episodeCollection.add(this.fullEpisodeCollection.bySeason(this.model.get('seasonNumber')).models, { merge : true }); + + this.episodeCollection.each(function(model) { model.episodeCollection = self.episodeCollection; }); }, - _refreshEpsiodes : function(){ + + _refreshEpsiodes : function() { this._updateEpisodeCollection(); this.render(); } diff --git a/src/UI/Series/Details/SeriesDetailsLayout.js b/src/UI/Series/Details/SeriesDetailsLayout.js index 984384f62..ea0a74a87 100644 --- a/src/UI/Series/Details/SeriesDetailsLayout.js +++ b/src/UI/Series/Details/SeriesDetailsLayout.js @@ -16,13 +16,15 @@ require('backstrech'); require('../../Mixins/backbone.signalr.mixin'); module.exports = Marionette.Layout.extend({ - itemViewContainer : '.x-series-seasons', - template : 'Series/Details/SeriesDetailsTemplate', - regions : { + itemViewContainer : '.x-series-seasons', + template : 'Series/Details/SeriesDetailsTemplate', + + regions : { seasons : '#seasons', info : '#info' }, - ui : { + + ui : { header : '.x-header', monitored : '.x-monitored', edit : '.x-edit', @@ -30,47 +32,59 @@ module.exports = Marionette.Layout.extend({ rename : '.x-rename', search : '.x-search' }, - events : { + + events : { 'click .x-monitored' : '_toggleMonitored', 'click .x-edit' : '_editSeries', 'click .x-refresh' : '_refreshSeries', 'click .x-rename' : '_renameSeries', 'click .x-search' : '_seriesSearch' }, - initialize : function(){ + + initialize : function() { this.seriesCollection = SeriesCollection.clone(); this.seriesCollection.shadowCollection.bindSignalR(); + this.listenTo(this.model, 'change:monitored', this._setMonitoredState); this.listenTo(this.model, 'remove', this._seriesRemoved); this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); - this.listenTo(this.model, 'change', function(model, options){ - if(options && options.changeSource === 'signalr') { + + this.listenTo(this.model, 'change', function(model, options) { + if (options && options.changeSource === 'signalr') { this._refresh(); } }); }, - onShow : function(){ + + onShow : function() { $('body').addClass('backdrop'); var fanArt = this._getFanArt(); - if(fanArt) { + + if (fanArt) { this._backstrech = $.backstretch(fanArt); - } - else { + } else { $('body').removeClass('backdrop'); } + this._showSeasons(); this._setMonitoredState(); this._showInfo(); }, - onRender : function(){ + + onRender : function() { CommandController.bindToCommand({ element : this.ui.refresh, - command : {name : 'refreshSeries'} + command : { + name : 'refreshSeries' + } }); CommandController.bindToCommand({ element : this.ui.search, - command : {name : 'seriesSearch'} + command : { + name : 'seriesSearch' + } }); + CommandController.bindToCommand({ element : this.ui.rename, command : { @@ -80,103 +94,129 @@ module.exports = Marionette.Layout.extend({ } }); }, - onClose : function(){ - if(this._backstrech) { + + onClose : function() { + if (this._backstrech) { this._backstrech.destroy(); delete this._backstrech; } + $('body').removeClass('backdrop'); reqres.removeHandler(reqres.Requests.GetEpisodeFileById); }, - _getFanArt : function(){ - var fanArt = _.where(this.model.get('images'), {coverType : 'fanart'}); - if(fanArt && fanArt[0]) { + + _getFanArt : function() { + var fanArt = _.where(this.model.get('images'), { coverType : 'fanart' }); + + if (fanArt && fanArt[0]) { return fanArt[0].url; } + return undefined; }, - _toggleMonitored : function(){ - var savePromise = this.model.save('monitored', !this.model.get('monitored'), {wait : true}); + + _toggleMonitored : function() { + var savePromise = this.model.save('monitored', !this.model.get('monitored'), { wait : true }); + this.ui.monitored.spinForPromise(savePromise); }, - _setMonitoredState : function(){ + + _setMonitoredState : function() { var monitored = this.model.get('monitored'); + this.ui.monitored.removeAttr('data-idle-icon'); - if(monitored) { + + if (monitored) { this.ui.monitored.addClass('icon-nd-monitored'); this.ui.monitored.removeClass('icon-nd-unmonitored'); this.$el.removeClass('series-not-monitored'); - } - else { + } else { this.ui.monitored.addClass('icon-nd-unmonitored'); this.ui.monitored.removeClass('icon-nd-monitored'); this.$el.addClass('series-not-monitored'); } }, - _editSeries : function(){ - vent.trigger(vent.Commands.EditSeriesCommand, {series : this.model}); + + _editSeries : function() { + vent.trigger(vent.Commands.EditSeriesCommand, { series : this.model }); }, - _refreshSeries : function(){ + + _refreshSeries : function() { CommandController.Execute('refreshSeries', { name : 'refreshSeries', seriesId : this.model.id }); }, - _seriesRemoved : function(){ - Backbone.history.navigate('/', {trigger : true}); + + _seriesRemoved : function() { + Backbone.history.navigate('/', { trigger : true }); }, - _renameSeries : function(){ - vent.trigger(vent.Commands.ShowRenamePreview, {series : this.model}); + + _renameSeries : function() { + vent.trigger(vent.Commands.ShowRenamePreview, { series : this.model }); }, - _seriesSearch : function(){ + + _seriesSearch : function() { CommandController.Execute('seriesSearch', { name : 'seriesSearch', seriesId : this.model.id }); }, - _showSeasons : function(){ + + _showSeasons : function() { var self = this; + this.seasons.show(new LoadingView()); + this.seasonCollection = new SeasonCollection(this.model.get('seasons')); - this.episodeCollection = new EpisodeCollection({seriesId : this.model.id}).bindSignalR(); - this.episodeFileCollection = new EpisodeFileCollection({seriesId : this.model.id}).bindSignalR(); - reqres.setHandler(reqres.Requests.GetEpisodeFileById, function(episodeFileId){ + this.episodeCollection = new EpisodeCollection({ seriesId : this.model.id }).bindSignalR(); + this.episodeFileCollection = new EpisodeFileCollection({ seriesId : this.model.id }).bindSignalR(); + + reqres.setHandler(reqres.Requests.GetEpisodeFileById, function(episodeFileId) { return self.episodeFileCollection.get(episodeFileId); }); - reqres.setHandler(reqres.Requests.GetAlternateNameBySeasonNumber, function(seriesId, seasonNumber){ - if(self.model.get('id') !== seriesId) { + + reqres.setHandler(reqres.Requests.GetAlternateNameBySeasonNumber, function(seriesId, seasonNumber) { + if (self.model.get('id') !== seriesId) { return []; } - return _.where(self.model.get('alternateTitles'), {seasonNumber : seasonNumber}); + + return _.where(self.model.get('alternateTitles'), { seasonNumber : seasonNumber }); }); - $.when(this.episodeCollection.fetch(), this.episodeFileCollection.fetch()).done(function(){ + + $.when(this.episodeCollection.fetch(), this.episodeFileCollection.fetch()).done(function() { var seasonCollectionView = new SeasonCollectionView({ collection : self.seasonCollection, episodeCollection : self.episodeCollection, series : self.model }); - if(!self.isClosed) { + + if (!self.isClosed) { self.seasons.show(seasonCollectionView); } }); }, - _showInfo : function(){ + + _showInfo : function() { this.info.show(new InfoView({ model : this.model, episodeFileCollection : this.episodeFileCollection })); }, - _commandComplete : function(options){ - if(options.command.get('name') === 'renamefiles') { - if(options.command.get('seriesId') === this.model.get('id')) { + + _commandComplete : function(options) { + if (options.command.get('name') === 'renamefiles') { + if (options.command.get('seriesId') === this.model.get('id')) { this._refresh(); } } }, - _refresh : function(){ - this.seasonCollection.add(this.model.get('seasons'), {merge : true}); + + _refresh : function() { + this.seasonCollection.add(this.model.get('seasons'), { merge : true }); this.episodeCollection.fetch(); this.episodeFileCollection.fetch(); + this._setMonitoredState(); this._showInfo(); } diff --git a/src/UI/Series/Edit/EditSeriesView.js b/src/UI/Series/Edit/EditSeriesView.js index fb816fd9c..3f8c789e8 100644 --- a/src/UI/Series/Edit/EditSeriesView.js +++ b/src/UI/Series/Edit/EditSeriesView.js @@ -1,4 +1,4 @@ -var vent = require('vent'); +var vent = require('vent'); var Marionette = require('marionette'); var Profiles = require('../../Profile/ProfileCollection'); var AsModelBoundView = require('../../Mixins/AsModelBoundView'); @@ -7,39 +7,48 @@ var AsEditModalView = require('../../Mixins/AsEditModalView'); require('../../Mixins/TagInput'); require('../../Mixins/FileBrowser'); -module.exports = (function(){ - var view = Marionette.ItemView.extend({ - template : 'Series/Edit/EditSeriesViewTemplate', - ui : { - profile : '.x-profile', - path : '.x-path', - tags : '.x-tags' - }, - events : {"click .x-remove" : '_removeSeries'}, - initialize : function(){ - this.model.set('profiles', Profiles); - }, - onRender : function(){ - this.ui.path.fileBrowser(); - this.ui.tags.tagInput({ - model : this.model, - property : 'tags' - }); - }, - _onBeforeSave : function(){ - var profileId = this.ui.profile.val(); - this.model.set({profileId : profileId}); - }, - _onAfterSave : function(){ - this.trigger('saved'); - vent.trigger(vent.Commands.CloseModalCommand); - }, - _removeSeries : function(){ - vent.trigger(vent.Commands.DeleteSeriesCommand, {series : this.model}); - } - }); - AsModelBoundView.call(view); - AsValidatedView.call(view); - AsEditModalView.call(view); - return view; -}).call(this); \ No newline at end of file +var view = Marionette.ItemView.extend({ + template : 'Series/Edit/EditSeriesViewTemplate', + + ui : { + profile : '.x-profile', + path : '.x-path', + tags : '.x-tags' + }, + + events : { + 'click .x-remove' : '_removeSeries' + }, + + initialize : function() { + this.model.set('profiles', Profiles); + }, + + onRender : function() { + this.ui.path.fileBrowser(); + this.ui.tags.tagInput({ + model : this.model, + property : 'tags' + }); + }, + + _onBeforeSave : function() { + var profileId = this.ui.profile.val(); + this.model.set({ profileId : profileId }); + }, + + _onAfterSave : function() { + this.trigger('saved'); + vent.trigger(vent.Commands.CloseModalCommand); + }, + + _removeSeries : function() { + vent.trigger(vent.Commands.DeleteSeriesCommand, { series : this.model }); + } +}); + +AsModelBoundView.call(view); +AsValidatedView.call(view); +AsEditModalView.call(view); + +module.exports = view; \ No newline at end of file diff --git a/src/UI/Series/Editor/Organize/OrganizeFilesView.js b/src/UI/Series/Editor/Organize/OrganizeFilesView.js index f762239bf..25534fb21 100644 --- a/src/UI/Series/Editor/Organize/OrganizeFilesView.js +++ b/src/UI/Series/Editor/Organize/OrganizeFilesView.js @@ -5,21 +5,28 @@ var Marionette = require('marionette'); var CommandController = require('../../../Commands/CommandController'); module.exports = Marionette.ItemView.extend({ - template : 'Series/Editor/Organize/OrganizeFilesViewTemplate', - events : {"click .x-confirm-organize" : '_organize'}, - initialize : function(options){ + template : 'Series/Editor/Organize/OrganizeFilesViewTemplate', + + events : { + 'click .x-confirm-organize' : '_organize' + }, + + initialize : function(options) { this.series = options.series; this.templateHelpers = { numberOfSeries : this.series.length, series : new Backbone.Collection(this.series).toJSON() }; }, - _organize : function(){ + + _organize : function() { var seriesIds = _.pluck(this.series, 'id'); + CommandController.Execute('renameSeries', { name : 'renameSeries', seriesIds : seriesIds }); + this.trigger('organizingFiles'); vent.trigger(vent.Commands.CloseModalCommand); } diff --git a/src/UI/Series/Editor/SeriesEditorFooterView.js b/src/UI/Series/Editor/SeriesEditorFooterView.js index 0f3d467ae..be1e0d682 100644 --- a/src/UI/Series/Editor/SeriesEditorFooterView.js +++ b/src/UI/Series/Editor/SeriesEditorFooterView.js @@ -10,8 +10,9 @@ var UpdateFilesSeriesView = require('./Organize/OrganizeFilesView'); var Config = require('../../Config'); module.exports = Marionette.ItemView.extend({ - template : 'Series/Editor/SeriesEditorFooterViewTemplate', - ui : { + template : 'Series/Editor/SeriesEditorFooterViewTemplate', + + ui : { monitored : '.x-monitored', profile : '.x-profiles', seasonFolder : '.x-season-folder', @@ -21,72 +22,87 @@ module.exports = Marionette.ItemView.extend({ organizeFilesButton : '.x-organize-files', container : '.series-editor-footer' }, - events : { - "click .x-save" : '_updateAndSave', - "change .x-root-folder" : '_rootFolderChanged', - "click .x-organize-files" : '_organizeFiles' + + events : { + 'click .x-save' : '_updateAndSave', + 'change .x-root-folder' : '_rootFolderChanged', + 'click .x-organize-files' : '_organizeFiles' }, - templateHelpers : function(){ + + templateHelpers : function() { return { profiles : Profiles, rootFolders : RootFolders.toJSON() }; }, - initialize : function(options){ + + initialize : function(options) { this.seriesCollection = options.collection; - RootFolders.fetch().done(function(){ + + RootFolders.fetch().done(function() { RootFolders.synced = true; }); + this.editorGrid = options.editorGrid; this.listenTo(this.seriesCollection, 'backgrid:selected', this._updateInfo); this.listenTo(RootFolders, 'all', this.render); }, - onRender : function(){ + + onRender : function() { this._updateInfo(); }, - _updateAndSave : function(){ + + _updateAndSave : function() { var selected = this.editorGrid.getSelectedModels(); + var monitored = this.ui.monitored.val(); var profile = this.ui.profile.val(); var seasonFolder = this.ui.seasonFolder.val(); var rootFolder = this.ui.rootFolder.val(); - _.each(selected, function(model){ - if(monitored === 'true') { + + _.each(selected, function(model) { + if (monitored === 'true') { model.set('monitored', true); - } - else if(monitored === 'false') { + } else if (monitored === 'false') { model.set('monitored', false); } - if(profile !== 'noChange') { + + if (profile !== 'noChange') { model.set('profileId', parseInt(profile, 10)); } - if(seasonFolder === 'true') { + + if (seasonFolder === 'true') { model.set('seasonFolder', true); - } - else if(seasonFolder === 'false') { + } else if (seasonFolder === 'false') { model.set('seasonFolder', false); } - if(rootFolder !== 'noChange') { + + if (rootFolder !== 'noChange') { var rootFolderPath = RootFolders.get(parseInt(rootFolder, 10)); + model.set('rootFolderPath', rootFolderPath.get('path')); } + model.edited = true; }); + this.seriesCollection.save(); }, - _updateInfo : function(){ + + _updateInfo : function() { var selected = this.editorGrid.getSelectedModels(); var selectedCount = selected.length; + this.ui.selectedCount.html('{0} series selected'.format(selectedCount)); - if(selectedCount === 0) { + + if (selectedCount === 0) { this.ui.monitored.attr('disabled', ''); this.ui.profile.attr('disabled', ''); this.ui.seasonFolder.attr('disabled', ''); this.ui.rootFolder.attr('disabled', ''); this.ui.saveButton.attr('disabled', ''); this.ui.organizeFilesButton.attr('disabled', ''); - } - else { + } else { this.ui.monitored.removeAttr('disabled', ''); this.ui.profile.removeAttr('disabled', ''); this.ui.seasonFolder.removeAttr('disabled', ''); @@ -95,26 +111,29 @@ module.exports = Marionette.ItemView.extend({ this.ui.organizeFilesButton.removeAttr('disabled', ''); } }, - _rootFolderChanged : function(){ + + _rootFolderChanged : function() { var rootFolderValue = this.ui.rootFolder.val(); - if(rootFolderValue === 'addNew') { + if (rootFolderValue === 'addNew') { var rootFolderLayout = new RootFolderLayout(); this.listenToOnce(rootFolderLayout, 'folderSelected', this._setRootFolder); vent.trigger(vent.Commands.OpenModalCommand, rootFolderLayout); - } - else { + } else { Config.setValue(Config.Keys.DefaultRootFolderId, rootFolderValue); } }, - _setRootFolder : function(options){ + + _setRootFolder : function(options) { vent.trigger(vent.Commands.CloseModalCommand); this.ui.rootFolder.val(options.model.id); this._rootFolderChanged(); }, - _organizeFiles : function(){ + + _organizeFiles : function() { var selected = this.editorGrid.getSelectedModels(); - var updateFilesSeriesView = new UpdateFilesSeriesView({series : selected}); + var updateFilesSeriesView = new UpdateFilesSeriesView({ series : selected }); this.listenToOnce(updateFilesSeriesView, 'updatingFiles', this._afterSave); + vent.trigger(vent.Commands.OpenModalCommand, updateFilesSeriesView); } }); \ No newline at end of file diff --git a/src/UI/Series/Editor/SeriesEditorLayout.js b/src/UI/Series/Editor/SeriesEditorLayout.js index aa6affe74..ce50d766b 100644 --- a/src/UI/Series/Editor/SeriesEditorLayout.js +++ b/src/UI/Series/Editor/SeriesEditorLayout.js @@ -13,135 +13,170 @@ var FooterView = require('./SeriesEditorFooterView'); require('../../Mixins/backbone.signalr.mixin'); module.exports = Marionette.Layout.extend({ - template : 'Series/Editor/SeriesEditorLayoutTemplate', - regions : { + template : 'Series/Editor/SeriesEditorLayoutTemplate', + + regions : { seriesRegion : '#x-series-editor', toolbar : '#x-toolbar' }, - ui : { + + ui : { monitored : '.x-monitored', profiles : '.x-profiles', rootFolder : '.x-root-folder', selectedCount : '.x-selected-count' }, - events : { - "click .x-save" : '_updateAndSave', - "change .x-root-folder" : '_rootFolderChanged' + + events : { + 'click .x-save' : '_updateAndSave', + 'change .x-root-folder' : '_rootFolderChanged' }, - columns : [{ - name : '', - cell : SelectAllCell, - headerCell : 'select-all', - sortable : false - }, { - name : 'statusWeight', - label : '', - cell : SeriesStatusCell - }, { - name : 'title', - label : 'Title', - cell : SeriesTitleCell, - cellValue : 'this' - }, { - name : 'profileId', - label : 'Profile', - cell : ProfileCell - }, { - name : 'seasonFolder', - label : 'Season Folder', - cell : SeasonFolderCell - }, { - name : 'path', - label : 'Path', - cell : 'string' - }], + + columns : [ + { + name : '', + cell : SelectAllCell, + headerCell : 'select-all', + sortable : false + }, + { + name : 'statusWeight', + label : '', + cell : SeriesStatusCell + }, + { + name : 'title', + label : 'Title', + cell : SeriesTitleCell, + cellValue : 'this' + }, + { + name : 'profileId', + label : 'Profile', + cell : ProfileCell + }, + { + name : 'seasonFolder', + label : 'Season Folder', + cell : SeasonFolderCell + }, + { + name : 'path', + label : 'Path', + cell : 'string' + } + ], + leftSideButtons : { type : 'default', storeState : false, - items : [{ - title : 'Season Pass', - icon : 'icon-bookmark', - route : 'seasonpass' - }, { - title : 'Update Library', - icon : 'icon-refresh', - command : 'refreshseries', - successMessage : 'Library was updated!', - errorMessage : 'Library update failed!' - }] + items : [ + { + title : 'Season Pass', + icon : 'icon-bookmark', + route : 'seasonpass' + }, + { + title : 'Update Library', + icon : 'icon-refresh', + command : 'refreshseries', + successMessage : 'Library was updated!', + errorMessage : 'Library update failed!' + } + ] }, - initialize : function(){ + + initialize : function() { this.seriesCollection = SeriesCollection.clone(); this.seriesCollection.shadowCollection.bindSignalR(); this.listenTo(this.seriesCollection, 'save', this.render); + this.filteringOptions = { type : 'radio', storeState : true, menuKey : 'serieseditor.filterMode', defaultAction : 'all', - items : [{ - key : 'all', - title : '', - tooltip : 'All', - icon : 'icon-circle-blank', - callback : this._setFilter - }, { - key : 'monitored', - title : '', - tooltip : 'Monitored Only', - icon : 'icon-nd-monitored', - callback : this._setFilter - }, { - key : 'continuing', - title : '', - tooltip : 'Continuing Only', - icon : 'icon-play', - callback : this._setFilter - }, { - key : 'ended', - title : '', - tooltip : 'Ended Only', - icon : 'icon-stop', - callback : this._setFilter - }] + items : [ + { + key : 'all', + title : '', + tooltip : 'All', + icon : 'icon-circle-blank', + callback : this._setFilter + }, + { + key : 'monitored', + title : '', + tooltip : 'Monitored Only', + icon : 'icon-nd-monitored', + callback : this._setFilter + }, + { + key : 'continuing', + title : '', + tooltip : 'Continuing Only', + icon : 'icon-play', + callback : this._setFilter + }, + { + key : 'ended', + title : '', + tooltip : 'Ended Only', + icon : 'icon-stop', + callback : this._setFilter + } + ] }; }, - onRender : function(){ + + onRender : function() { this._showToolbar(); this._showTable(); }, - onClose : function(){ + + onClose : function() { vent.trigger(vent.Commands.CloseControlPanelCommand); }, - _showTable : function(){ - if(this.seriesCollection.shadowCollection.length === 0) { + + _showTable : function() { + if (this.seriesCollection.shadowCollection.length === 0) { this.seriesRegion.show(new EmptyView()); this.toolbar.close(); return; } + this.editorGrid = new Backgrid.Grid({ collection : this.seriesCollection, columns : this.columns, className : 'table table-hover' }); + this.seriesRegion.show(this.editorGrid); this._showFooter(); }, - _showToolbar : function(){ + + _showToolbar : function() { this.toolbar.show(new ToolbarLayout({ - left : [this.leftSideButtons], - right : [this.filteringOptions], + left : [ + this.leftSideButtons + ], + right : [ + this.filteringOptions + ], context : this })); }, - _showFooter : function(){ + + _showFooter : function() { vent.trigger(vent.Commands.OpenControlPanelCommand, new FooterView({ editorGrid : this.editorGrid, collection : this.seriesCollection })); }, - _setFilter : function(buttonContext){ + + _setFilter : function(buttonContext) { var mode = buttonContext.model.get('key'); + this.seriesCollection.setFilterMode(mode); } }); \ No newline at end of file diff --git a/src/UI/Series/EpisodeCollection.js b/src/UI/Series/EpisodeCollection.js index 62e2a629a..eeae5c04b 100644 --- a/src/UI/Series/EpisodeCollection.js +++ b/src/UI/Series/EpisodeCollection.js @@ -3,42 +3,56 @@ var EpisodeModel = require('./EpisodeModel'); require('./EpisodeCollection'); module.exports = Backbone.Collection.extend({ - url : window.NzbDrone.ApiRoot + '/episode', - model : EpisodeModel, - state : { + url : window.NzbDrone.ApiRoot + '/episode', + model : EpisodeModel, + + state : { sortKey : 'episodeNumber', order : 1 }, + originalFetch : Backbone.Collection.prototype.fetch, - initialize : function(options){ + + initialize : function(options) { this.seriesId = options.seriesId; }, - bySeason : function(season){ - var filtered = this.filter(function(episode){ + + bySeason : function(season) { + var filtered = this.filter(function(episode) { return episode.get('seasonNumber') === season; }); + var EpisodeCollection = require('./EpisodeCollection'); + return new EpisodeCollection(filtered); }, - comparator : function(model1, model2){ + + comparator : function(model1, model2) { var episode1 = model1.get('episodeNumber'); var episode2 = model2.get('episodeNumber'); - if(episode1 < episode2) { + + if (episode1 < episode2) { return 1; } - if(episode1 > episode2) { + + if (episode1 > episode2) { return -1; } + return 0; }, - fetch : function(options){ - if(!this.seriesId) { + + fetch : function(options) { + if (!this.seriesId) { throw 'seriesId is required'; } - if(!options) { + + if (!options) { options = {}; } - options.data = {seriesId : this.seriesId}; + + options.data = { seriesId : this.seriesId }; + return this.originalFetch.call(this, options); } }); \ No newline at end of file diff --git a/src/UI/Series/EpisodeFileCollection.js b/src/UI/Series/EpisodeFileCollection.js index 315db296f..dff988512 100644 --- a/src/UI/Series/EpisodeFileCollection.js +++ b/src/UI/Series/EpisodeFileCollection.js @@ -2,21 +2,27 @@ var Backbone = require('backbone'); var EpisodeFileModel = require('./EpisodeFileModel'); module.exports = Backbone.Collection.extend({ - url : window.NzbDrone.ApiRoot + '/episodefile', - model : EpisodeFileModel, + url : window.NzbDrone.ApiRoot + '/episodefile', + model : EpisodeFileModel, + originalFetch : Backbone.Collection.prototype.fetch, - initialize : function(options){ + + initialize : function(options) { this.seriesId = options.seriesId; this.models = []; }, - fetch : function(options){ - if(!this.seriesId) { + + fetch : function(options) { + if (!this.seriesId) { throw 'seriesId is required'; } - if(!options) { + + if (!options) { options = {}; } - options.data = {seriesId : this.seriesId}; + + options.data = { seriesId : this.seriesId }; + return this.originalFetch.call(this, options); } }); \ No newline at end of file diff --git a/src/UI/Series/EpisodeModel.js b/src/UI/Series/EpisodeModel.js index 825d2be29..ebb72cf29 100644 --- a/src/UI/Series/EpisodeModel.js +++ b/src/UI/Series/EpisodeModel.js @@ -1,13 +1,17 @@ var Backbone = require('backbone'); module.exports = Backbone.Model.extend({ - defaults : { + defaults : { seasonNumber : 0, status : 0 }, - methodUrls : {"update" : window.NzbDrone.ApiRoot + '/episode'}, - sync : function(method, model, options){ - if(model.methodUrls && model.methodUrls[method.toLowerCase()]) { + + methodUrls : { + 'update' : window.NzbDrone.ApiRoot + '/episode' + }, + + sync : function(method, model, options) { + if (model.methodUrls && model.methodUrls[method.toLowerCase()]) { options = options || {}; options.url = model.methodUrls[method.toLowerCase()]; } diff --git a/src/UI/Series/Index/EmptyView.js b/src/UI/Series/Index/EmptyView.js index b19de6571..01dcc07a4 100644 --- a/src/UI/Series/Index/EmptyView.js +++ b/src/UI/Series/Index/EmptyView.js @@ -1,3 +1,5 @@ var Marionette = require('marionette'); -module.exports = Marionette.CompositeView.extend({template : 'Series/Index/EmptyTemplate'}); \ No newline at end of file +module.exports = Marionette.CompositeView.extend({ + template : 'Series/Index/EmptyTemplate' +}); \ No newline at end of file diff --git a/src/UI/Series/Index/FooterView.js b/src/UI/Series/Index/FooterView.js index 6debef039..1d31cc404 100644 --- a/src/UI/Series/Index/FooterView.js +++ b/src/UI/Series/Index/FooterView.js @@ -1,3 +1,5 @@ var Marionette = require('marionette'); -module.exports = Marionette.CompositeView.extend({template : 'Series/Index/FooterViewTemplate'}); \ No newline at end of file +module.exports = Marionette.CompositeView.extend({ + template : 'Series/Index/FooterViewTemplate' +}); \ No newline at end of file diff --git a/src/UI/Series/Index/Overview/SeriesOverviewItemView.js b/src/UI/Series/Index/Overview/SeriesOverviewItemView.js index 7b01a78df..bb780480b 100644 --- a/src/UI/Series/Index/Overview/SeriesOverviewItemView.js +++ b/src/UI/Series/Index/Overview/SeriesOverviewItemView.js @@ -1,5 +1,7 @@ -var vent = require('vent'); +var vent = require('vent'); var Marionette = require('marionette'); var SeriesIndexItemView = require('../SeriesIndexItemView'); -module.exports = SeriesIndexItemView.extend({template : 'Series/Index/Overview/SeriesOverviewItemViewTemplate'}); \ No newline at end of file +module.exports = SeriesIndexItemView.extend({ + template : 'Series/Index/Overview/SeriesOverviewItemViewTemplate' +}); \ No newline at end of file diff --git a/src/UI/Series/Index/Posters/SeriesPostersItemView.js b/src/UI/Series/Index/Posters/SeriesPostersItemView.js index d62af00ae..3c4182483 100644 --- a/src/UI/Series/Index/Posters/SeriesPostersItemView.js +++ b/src/UI/Series/Index/Posters/SeriesPostersItemView.js @@ -1,15 +1,18 @@ -var SeriesIndexItemView = require('../SeriesIndexItemView'); +var SeriesIndexItemView = require('../SeriesIndexItemView'); module.exports = SeriesIndexItemView.extend({ - tagName : 'li', - template : 'Series/Index/Posters/SeriesPostersItemViewTemplate', - initialize : function(){ + tagName : 'li', + template : 'Series/Index/Posters/SeriesPostersItemViewTemplate', + + initialize : function() { this.events['mouseenter .x-series-poster'] = 'posterHoverAction'; this.events['mouseleave .x-series-poster'] = 'posterHoverAction'; + this.ui.controls = '.x-series-controls'; this.ui.title = '.x-title'; }, - posterHoverAction : function(){ + + posterHoverAction : function() { this.ui.controls.slideToggle(); this.ui.title.slideToggle(); } diff --git a/src/UI/Series/Index/SeriesIndexItemView.js b/src/UI/Series/Index/SeriesIndexItemView.js index d116e064c..427fe489e 100644 --- a/src/UI/Series/Index/SeriesIndexItemView.js +++ b/src/UI/Series/Index/SeriesIndexItemView.js @@ -1,14 +1,18 @@ -var vent = require('vent'); +var vent = require('vent'); var Marionette = require('marionette'); var CommandController = require('../../Commands/CommandController'); module.exports = Marionette.ItemView.extend({ - ui : {refresh : '.x-refresh'}, - events : { + ui : { + refresh : '.x-refresh' + }, + + events : { 'click .x-edit' : '_editSeries', 'click .x-refresh' : '_refreshSeries' }, - onRender : function(){ + + onRender : function() { CommandController.bindToCommand({ element : this.ui.refresh, command : { @@ -17,10 +21,12 @@ module.exports = Marionette.ItemView.extend({ } }); }, - _editSeries : function(){ - vent.trigger(vent.Commands.EditSeriesCommand, {series : this.model}); + + _editSeries : function() { + vent.trigger(vent.Commands.EditSeriesCommand, { series : this.model }); }, - _refreshSeries : function(){ + + _refreshSeries : function() { CommandController.Execute('refreshSeries', { name : 'refreshSeries', seriesId : this.model.id diff --git a/src/UI/Series/Index/SeriesIndexLayout.js b/src/UI/Series/Index/SeriesIndexLayout.js index 3ac97d448..1fc524fed 100644 --- a/src/UI/Series/Index/SeriesIndexLayout.js +++ b/src/UI/Series/Index/SeriesIndexLayout.js @@ -18,229 +18,297 @@ var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout'); require('../../Mixins/backbone.signalr.mixin'); module.exports = Marionette.Layout.extend({ - template : 'Series/Index/SeriesIndexLayoutTemplate', - regions : { + template : 'Series/Index/SeriesIndexLayoutTemplate', + + regions : { seriesRegion : '#x-series', toolbar : '#x-toolbar', toolbar2 : '#x-toolbar2', footer : '#x-series-footer' }, - columns : [{ - name : 'statusWeight', - label : '', - cell : SeriesStatusCell - }, { - name : 'title', - label : 'Title', - cell : SeriesTitleCell, - cellValue : 'this', - sortValue : 'sortTitle' - }, { - name : 'seasonCount', - label : 'Seasons', - cell : 'integer' - }, { - name : 'profileId', - label : 'Profile', - cell : ProfileCell - }, { - name : 'network', - label : 'Network', - cell : 'string' - }, { - name : 'nextAiring', - label : 'Next Airing', - cell : RelativeDateCell - }, { - name : 'percentOfEpisodes', - label : 'Episodes', - cell : EpisodeProgressCell, - className : 'episode-progress-cell' - }, { - name : 'this', - label : '', - sortable : false, - cell : SeriesActionsCell - }], - leftSideButtons : { + + columns : [ + { + name : 'statusWeight', + label : '', + cell : SeriesStatusCell + }, + { + name : 'title', + label : 'Title', + cell : SeriesTitleCell, + cellValue : 'this', + sortValue : 'sortTitle' + }, + { + name : 'seasonCount', + label : 'Seasons', + cell : 'integer' + }, + { + name : 'profileId', + label : 'Profile', + cell : ProfileCell + }, + { + name : 'network', + label : 'Network', + cell : 'string' + }, + { + name : 'nextAiring', + label : 'Next Airing', + cell : RelativeDateCell + }, + { + name : 'percentOfEpisodes', + label : 'Episodes', + cell : EpisodeProgressCell, + className : 'episode-progress-cell' + }, + { + name : 'this', + label : '', + sortable : false, + cell : SeriesActionsCell + } + ], + + leftSideButtons : { type : 'default', storeState : false, collapse : true, - items : [{ - title : 'Add Series', - icon : 'icon-plus', - route : 'addseries' - }, { - title : 'Season Pass', - icon : 'icon-bookmark', - route : 'seasonpass' - }, { - title : 'Series Editor', - icon : 'icon-nd-edit', - route : 'serieseditor' - }, { - title : 'RSS Sync', - icon : 'icon-rss', - command : 'rsssync', - errorMessage : 'RSS Sync Failed!' - }, { - title : 'Update Library', - icon : 'icon-refresh', - command : 'refreshseries', - successMessage : 'Library was updated!', - errorMessage : 'Library update failed!' - }] + items : [ + { + title : 'Add Series', + icon : 'icon-plus', + route : 'addseries' + }, + { + title : 'Season Pass', + icon : 'icon-bookmark', + route : 'seasonpass' + }, + { + title : 'Series Editor', + icon : 'icon-nd-edit', + route : 'serieseditor' + }, + { + title : 'RSS Sync', + icon : 'icon-rss', + command : 'rsssync', + errorMessage : 'RSS Sync Failed!' + }, + { + title : 'Update Library', + icon : 'icon-refresh', + command : 'refreshseries', + successMessage : 'Library was updated!', + errorMessage : 'Library update failed!' + } + ] }, - initialize : function(){ + + initialize : function() { this.seriesCollection = SeriesCollection.clone(); this.seriesCollection.shadowCollection.bindSignalR(); - this.listenTo(this.seriesCollection.shadowCollection, 'sync', function(model, collection, options){ + + this.listenTo(this.seriesCollection.shadowCollection, 'sync', function(model, collection, options) { this.seriesCollection.fullCollection.resetFiltered(); this._renderView(); }); - this.listenTo(this.seriesCollection.shadowCollection, 'add', function(model, collection, options){ + + this.listenTo(this.seriesCollection.shadowCollection, 'add', function(model, collection, options) { this.seriesCollection.fullCollection.resetFiltered(); this._renderView(); }); - this.listenTo(this.seriesCollection.shadowCollection, 'remove', function(model, collection, options){ + + this.listenTo(this.seriesCollection.shadowCollection, 'remove', function(model, collection, options) { this.seriesCollection.fullCollection.resetFiltered(); this._renderView(); }); + this.sortingOptions = { type : 'sorting', storeState : false, viewCollection : this.seriesCollection, - items : [{ - title : 'Title', - name : 'title' - }, { - title : 'Seasons', - name : 'seasonCount' - }, { - title : 'Quality', - name : 'profileId' - }, { - title : 'Network', - name : 'network' - }, { - title : 'Next Airing', - name : 'nextAiring' - }, { - title : 'Episodes', - name : 'percentOfEpisodes' - }] + items : [ + { + title : 'Title', + name : 'title' + }, + { + title : 'Seasons', + name : 'seasonCount' + }, + { + title : 'Quality', + name : 'profileId' + }, + { + title : 'Network', + name : 'network' + }, + { + title : 'Next Airing', + name : 'nextAiring' + }, + { + title : 'Episodes', + name : 'percentOfEpisodes' + } + ] }; + this.filteringOptions = { type : 'radio', storeState : true, menuKey : 'series.filterMode', defaultAction : 'all', - items : [{ - key : 'all', - title : '', - tooltip : 'All', - icon : 'icon-circle-blank', - callback : this._setFilter - }, { - key : 'monitored', - title : '', - tooltip : 'Monitored Only', - icon : 'icon-nd-monitored', - callback : this._setFilter - }, { - key : 'continuing', - title : '', - tooltip : 'Continuing Only', - icon : 'icon-play', - callback : this._setFilter - }, { - key : 'ended', - title : '', - tooltip : 'Ended Only', - icon : 'icon-stop', - callback : this._setFilter - }] + items : [ + { + key : 'all', + title : '', + tooltip : 'All', + icon : 'icon-circle-blank', + callback : this._setFilter + }, + { + key : 'monitored', + title : '', + tooltip : 'Monitored Only', + icon : 'icon-nd-monitored', + callback : this._setFilter + }, + { + key : 'continuing', + title : '', + tooltip : 'Continuing Only', + icon : 'icon-play', + callback : this._setFilter + }, + { + key : 'ended', + title : '', + tooltip : 'Ended Only', + icon : 'icon-stop', + callback : this._setFilter + } + ] }; + this.viewButtons = { type : 'radio', storeState : true, menuKey : 'seriesViewMode', defaultAction : 'listView', - items : [{ - key : 'posterView', - title : '', - tooltip : 'Posters', - icon : 'icon-th-large', - callback : this._showPosters - }, { - key : 'listView', - title : '', - tooltip : 'Overview List', - icon : 'icon-th-list', - callback : this._showList - }, { - key : 'tableView', - title : '', - tooltip : 'Table', - icon : 'icon-table', - callback : this._showTable - }] + items : [ + { + key : 'posterView', + title : '', + tooltip : 'Posters', + icon : 'icon-th-large', + callback : this._showPosters + }, + { + key : 'listView', + title : '', + tooltip : 'Overview List', + icon : 'icon-th-list', + callback : this._showList + }, + { + key : 'tableView', + title : '', + tooltip : 'Table', + icon : 'icon-table', + callback : this._showTable + } + ] }; }, - onShow : function(){ + + onShow : function() { this._showToolbar(); this._fetchCollection(); }, - _showTable : function(){ + + _showTable : function() { this.currentView = new Backgrid.Grid({ collection : this.seriesCollection, columns : this.columns, className : 'table table-hover' }); + this._renderView(); }, - _showList : function(){ - this.currentView = new ListCollectionView({collection : this.seriesCollection}); + + _showList : function() { + this.currentView = new ListCollectionView({ + collection : this.seriesCollection + }); + this._renderView(); }, - _showPosters : function(){ - this.currentView = new PosterCollectionView({collection : this.seriesCollection}); + + _showPosters : function() { + this.currentView = new PosterCollectionView({ + collection : this.seriesCollection + }); + this._renderView(); }, - _renderView : function(){ - if(SeriesCollection.length === 0) { + + _renderView : function() { + if (SeriesCollection.length === 0) { this.seriesRegion.show(new EmptyView()); + this.toolbar.close(); this.toolbar2.close(); - } - else { + } else { this.seriesRegion.show(this.currentView); + this._showToolbar(); this._showFooter(); } }, - _fetchCollection : function(){ + + _fetchCollection : function() { this.seriesCollection.fetch(); }, - _setFilter : function(buttonContext){ + + _setFilter : function(buttonContext) { var mode = buttonContext.model.get('key'); + this.seriesCollection.setFilterMode(mode); }, - _showToolbar : function(){ - if(this.toolbar.currentView) { + + _showToolbar : function() { + if (this.toolbar.currentView) { return; } + this.toolbar2.show(new ToolbarLayout({ - right : [this.filteringOptions], + right : [ + this.filteringOptions + ], context : this })); + this.toolbar.show(new ToolbarLayout({ - right : [this.sortingOptions, this.viewButtons], - left : [this.leftSideButtons], + right : [ + this.sortingOptions, + this.viewButtons + ], + left : [ + this.leftSideButtons + ], context : this })); }, - _showFooter : function(){ + + _showFooter : function() { var footerModel = new FooterModel(); var series = SeriesCollection.models.length; var episodes = 0; @@ -248,19 +316,22 @@ module.exports = Marionette.Layout.extend({ var ended = 0; var continuing = 0; var monitored = 0; - _.each(SeriesCollection.models, function(model){ + + _.each(SeriesCollection.models, function(model) { episodes += model.get('episodeCount'); episodeFiles += model.get('episodeFileCount'); - if(model.get('status').toLowerCase() === 'ended') { + + if (model.get('status').toLowerCase() === 'ended') { ended++; - } - else { + } else { continuing++; } - if(model.get('monitored')) { + + if (model.get('monitored')) { monitored++; } }); + footerModel.set({ series : series, ended : ended, @@ -270,6 +341,7 @@ module.exports = Marionette.Layout.extend({ episodes : episodes, episodeFiles : episodeFiles }); - this.footer.show(new FooterView({model : footerModel})); + + this.footer.show(new FooterView({ model : footerModel })); } }); \ No newline at end of file diff --git a/src/UI/Series/SeasonCollection.js b/src/UI/Series/SeasonCollection.js index 57aff0d35..ed661af2b 100644 --- a/src/UI/Series/SeasonCollection.js +++ b/src/UI/Series/SeasonCollection.js @@ -2,8 +2,9 @@ var Backbone = require('backbone'); var SeasonModel = require('./SeasonModel'); module.exports = Backbone.Collection.extend({ - model : SeasonModel, - comparator : function(season){ + model : SeasonModel, + + comparator : function(season) { return -season.get('seasonNumber'); } }); \ No newline at end of file diff --git a/src/UI/Series/SeasonModel.js b/src/UI/Series/SeasonModel.js index 1ae0fdec7..1ba049eb6 100644 --- a/src/UI/Series/SeasonModel.js +++ b/src/UI/Series/SeasonModel.js @@ -1,8 +1,11 @@ var Backbone = require('backbone'); module.exports = Backbone.Model.extend({ - defaults : {seasonNumber : 0}, - initialize : function(){ + defaults : { + seasonNumber : 0 + }, + + initialize : function() { this.set('id', this.get('seasonNumber')); } }); \ No newline at end of file diff --git a/src/UI/Series/SeriesCollection.js b/src/UI/Series/SeriesCollection.js index ca1a68332..44faf3dc5 100644 --- a/src/UI/Series/SeriesCollection.js +++ b/src/UI/Series/SeriesCollection.js @@ -8,68 +8,98 @@ var AsSortedCollection = require('../Mixins/AsSortedCollection'); var AsPersistedStateCollection = require('../Mixins/AsPersistedStateCollection'); var moment = require('moment'); -module.exports = (function(){ - var Collection = PageableCollection.extend({ - url : window.NzbDrone.ApiRoot + '/series', - model : SeriesModel, - tableName : 'series', - state : { - sortKey : 'sortTitle', - order : -1, - pageSize : 100000, - secondarySortKey : 'sortTitle', - secondarySortOrder : -1 - }, - mode : 'client', - save : function(){ - var self = this; - var proxy = _.extend(new Backbone.Model(), { - id : '', - url : self.url + '/editor', - toJSON : function(){ - return self.filter(function(model){ - return model.edited; - }); - } - }); - this.listenTo(proxy, 'sync', function(proxyModel, models){ - this.add(models, {merge : true}); - this.trigger('save', this); - }); - return proxy.save(); - }, - filterModes : { - "all" : [null, null], - "continuing" : ['status', 'continuing'], - "ended" : ['status', 'ended'], - "monitored" : ['monitored', true] +var Collection = PageableCollection.extend({ + url : window.NzbDrone.ApiRoot + '/series', + model : SeriesModel, + tableName : 'series', + + state : { + sortKey : 'sortTitle', + order : -1, + pageSize : 100000, + secondarySortKey : 'sortTitle', + secondarySortOrder : -1 + }, + + mode : 'client', + + save : function() { + var self = this; + + var proxy = _.extend(new Backbone.Model(), { + id : '', + + url : self.url + '/editor', + + toJSON : function() { + return self.filter(function(model) { + return model.edited; + }); + } + }); + + this.listenTo(proxy, 'sync', function(proxyModel, models) { + this.add(models, { merge : true }); + this.trigger('save', this); + }); + + return proxy.save(); + }, + + filterModes : { + 'all' : [ + null, + null + ], + 'continuing' : [ + 'status', + 'continuing' + ], + 'ended' : [ + 'status', + 'ended' + ], + 'monitored' : [ + 'monitored', + true + ] + }, + + sortMappings : { + 'title' : { + sortKey : 'sortTitle' }, - sortMappings : { - "title" : {sortKey : 'sortTitle'}, - "nextAiring" : { - sortValue : function(model, attr, order){ - var nextAiring = model.get(attr); - if(nextAiring) { - return moment(nextAiring).unix(); - } - if(order === 1) { - return 0; - } - return Number.MAX_VALUE; + 'nextAiring' : { + sortValue : function(model, attr, order) { + var nextAiring = model.get(attr); + + if (nextAiring) { + return moment(nextAiring).unix(); } - }, - percentOfEpisodes : { - sortValue : function(model, attr){ - var percentOfEpisodes = model.get(attr); - var episodeCount = model.get('episodeCount'); - return percentOfEpisodes + episodeCount / 1000000; + + if (order === 1) { + return 0; } + + return Number.MAX_VALUE; + } + }, + + percentOfEpisodes : { + sortValue : function(model, attr) { + var percentOfEpisodes = model.get(attr); + var episodeCount = model.get('episodeCount'); + + return percentOfEpisodes + episodeCount / 1000000; } } - }); - Collection = AsFilteredCollection.call(Collection); - Collection = AsSortedCollection.call(Collection); - Collection = AsPersistedStateCollection.call(Collection); - var data = ApiData.get('series'); - return new Collection(data, {full : true}); -}).call(this); \ No newline at end of file + } +}); + +Collection = AsFilteredCollection.call(Collection); +Collection = AsSortedCollection.call(Collection); +Collection = AsPersistedStateCollection.call(Collection); + +var data = ApiData.get('series'); + +module.exports = new Collection(data, { full : true }); \ No newline at end of file diff --git a/src/UI/Series/SeriesController.js b/src/UI/Series/SeriesController.js index 09dc521ac..60d1049cd 100644 --- a/src/UI/Series/SeriesController.js +++ b/src/UI/Series/SeriesController.js @@ -6,24 +6,28 @@ var SeriesDetailsLayout = require('./Details/SeriesDetailsLayout'); module.exports = NzbDroneController.extend({ _originalInit : NzbDroneController.prototype.initialize, - initialize : function(){ + + initialize : function() { this.route('', this.series); this.route('series', this.series); this.route('series/:query', this.seriesDetails); + this._originalInit.apply(this, arguments); }, - series : function(){ + + series : function() { this.setTitle('Sonarr'); this.showMainRegion(new SeriesIndexLayout()); }, - seriesDetails : function(query){ - var series = SeriesCollection.where({titleSlug : query}); - if(series.length !== 0) { + + seriesDetails : function(query) { + var series = SeriesCollection.where({ titleSlug : query }); + + if (series.length !== 0) { var targetSeries = series[0]; this.setTitle(targetSeries.get('title')); - this.showMainRegion(new SeriesDetailsLayout({model : targetSeries})); - } - else { + this.showMainRegion(new SeriesDetailsLayout({ model : targetSeries })); + } else { this.showNotFound(); } } diff --git a/src/UI/Series/SeriesModel.js b/src/UI/Series/SeriesModel.js index aecc7cd1b..9d154fa7d 100644 --- a/src/UI/Series/SeriesModel.js +++ b/src/UI/Series/SeriesModel.js @@ -2,26 +2,28 @@ var Backbone = require('backbone'); var _ = require('underscore'); module.exports = Backbone.Model.extend({ - urlRoot : window.NzbDrone.ApiRoot + '/series', - defaults : { + urlRoot : window.NzbDrone.ApiRoot + '/series', + + defaults : { episodeFileCount : 0, episodeCount : 0, isExisting : false, status : 0 }, - setSeasonMonitored : function(seasonNumber){ - _.each(this.get('seasons'), function(season){ - if(season.seasonNumber === seasonNumber) { + + setSeasonMonitored : function(seasonNumber) { + _.each(this.get('seasons'), function(season) { + if (season.seasonNumber === seasonNumber) { season.monitored = !season.monitored; } }); }, - setSeasonPass : function(seasonNumber){ - _.each(this.get('seasons'), function(season){ - if(season.seasonNumber >= seasonNumber) { + + setSeasonPass : function(seasonNumber) { + _.each(this.get('seasons'), function(season) { + if (season.seasonNumber >= seasonNumber) { season.monitored = true; - } - else { + } else { season.monitored = false; } });