diff --git a/src/UI/Calendar/CalendarFeedView.js b/src/UI/Calendar/CalendarFeedView.js index 4a8738b85..4ca9f690a 100644 --- a/src/UI/Calendar/CalendarFeedView.js +++ b/src/UI/Calendar/CalendarFeedView.js @@ -3,16 +3,19 @@ var StatusModel = require('../System/StatusModel'); require('../Mixins/CopyToClipboard'); module.exports = Marionette.Layout.extend({ - template : 'Calendar/CalendarFeedViewTemplate', - ui : { + template : 'Calendar/CalendarFeedViewTemplate', + + ui : { icalUrl : '.x-ical-url', icalCopy : '.x-ical-copy' }, + templateHelpers : { icalHttpUrl : window.location.protocol + '//' + window.location.host + StatusModel.get('urlBase') + '/feed/calendar/NzbDrone.ics?apikey=' + window.NzbDrone.ApiKey, icalWebCalUrl : 'webcal://' + window.location.host + StatusModel.get('urlBase') + '/feed/calendar/NzbDrone.ics?apikey=' + window.NzbDrone.ApiKey }, - onShow : function(){ + + onShow : function() { this.ui.icalCopy.copyToClipboard(this.ui.icalUrl); } }); \ No newline at end of file diff --git a/src/UI/Calendar/CalendarLayout.js b/src/UI/Calendar/CalendarLayout.js index eece4d949..efc5707c2 100644 --- a/src/UI/Calendar/CalendarLayout.js +++ b/src/UI/Calendar/CalendarLayout.js @@ -5,23 +5,31 @@ var CalendarView = require('./CalendarView'); var CalendarFeedView = require('./CalendarFeedView'); module.exports = Marionette.Layout.extend({ - template : 'Calendar/CalendarLayoutTemplate', - regions : { + template : 'Calendar/CalendarLayoutTemplate', + + regions : { upcoming : '#x-upcoming', calendar : '#x-calendar' }, - events : {"click .x-ical" : '_showiCal'}, - onShow : function(){ + + events : { + 'click .x-ical' : '_showiCal' + }, + + onShow : function() { this._showUpcoming(); this._showCalendar(); }, - _showUpcoming : function(){ + + _showUpcoming : function() { this.upcoming.show(new UpcomingCollectionView()); }, - _showCalendar : function(){ + + _showCalendar : function() { this.calendar.show(new CalendarView()); }, - _showiCal : function(){ + + _showiCal : function() { var view = new CalendarFeedView(); AppLayout.modalRegion.show(view); } diff --git a/src/UI/Calendar/CalendarView.js b/src/UI/Calendar/CalendarView.js index a656a31ca..5bbdb3ebe 100644 --- a/src/UI/Calendar/CalendarView.js +++ b/src/UI/Calendar/CalendarView.js @@ -11,60 +11,75 @@ require('fullcalendar'); require('jquery.easypiechart'); module.exports = Marionette.ItemView.extend({ - storageKey : 'calendar.view', - initialize : function(){ - this.collection = new CalendarCollection().bindSignalR({updateOnly : true}); + storageKey : 'calendar.view', + + initialize : function() { + this.collection = new CalendarCollection().bindSignalR({ updateOnly : true }); this.listenTo(this.collection, 'change', this._reloadCalendarEvents); this.listenTo(QueueCollection, 'sync', this._reloadCalendarEvents); }, - render : function(){ + + render : function() { this.$el.empty().fullCalendar(this._getOptions()); }, - onShow : function(){ + + onShow : function() { this.$('.fc-button-today').click(); }, - _viewRender : function(view){ - if($(window).width() < 768) { + + _viewRender : function(view) { + if ($(window).width() < 768) { this.$('.fc-header-title').show(); this.$('.calendar-title').remove(); + var title = this.$('.fc-header-title').text(); var titleDiv = '

{0}

'.format(title); + this.$('.fc-header').before(titleDiv); this.$('.fc-header-title').hide(); } - if(Config.getValue(this.storageKey) !== view.name) { + + if (Config.getValue(this.storageKey) !== view.name) { Config.setValue(this.storageKey, view.name); } + this._getEvents(view); }, - _eventRender : function(event, element){ + + _eventRender : function(event, element) { this.$(element).addClass(event.statusLevel); this.$(element).children('.fc-event-inner').addClass(event.statusLevel); - if(event.downloading) { + + if (event.downloading) { var progress = 100 - event.downloading.get('sizeleft') / event.downloading.get('size') * 100; var releaseTitle = event.downloading.get('title'); var estimatedCompletionTime = moment(event.downloading.get('estimatedCompletionTime')).fromNow(); var status = event.downloading.get('status').toLocaleLowerCase(); var errorMessage = event.downloading.get('errorMessage'); - if(status === 'pending') { + + if (status === 'pending') { this._addStatusIcon(element, 'icon-time', 'Release will be processed {0}'.format(estimatedCompletionTime)); } - else if(errorMessage) { - if(status === 'completed') { + + else if (errorMessage) { + if (status === 'completed') { this._addStatusIcon(element, 'icon-nd-import-failed', 'Import failed: {0}'.format(errorMessage)); - } - else { + } else { this._addStatusIcon(element, 'icon-nd-download-failed', 'Download failed: {0}'.format(errorMessage)); } } - else if(status === 'failed') { + + else if (status === 'failed') { this._addStatusIcon(element, 'icon-nd-download-failed', 'Download failed: check download client for more details'); } - else if(status === 'warning') { + + else if (status === 'warning') { this._addStatusIcon(element, 'icon-nd-download-warning', 'Download warning: check download client for more details'); } + else { this.$(element).find('.fc-event-time').after(''.format(progress)); + this.$(element).find('.chart').easyPieChart({ barColor : '#ffffff', trackColor : false, @@ -73,6 +88,7 @@ module.exports = Marionette.ItemView.extend({ size : 14, animate : false }); + this.$(element).find('.chart').tooltip({ title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle), container : '.fc-content' @@ -80,10 +96,13 @@ module.exports = Marionette.ItemView.extend({ } } }, - _getEvents : function(view){ + + _getEvents : function(view) { var start = view.start.toISOString(); var end = view.end.toISOString(); + this.$el.fullCalendar('removeEvents'); + this.collection.fetch({ data : { start : start, @@ -92,14 +111,18 @@ module.exports = Marionette.ItemView.extend({ success : this._setEventData.bind(this) }); }, - _setEventData : function(collection){ + + _setEventData : function(collection) { var events = []; + var self = this; - collection.each(function(model){ + + collection.each(function(model) { var seriesTitle = model.get('series').title; var start = model.get('airDateUtc'); var runtime = model.get('series').runtime; var end = moment(start).add('minutes', runtime).toISOString(); + var event = { title : seriesTitle, start : moment(start), @@ -109,42 +132,55 @@ module.exports = Marionette.ItemView.extend({ downloading : QueueCollection.findEpisode(model.get('id')), model : model }; + events.push(event); }); + this.$el.fullCalendar('addEventSource', events); }, - _getStatusLevel : function(element, endTime){ + + _getStatusLevel : function(element, endTime) { var hasFile = element.get('hasFile'); var downloading = QueueCollection.findEpisode(element.get('id')) || element.get('grabbed'); var currentTime = moment(); var start = moment(element.get('airDateUtc')); var end = moment(endTime); + var statusLevel = 'primary'; - if(hasFile) { + + if (hasFile) { statusLevel = 'success'; } - else if(downloading) { + + else if (downloading) { statusLevel = 'purple'; } - else if(currentTime.isAfter(start) && currentTime.isBefore(end)) { + + else if (currentTime.isAfter(start) && currentTime.isBefore(end)) { statusLevel = 'warning'; } - else if(start.isBefore(currentTime) && !hasFile) { + + else if (start.isBefore(currentTime) && !hasFile) { statusLevel = 'danger'; } - else if(element.get('episodeNumber') === 1) { + + else if (element.get('episodeNumber') === 1) { statusLevel = 'premiere'; } - if(end.isBefore(currentTime.startOf('day'))) { + + if (end.isBefore(currentTime.startOf('day'))) { statusLevel += ' past'; } + return statusLevel; }, - _reloadCalendarEvents : function(){ + + _reloadCalendarEvents : function() { this.$el.fullCalendar('removeEvents'); this._setEventData(this.collection); }, - _getOptions : function(){ + + _getOptions : function() { var options = { allDayDefault : false, weekMode : 'variable', @@ -152,40 +188,50 @@ module.exports = Marionette.ItemView.extend({ timeFormat : 'h(:mm)a', viewRender : this._viewRender.bind(this), eventRender : this._eventRender.bind(this), - eventClick : function(event){ - vent.trigger(vent.Commands.ShowEpisodeDetails, {episode : event.model}); + eventClick : function(event) { + vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : event.model }); } }; - if($(window).width() < 768) { + + if ($(window).width() < 768) { options.defaultView = Config.getValue(this.storageKey, 'basicDay'); + options.header = { left : 'prev,next today', center : 'title', right : 'basicWeek,basicDay' }; } + else { options.defaultView = Config.getValue(this.storageKey, 'basicWeek'); + options.header = { left : 'prev,next today', center : 'title', right : 'month,basicWeek,basicDay' }; } + options.titleFormat = { month : 'MMMM YYYY', week : UiSettings.get('shortDateFormat'), day : UiSettings.get('longDateFormat') }; + options.columnFormat = { month : 'ddd', week : UiSettings.get('calendarWeekColumnHeader'), day : 'dddd' }; - options.timeFormat = {"default" : UiSettings.get('timeFormat')}; + + options.timeFormat = { + 'default' : UiSettings.get('timeFormat') + }; + return options; }, - _addStatusIcon : function(element, icon, tooltip){ + _addStatusIcon : function(element, icon, tooltip) { this.$(element).find('.fc-event-time').after(''.format(icon)); this.$(element).find('.status').tooltip({ title : tooltip, diff --git a/src/UI/Calendar/Collection.js b/src/UI/Calendar/Collection.js index 1ef2ccc88..63c3f1b69 100644 --- a/src/UI/Calendar/Collection.js +++ b/src/UI/Calendar/Collection.js @@ -2,9 +2,10 @@ var Backbone = require('backbone'); var EpisodeModel = require('../Series/EpisodeModel'); module.exports = Backbone.Collection.extend({ - url : window.NzbDrone.ApiRoot + '/calendar', - model : EpisodeModel, - comparator : function(model){ + url : window.NzbDrone.ApiRoot + '/calendar', + model : EpisodeModel, + + comparator : function(model) { var date = new Date(model.get('airDateUtc')); var time = date.getTime(); return time; diff --git a/src/UI/Calendar/UpcomingCollection.js b/src/UI/Calendar/UpcomingCollection.js index 32d2096d4..5c0e9542e 100644 --- a/src/UI/Calendar/UpcomingCollection.js +++ b/src/UI/Calendar/UpcomingCollection.js @@ -3,21 +3,26 @@ var moment = require('moment'); var EpisodeModel = require('../Series/EpisodeModel'); module.exports = Backbone.Collection.extend({ - url : window.NzbDrone.ApiRoot + '/calendar', - model : EpisodeModel, - comparator : function(model1, model2){ + url : window.NzbDrone.ApiRoot + '/calendar', + model : EpisodeModel, + + comparator : function(model1, model2) { var airDate1 = model1.get('airDateUtc'); var date1 = moment(airDate1); var time1 = date1.unix(); + var airDate2 = model2.get('airDateUtc'); var date2 = moment(airDate2); var time2 = date2.unix(); - if(time1 < time2) { + + if (time1 < time2) { return -1; } - if(time1 > time2) { + + if (time1 > time2) { return 1; } + return 0; } }); \ No newline at end of file diff --git a/src/UI/Calendar/UpcomingCollectionView.js b/src/UI/Calendar/UpcomingCollectionView.js index 4dd1c86a8..29d33c22d 100644 --- a/src/UI/Calendar/UpcomingCollectionView.js +++ b/src/UI/Calendar/UpcomingCollectionView.js @@ -5,17 +5,21 @@ var UpcomingItemView = require('./UpcomingItemView'); require('../Mixins/backbone.signalr.mixin'); module.exports = Marionette.CollectionView.extend({ - itemView : UpcomingItemView, - initialize : function(){ - this.collection = new UpcomingCollection().bindSignalR({updateOnly : true}); + itemView : UpcomingItemView, + + initialize : function() { + this.collection = new UpcomingCollection().bindSignalR({ updateOnly : true }); this.collection.fetch(); + this._fetchCollection = _.bind(this._fetchCollection, this); this.timer = window.setInterval(this._fetchCollection, 60 * 60 * 1000); }, - onClose : function(){ + + onClose : function() { window.clearInterval(this.timer); }, - _fetchCollection : function(){ + + _fetchCollection : function() { this.collection.fetch(); } }); \ No newline at end of file diff --git a/src/UI/Calendar/UpcomingItemView.js b/src/UI/Calendar/UpcomingItemView.js index 7599c9428..f0b8eb18c 100644 --- a/src/UI/Calendar/UpcomingItemView.js +++ b/src/UI/Calendar/UpcomingItemView.js @@ -3,17 +3,26 @@ var Marionette = require('marionette'); var moment = require('moment'); module.exports = Marionette.ItemView.extend({ - template : 'Calendar/UpcomingItemViewTemplate', - tagName : 'div', - events : {"click .x-episode-title" : '_showEpisodeDetails'}, - initialize : function(){ + template : 'Calendar/UpcomingItemViewTemplate', + tagName : 'div', + + events : { + 'click .x-episode-title' : '_showEpisodeDetails' + }, + + initialize : function() { var start = this.model.get('airDateUtc'); var runtime = this.model.get('series').runtime; var end = moment(start).add('minutes', runtime); - this.model.set({end : end.toISOString()}); + + this.model.set({ + end : end.toISOString() + }); + this.listenTo(this.model, 'change', this.render); }, - _showEpisodeDetails : function(){ - vent.trigger(vent.Commands.ShowEpisodeDetails, {episode : this.model}); + + _showEpisodeDetails : function() { + vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : this.model }); } }); \ No newline at end of file