Add Calendar Tab back. Fixes #32

pull/2/head
Leonardo Galli 8 years ago committed by GitHub
parent 199d9c93ed
commit 967d3fd5c0

@ -2,24 +2,38 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Api.Episodes; using NzbDrone.Api.Episodes;
using NzbDrone.Api.Movie;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.MovieStats;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
using NzbDrone.Core.Validation.Paths;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Validation;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.SignalR; using NzbDrone.SignalR;
namespace NzbDrone.Api.Calendar namespace NzbDrone.Api.Calendar
{ {
public class CalendarModule : EpisodeModuleWithSignalR public class CalendarModule : MovieModule
{ {
public CalendarModule(IEpisodeService episodeService, public CalendarModule(IBroadcastSignalRMessage signalR,
ISeriesService seriesService, IMovieService moviesService,
IQualityUpgradableSpecification qualityUpgradableSpecification, IMovieStatisticsService moviesStatisticsService,
IBroadcastSignalRMessage signalRBroadcaster) ISceneMappingService sceneMappingService,
: base(episodeService, seriesService, qualityUpgradableSpecification, signalRBroadcaster, "calendar") IMapCoversToLocal coverMapper)
: base(signalR, moviesService, moviesStatisticsService, sceneMappingService, coverMapper, "calendar")
{ {
GetResourceAll = GetCalendar; GetResourceAll = GetCalendar;
} }
private List<EpisodeResource> GetCalendar() private List<MovieResource> GetCalendar()
{ {
var start = DateTime.Today; var start = DateTime.Today;
var end = DateTime.Today.AddDays(2); var end = DateTime.Today.AddDays(2);
@ -33,9 +47,9 @@ namespace NzbDrone.Api.Calendar
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value); if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value); if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value);
var resources = MapToResource(_episodeService.EpisodesBetweenDates(start, end, includeUnmonitored), true, true); var resources = _moviesService.GetMoviesBetweenDates(start, end, includeUnmonitored).Select(MapToResource);
return resources.OrderBy(e => e.AirDateUtc).ToList(); return resources.OrderBy(e => e.InCinemas).ToList();
} }
} }
} }

@ -28,7 +28,7 @@ namespace NzbDrone.Api.Movie
IHandle<MediaCoversUpdatedEvent> IHandle<MediaCoversUpdatedEvent>
{ {
private readonly IMovieService _moviesService; protected readonly IMovieService _moviesService;
private readonly IMovieStatisticsService _moviesStatisticsService; private readonly IMovieStatisticsService _moviesStatisticsService;
private readonly IMapCoversToLocal _coverMapper; private readonly IMapCoversToLocal _coverMapper;
@ -78,13 +78,33 @@ namespace NzbDrone.Api.Movie
PutValidator.RuleFor(s => s.Path).IsValidPath(); PutValidator.RuleFor(s => s.Path).IsValidPath();
} }
public MovieModule(IBroadcastSignalRMessage signalRBroadcaster,
IMovieService moviesService,
IMovieStatisticsService moviesStatisticsService,
ISceneMappingService sceneMappingService,
IMapCoversToLocal coverMapper,
string resource)
: base(signalRBroadcaster, resource)
{
_moviesService = moviesService;
_moviesStatisticsService = moviesStatisticsService;
_coverMapper = coverMapper;
GetResourceAll = AllMovie;
GetResourceById = GetMovie;
CreateResource = AddMovie;
UpdateResource = UpdateMovie;
DeleteResource = DeleteMovie;
}
private MovieResource GetMovie(int id) private MovieResource GetMovie(int id)
{ {
var movies = _moviesService.GetMovie(id); var movies = _moviesService.GetMovie(id);
return MapToResource(movies); return MapToResource(movies);
} }
private MovieResource MapToResource(Core.Tv.Movie movies) protected MovieResource MapToResource(Core.Tv.Movie movies)
{ {
if (movies == null) return null; if (movies == null) return null;

@ -14,6 +14,7 @@ namespace NzbDrone.Core.Tv
Movie FindByTitle(string cleanTitle, int year); Movie FindByTitle(string cleanTitle, int year);
Movie FindByImdbId(string imdbid); Movie FindByImdbId(string imdbid);
Movie FindByTitleSlug(string slug); Movie FindByTitleSlug(string slug);
List<Movie> MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
List<Movie> GetMoviesByFileId(int fileId); List<Movie> GetMoviesByFileId(int fileId);
void SetFileId(int fileId, int movieId); void SetFileId(int fileId, int movieId);
} }
@ -119,5 +120,17 @@ namespace NzbDrone.Core.Tv
{ {
return Query.Where(m => m.TitleSlug == slug).FirstOrDefault(); return Query.Where(m => m.TitleSlug == slug).FirstOrDefault();
} }
public List<Movie> MoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
{
var query = Query.Where(m => m.InCinemas >= start && m.InCinemas <= end).OrWhere(m => m.PhysicalRelease >= start && m.PhysicalRelease <= end);
if (!includeUnmonitored)
{
query.AndWhere(e => e.Monitored);
}
return query.ToList();
}
} }
} }

@ -26,6 +26,7 @@ namespace NzbDrone.Core.Tv
Movie FindByTitleInexact(string title); Movie FindByTitleInexact(string title);
Movie FindByTitleSlug(string slug); Movie FindByTitleSlug(string slug);
Movie GetMovieByFileId(int fileId); Movie GetMovieByFileId(int fileId);
List<Movie> GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored);
void DeleteMovie(int movieId, bool deleteFiles); void DeleteMovie(int movieId, bool deleteFiles);
List<Movie> GetAllMovies(); List<Movie> GetAllMovies();
Movie UpdateMovie(Movie movie); Movie UpdateMovie(Movie movie);
@ -224,5 +225,12 @@ namespace NzbDrone.Core.Tv
{ {
return _movieRepository.FindByTitleSlug(slug); return _movieRepository.FindByTitleSlug(slug);
} }
public List<Movie> GetMoviesBetweenDates(DateTime start, DateTime end, bool includeUnmonitored)
{
var episodes = _movieRepository.MoviesBetweenDates(start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored);
return episodes;
}
} }
} }

@ -26,6 +26,12 @@ var QueueCollection = PageableCollection.extend({
}); });
}, },
findMovie : function(movieId) {
return _.find(this.fullCollection.models, function(queueModel) {
return queueModel.get('movie').id === movieId;
});
},
sortMappings : { sortMappings : {
series : { series : {
sortValue : function(model, attr) { sortValue : function(model, attr) {

@ -1,5 +1,5 @@
var Backbone = require('backbone'); var Backbone = require('backbone');
var EpisodeModel = require('../Series/EpisodeModel'); var EpisodeModel = require('../Movies/MovieModel');
module.exports = Backbone.Collection.extend({ module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/calendar', url : window.NzbDrone.ApiRoot + '/calendar',
@ -7,8 +7,8 @@ module.exports = Backbone.Collection.extend({
tableName : 'calendar', tableName : 'calendar',
comparator : function(model) { comparator : function(model) {
var date = new Date(model.get('airDateUtc')); var date = new Date(model.get('inCinemas'));
var time = date.getTime(); var time = date.getTime();
return time; return time;
} }
}); });

@ -1,75 +1,57 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Radarr Calendar feed</h3> <h3>Radarr Calendar feed</h3>
</div> </div>
<div class="modal-body edit-series-modal"> <div class="modal-body edit-series-modal">
<div class="form-horizontal"> <div class="form-horizontal">
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">Include Unmonitored</label> <label class="col-sm-3 control-label">Include Unmonitored</label>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="input-group"> <div class="input-group">
<label class="checkbox toggle well"> <label class="checkbox toggle well">
<input type="checkbox" name="includeUnmonitored" class="form-control x-includeUnmonitored"/> <input type="checkbox" name="includeUnmonitored" class="form-control x-includeUnmonitored"/>
<p> <p>
<span>Yes</span> <span>Yes</span>
<span>No</span> <span>No</span>
</p> </p>
<div class="btn btn-primary slide-button"/> <div class="btn btn-primary slide-button"/>
</label> </label>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">Season Premiers Only</label> <label class="col-sm-3 control-label">Tags</label>
<div class="col-sm-4"> <div class="col-sm-1 col-sm-push-5 help-inline">
<div class="input-group"> <i class="icon-sonarr-form-info" title="One or more tags only show matching series" />
<label class="checkbox toggle well"> </div>
<input type="checkbox" name="premiersOnly" class="form-control x-premiersOnly"/>
<div class="col-sm-5 col-sm-pull-1">
<p> <input type="text" class="form-control x-tags">
<span>Yes</span> </div>
<span>No</span> </div>
</p> <div class="form-group">
<label class="col-sm-3 control-label">iCal feed</label>
<div class="btn btn-primary slide-button"/> <div class="col-sm-1 col-sm-push-8 help-inline">
</label> <i class="icon-sonarr-form-info" title="Copy this url into your clients subscription form or use the subscribe button if your browser support webcal" />
</div> </div>
</div> <div class="col-sm-8 col-sm-pull-1">
</div> <div class="input-group ical-url">
<div class="form-group"> <input type="text" class="form-control x-ical-url" readonly="readonly" />
<label class="col-sm-3 control-label">Tags</label> <div class="input-group-btn">
<button class="btn btn-icon-only x-ical-copy"><i class="icon-sonarr-copy"></i></button>
<div class="col-sm-1 col-sm-push-5 help-inline"> <button class="btn btn-icon-only no-router x-ical-webcal" title="Subscribe" target="_blank"><i class="icon-sonarr-calendar-o"></i></button>
<i class="icon-sonarr-form-info" title="One or more tags only show matching series" /> </div>
</div> </div>
</div>
<div class="col-sm-5 col-sm-pull-1"> </div>
<input type="text" class="form-control x-tags"> </div>
</div> </div>
</div> <div class="modal-footer">
<div class="form-group"> <button class="btn" data-dismiss="modal">Close</button>
<label class="col-sm-3 control-label">iCal feed</label> </div>
<div class="col-sm-1 col-sm-push-8 help-inline">
<i class="icon-sonarr-form-info" title="Copy this url into your clients subscription form or use the subscribe button if your browser support webcal" />
</div>
<div class="col-sm-8 col-sm-pull-1">
<div class="input-group ical-url">
<input type="text" class="form-control x-ical-url" readonly="readonly" />
<div class="input-group-btn">
<button class="btn btn-icon-only x-ical-copy"><i class="icon-sonarr-copy"></i></button>
<button class="btn btn-icon-only no-router x-ical-webcal" title="Subscribe" target="_blank"><i class="icon-sonarr-calendar-o"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">Close</button>
</div>
</div> </div>

@ -10,13 +10,13 @@
<div id="x-calendar" class="calendar"/> <div id="x-calendar" class="calendar"/>
<div class="legend calendar"> <div class="legend calendar">
<ul class='legend-labels'> <ul class='legend-labels'>
<li class="legend-label"><span class="premiere" title="Premiere episode hasn't aired yet"></span>Unaired Premiere</li> <li class="legend-label"><span class="premiere" title="This Movie is still in cinemas and hasn't been released yet. Only poor qualities will be available"></span>In Cinemas</li>
<li class="legend-label"><span class="primary" title="Episode hasn't aired yet"></span>Unaired</li> <li class="legend-label"><span class="primary" title="This movie has only been announced yet."></span>Announced</li>
<li class="legend-label"><span class="warning" title="Episode is currently airing"></span>On Air</li> <!--<li class="legend-label"><span class="warning" title="Episode is currently airing"></span>On Air</li>-->
<li class="legend-label"><span class="purple" title="Episode is currently downloading"></span>Downloading</li> <li class="legend-label"><span class="purple" title="Movie is currently downloading"></span>Downloading</li>
<li class="legend-label"><span class="danger" title="Episode file has not been found"></span>Missing</li> <li class="legend-label"><span class="danger" title="Movie file has not been found"></span>Missing</li>
<li class="legend-label"><span class="success" title="Episode was downloaded and sorted"></span>Downloaded</li> <li class="legend-label"><span class="success" title="Movie was downloaded and sorted"></span>Downloaded</li>
<li class="legend-label"><span class="unmonitored" title="Episode is unmonitored"></span>Unmonitored</li> <li class="legend-label"><span class="unmonitored" title="Movie is unmonitored"></span>Unmonitored</li>
</ul> </ul>
</div> </div>
</div> </div>

@ -12,273 +12,274 @@ require('fullcalendar');
require('jquery.easypiechart'); require('jquery.easypiechart');
module.exports = Marionette.ItemView.extend({ module.exports = Marionette.ItemView.extend({
storageKey : 'calendar.view', storageKey : 'calendar.view',
initialize : function() { initialize : function() {
this.showUnmonitored = Config.getValue('calendar.show', 'monitored') === 'all'; this.showUnmonitored = Config.getValue('calendar.show', 'monitored') === 'all';
this.collection = new CalendarCollection().bindSignalR({ updateOnly : true }); this.collection = new CalendarCollection().bindSignalR({ updateOnly : true });
this.listenTo(this.collection, 'change', this._reloadCalendarEvents); this.listenTo(this.collection, 'change', this._reloadCalendarEvents);
this.listenTo(QueueCollection, 'sync', this._reloadCalendarEvents); this.listenTo(QueueCollection, 'sync', this._reloadCalendarEvents);
}, },
render : function() { render : function() {
this.$el.empty().fullCalendar(this._getOptions()); this.$el.empty().fullCalendar(this._getOptions());
}, },
onShow : function() { onShow : function() {
this.$('.fc-today-button').click(); this.$('.fc-today-button').click();
}, },
setShowUnmonitored : function (showUnmonitored) { setShowUnmonitored : function (showUnmonitored) {
if (this.showUnmonitored !== showUnmonitored) { if (this.showUnmonitored !== showUnmonitored) {
this.showUnmonitored = showUnmonitored; this.showUnmonitored = showUnmonitored;
this._getEvents(this.$el.fullCalendar('getView')); this._getEvents(this.$el.fullCalendar('getView'));
} }
}, },
_viewRender : function(view, element) { _viewRender : function(view, element) {
if (Config.getValue(this.storageKey) !== view.name) { if (Config.getValue(this.storageKey) !== view.name) {
Config.setValue(this.storageKey, view.name); Config.setValue(this.storageKey, view.name);
} }
this._getEvents(view); this._getEvents(view);
element.find('.fc-day-grid-container').css('height', ''); element.find('.fc-day-grid-container').css('height', '');
}, },
_eventRender : function(event, element) { _eventRender : function(event, element) {
element.addClass(event.statusLevel); element.addClass(event.statusLevel);
element.children('.fc-content').addClass(event.statusLevel); element.children('.fc-content').addClass(event.statusLevel);
if (event.downloading) { if (event.downloading) {
var progress = 100 - event.downloading.get('sizeleft') / event.downloading.get('size') * 100; var progress = 100 - event.downloading.get('sizeleft') / event.downloading.get('size') * 100;
var releaseTitle = event.downloading.get('title'); var releaseTitle = event.downloading.get('title');
var estimatedCompletionTime = moment(event.downloading.get('estimatedCompletionTime')).fromNow(); var estimatedCompletionTime = moment(event.downloading.get('estimatedCompletionTime')).fromNow();
var status = event.downloading.get('status').toLocaleLowerCase(); var status = event.downloading.get('status').toLocaleLowerCase();
var errorMessage = event.downloading.get('errorMessage'); var errorMessage = event.downloading.get('errorMessage');
if (status === 'pending') { if (status === 'pending') {
this._addStatusIcon(element, 'icon-sonarr-pending', 'Release will be processed {0}'.format(estimatedCompletionTime)); this._addStatusIcon(element, 'icon-sonarr-pending', 'Release will be processed {0}'.format(estimatedCompletionTime));
} }
else if (errorMessage) { else if (errorMessage) {
if (status === 'completed') { if (status === 'completed') {
this._addStatusIcon(element, 'icon-sonarr-import-failed', 'Import failed: {0}'.format(errorMessage)); this._addStatusIcon(element, 'icon-sonarr-import-failed', 'Import failed: {0}'.format(errorMessage));
} else { } else {
this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: {0}'.format(errorMessage)); this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: {0}'.format(errorMessage));
} }
} }
else if (status === 'failed') { else if (status === 'failed') {
this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: check download client for more details'); this._addStatusIcon(element, 'icon-sonarr-download-failed', 'Download failed: check download client for more details');
} }
else if (status === 'warning') { else if (status === 'warning') {
this._addStatusIcon(element, 'icon-sonarr-download-warning', 'Download warning: check download client for more details'); this._addStatusIcon(element, 'icon-sonarr-download-warning', 'Download warning: check download client for more details');
} }
else { else {
element.find('.fc-time').after('<span class="chart pull-right" data-percent="{0}"></span>'.format(progress)); element.find('.fc-time').after('<span class="chart pull-right" data-percent="{0}"></span>'.format(progress));
element.find('.chart').easyPieChart({ element.find('.chart').easyPieChart({
barColor : '#ffffff', barColor : '#ffffff',
trackColor : false, trackColor : false,
scaleColor : false, scaleColor : false,
lineWidth : 2, lineWidth : 2,
size : 14, size : 14,
animate : false animate : false
}); });
element.find('.chart').tooltip({ element.find('.chart').tooltip({
title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle), title : 'Episode is downloading - {0}% {1}'.format(progress.toFixed(1), releaseTitle),
container : '.fc' container : '.fc'
}); });
} }
} }
else if (event.model.get('unverifiedSceneNumbering')) { else if (event.model.get('unverifiedSceneNumbering')) {
this._addStatusIcon(element, 'icon-sonarr-form-warning', 'Scene number hasn\'t been verified yet.'); this._addStatusIcon(element, 'icon-sonarr-form-warning', 'Scene number hasn\'t been verified yet.');
} }
},
else if (event.model.get('series').seriesType === 'anime' && event.model.get('seasonNumber') > 0 && !event.model.has('absoluteEpisodeNumber')) {
this._addStatusIcon(element, 'icon-sonarr-form-warning', 'Episode does not have an absolute episode number'); _eventAfterAllRender : function () {
} if ($(window).width() < 768) {
}, this.$('.fc-center').show();
this.$('.calendar-title').remove();
_eventAfterAllRender : function () {
if ($(window).width() < 768) { var title = this.$('.fc-center').html();
this.$('.fc-center').show(); var titleDiv = '<div class="calendar-title">{0}</div>'.format(title);
this.$('.calendar-title').remove();
this.$('.fc-toolbar').before(titleDiv);
var title = this.$('.fc-center').html(); this.$('.fc-center').hide();
var titleDiv = '<div class="calendar-title">{0}</div>'.format(title); }
this.$('.fc-toolbar').before(titleDiv); this._clearScrollBar();
this.$('.fc-center').hide(); },
}
_windowResize : function () {
this._clearScrollBar(); this._clearScrollBar();
}, },
_windowResize : function () { _getEvents : function(view) {
this._clearScrollBar(); var start = moment(view.start.toISOString()).toISOString();
}, var end = moment(view.end.toISOString()).toISOString();
_getEvents : function(view) { this.$el.fullCalendar('removeEvents');
var start = moment(view.start.toISOString()).toISOString();
var end = moment(view.end.toISOString()).toISOString(); this.collection.fetch({
data : {
this.$el.fullCalendar('removeEvents'); start : start,
end : end,
this.collection.fetch({ unmonitored : this.showUnmonitored
data : { },
start : start, success : this._setEventData.bind(this, new Date(start), new Date(end))
end : end, });
unmonitored : this.showUnmonitored },
},
success : this._setEventData.bind(this) _setEventData : function(startD, endD, collection) {
}); if (collection.length === 0) {
}, return;
}
_setEventData : function(collection) {
if (collection.length === 0) { var events = [];
return; var self = this;
}
collection.each(function(model) {
var events = []; var seriesTitle = model.get('title');
var self = this; var start = model.get('inCinemas');
var startDate = new Date(start);
collection.each(function(model) { if (!(startD <= startDate && startDate <= endD)) {
var seriesTitle = model.get('series').title; start = model.get("physicalRelease");
var start = model.get('airDateUtc'); }
var runtime = model.get('series').runtime; var runtime = model.get('runtime');
var end = moment(start).add('minutes', runtime).toISOString(); var end = moment(start).add('minutes', runtime).toISOString();
var event = { var event = {
title : seriesTitle, title : seriesTitle,
start : moment(start), start : moment(start),
end : moment(end), end : moment(end),
allDay : false, allDay : true,
statusLevel : self._getStatusLevel(model, end), statusLevel : self._getStatusLevel(model, end),
downloading : QueueCollection.findEpisode(model.get('id')), downloading : QueueCollection.findMovie(model.get('id')),
model : model, model : model,
sortOrder : (model.get('seasonNumber') === 0 ? 1000000 : model.get('seasonNumber') * 10000) + model.get('episodeNumber') sortOrder : 0
}; };
events.push(event); events.push(event);
}); });
this.$el.fullCalendar('addEventSource', events); this.$el.fullCalendar('addEventSource', events);
}, },
_getStatusLevel : function(element, endTime) { _getStatusLevel : function(element, endTime) {
var hasFile = element.get('hasFile'); var hasFile = element.get('hasFile');
var downloading = QueueCollection.findEpisode(element.get('id')) || element.get('grabbed'); var downloading = QueueCollection.findMovie(element.get('id')) || element.get('grabbed');
var currentTime = moment(); var currentTime = moment();
var start = moment(element.get('airDateUtc')); var start = moment(element.get('inCinemas'));
var end = moment(endTime); var status = element.getStatus();
var monitored = element.get('series').monitored && element.get('monitored'); var end = moment(endTime);
var monitored = element.get('monitored');
var statusLevel = 'primary';
var statusLevel = 'primary';
if (hasFile) {
statusLevel = 'success'; if (hasFile) {
} statusLevel = 'success';
}
else if (downloading) {
statusLevel = 'purple'; else if (downloading) {
} statusLevel = 'purple';
}
else if (!monitored) {
statusLevel = 'unmonitored'; else if (!monitored) {
} statusLevel = 'unmonitored';
}
else if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
statusLevel = 'warning'; else if (status == "inCinemas") {
} statusLevel = 'premiere';
}
else if (start.isBefore(currentTime) && !hasFile) {
statusLevel = 'danger'; else if (status == "released") {
} statusLevel = 'danger';
}
else if (element.get('episodeNumber') === 1) {
statusLevel = 'premiere'; else if (status == "announced") {
} statusLevel = 'primary';
}
if (end.isBefore(currentTime.startOf('day'))) {
statusLevel += ' past'; if (end.isBefore(currentTime.startOf('day'))) {
} statusLevel += ' past';
}
return statusLevel;
}, return statusLevel;
},
_reloadCalendarEvents : function() {
this.$el.fullCalendar('removeEvents'); _reloadCalendarEvents : function() {
this._setEventData(this.collection); this.$el.fullCalendar('removeEvents');
}, var view = this.$el.fullCalendar('getView');
var start = moment(view.start.toISOString()).toISOString();
_getOptions : function() { var end = moment(view.end.toISOString()).toISOString();
var options = { this._setEventData(new Date(start), new Date(end), this.collection);
allDayDefault : false, },
weekMode : 'variable',
firstDay : UiSettings.get('firstDayOfWeek'), _getOptions : function() {
timeFormat : 'h(:mm)t', var options = {
viewRender : this._viewRender.bind(this), allDayDefault : true,
eventRender : this._eventRender.bind(this), weekMode : 'variable',
eventAfterAllRender : this._eventAfterAllRender.bind(this), firstDay : UiSettings.get('firstDayOfWeek'),
windowResize : this._windowResize.bind(this), timeFormat : 'h(:mm)t',
eventClick : function(event) { viewRender : this._viewRender.bind(this),
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : event.model }); eventRender : this._eventRender.bind(this),
} eventAfterAllRender : this._eventAfterAllRender.bind(this),
}; windowResize : this._windowResize.bind(this),
eventClick : function(event) {
if ($(window).width() < 768) { //vent.trigger(vent.Commands.ShowMovieDetails, { movie : event.model });
options.defaultView = Config.getValue(this.storageKey, 'basicDay'); window.location.href = "movies/"+event.model.get("titleSlug");
}
options.header = { };
left : 'prev,next today',
center : 'title', if ($(window).width() < 768) {
right : 'basicWeek,basicDay' options.defaultView = Config.getValue(this.storageKey, 'listYear');
};
} options.header = {
left : 'prev,next today',
else { center : 'title',
options.defaultView = Config.getValue(this.storageKey, 'basicWeek'); right : 'listYear'
};
options.header = { }
left : 'prev,next today',
center : 'title', else {
right : 'month,basicWeek,basicDay' options.defaultView = Config.getValue(this.storageKey, 'month');
};
} options.header = {
left : 'prev,next today',
options.titleFormat = { center : 'title',
month : 'MMMM YYYY', right : 'month,listYear'
week : UiSettings.get('shortDateFormat'), };
day : UiSettings.get('longDateFormat') }
};
options.titleFormat = "L";
options.columnFormat = {
month : 'ddd', options.columnFormat = "L"/*{
week : UiSettings.get('calendarWeekColumnHeader'), month : 'ddd',
day : 'dddd' week : UiSettings.get('calendarWeekColumnHeader'),
}; day : 'dddd'
};*///For now ignore settings. TODO update that.
options.timeFormat = UiSettings.get('timeFormat');
options.timeFormat = UiSettings.get('timeFormat');
return options;
}, return options;
},
_addStatusIcon : function(element, icon, tooltip) {
element.find('.fc-time').after('<span class="status pull-right"><i class="{0}"></i></span>'.format(icon)); _addStatusIcon : function(element, icon, tooltip) {
element.find('.status').tooltip({ element.find('.fc-time').after('<span class="status pull-right"><i class="{0}"></i></span>'.format(icon));
title : tooltip, element.find('.status').tooltip({
container : '.fc' title : tooltip,
}); container : '.fc'
}, });
},
_clearScrollBar : function () {
// Remove height from calendar so we don't have another scroll bar _clearScrollBar : function () {
this.$('.fc-day-grid-container').css('height', ''); // Remove height from calendar so we don't have another scroll bar
this.$('.fc-row.fc-widget-header').attr('style', ''); this.$('.fc-day-grid-container').css('height', '');
} this.$('.fc-row.fc-widget-header').attr('style', '');
}); }
});

@ -1,17 +1,17 @@
var Backbone = require('backbone'); var Backbone = require('backbone');
var moment = require('moment'); var moment = require('moment');
var EpisodeModel = require('../Series/EpisodeModel'); var EpisodeModel = require('../Movies/MovieModel');
module.exports = Backbone.Collection.extend({ module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/calendar', url : window.NzbDrone.ApiRoot + '/calendar',
model : EpisodeModel, model : EpisodeModel,
comparator : function(model1, model2) { comparator : function(model1, model2) {
var airDate1 = model1.get('airDateUtc'); var airDate1 = model1.get('inCinemas');
var date1 = moment(airDate1); var date1 = moment(airDate1);
var time1 = date1.unix(); var time1 = date1.unix();
var airDate2 = model2.get('airDateUtc'); var airDate2 = model2.get('inCinemas');
var date2 = moment(airDate2); var date2 = moment(airDate2);
var time2 = date2.unix(); var time2 = date2.unix();
@ -25,4 +25,4 @@ module.exports = Backbone.Collection.extend({
return 0; return 0;
} }
}); });

@ -11,8 +11,8 @@ module.exports = Marionette.ItemView.extend({
}, },
initialize : function() { initialize : function() {
var start = this.model.get('airDateUtc'); var start = this.model.get('inCinemas');
var runtime = this.model.get('series').runtime; var runtime = this.model.get('runtime');
var end = moment(start).add('minutes', runtime); var end = moment(start).add('minutes', runtime);
this.model.set({ this.model.set({
@ -25,4 +25,4 @@ module.exports = Marionette.ItemView.extend({
_showEpisodeDetails : function() { _showEpisodeDetails : function() {
vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : this.model }); vent.trigger(vent.Commands.ShowEpisodeDetails, { episode : this.model });
} }
}); });

@ -7,248 +7,259 @@
@import "../Content/Overrides/bootstrap"; @import "../Content/Overrides/bootstrap";
.calendar { .calendar {
width: 100%; width: 100%;
th, td { th, td {
border-color : #eeeeee; border-color : #eeeeee;
} }
.fc-event-skin { .fc-event-skin {
background-color : #007ccd; background-color : #007ccd;
border : 1px solid #007ccd; border : 1px solid #007ccd;
border-radius : 4px; border-radius : 4px;
text-align : center; text-align : center;
} }
.fc-event { .fc-event {
.clickable; .clickable;
.status { .status {
margin-right : 4px; margin-right : 4px;
} }
} }
th { th {
background-color : #eeeeee; background-color : #eeeeee;
} }
h2 { h2 {
font-size : 17.5px; font-size : 17.5px;
} }
.fc-state-highlight { .fc-state-highlight {
background : #dbdbdb; background : #dbdbdb;
} }
.past { .past {
opacity : 0.8; opacity : 0.8;
} }
.fc-title {
white-space: normal;
}
.fc-list-table {
.past {
opacity: 1.0;
}
}
} }
.event { .event {
display : inline-block; display : inline-block;
width : 100%; width : 100%;
margin-bottom : 10px; margin-bottom : 10px;
border-top : 1px solid #eeeeee; border-top : 1px solid #eeeeee;
padding-top : 10px; padding-top : 10px;
h4 { h4 {
font-weight : 500; font-weight : 500;
color : #008dcd; color : #008dcd;
margin : 5px 0px; margin : 5px 0px;
} }
p { p {
color : #999999; color : #999999;
margin : 0px; margin : 0px;
} }
.date { .date {
text-align : center; text-align : center;
display : inline-block; display : inline-block;
border-left : 4px solid #eeeeee; border-left : 4px solid #eeeeee;
padding-left : 16px; padding-left : 16px;
float : left; float : left;
margin-right : 20px; margin-right : 20px;
h4 { h4 {
line-height : 1em; line-height : 1em;
color : #555555; color : #555555;
font-weight : 300; font-weight : 300;
text-transform : uppercase; text-transform : uppercase;
} }
h1 { h1 {
font-weight : 500; font-weight : 500;
line-height : 0.8em; line-height : 0.8em;
} }
} }
.primary { .primary {
border-color : @btn-primary-bg; border-color : @btn-primary-bg;
} }
.info { .info {
border-color : @btn-info-bg; border-color : @btn-info-bg;
} }
.inverse { .inverse {
border-color : @btn-link-disabled-color; border-color : @btn-link-disabled-color;
} }
.warning { .warning {
border-color : @btn-warning-bg; border-color : @btn-warning-bg;
} }
.danger { .danger {
border-color : @btn-danger-bg; border-color : @btn-danger-bg;
} color: white;
}
.success {
border-color : @btn-success-bg; .success {
} border-color : @btn-success-bg;
}
.purple {
border-color : @nzbdronePurple; .purple {
} border-color : @nzbdronePurple;
}
.pink {
border-color : @nzbdronePink; .pink {
} border-color : @nzbdronePink;
}
.premiere {
border-color : @droneTeal; .premiere {
} border-color : @droneTeal;
}
.unmonitored {
border-color : grey; .unmonitored {
} border-color : grey;
}
.episode-title {
.btn-link; .episode-title {
.text-overflow; .btn-link;
color : @link-color; .text-overflow;
margin-top : 1px; color : @link-color;
display : inline-block; margin-top : 1px;
display : inline-block;
@media (max-width: @screen-xs-min) {
width : 140px; @media (max-width: @screen-xs-min) {
} width : 140px;
}
@media (min-width: @screen-md-min) {
width : 135px; @media (min-width: @screen-md-min) {
} width : 135px;
} }
}
} }
.calendar { .calendar {
// background-position : -160px -128px; // background-position : -160px -128px;
.primary { .primary {
border-color : @btn-primary-bg; border-color : @btn-primary-bg;
background-color : @btn-primary-bg; background-color : @btn-primary-bg;
.color-impaired-background-gradient(90deg, @btn-primary-bg); .color-impaired-background-gradient(90deg, @btn-primary-bg);
} }
.info { .info {
border-color : @btn-info-bg; border-color : @btn-info-bg;
background-color : @btn-info-bg; background-color : @btn-info-bg;
} }
.inverse { .inverse {
border-color : @btn-link-disabled-color; border-color : @btn-link-disabled-color;
background-color : @btn-link-disabled-color; background-color : @btn-link-disabled-color;
} }
.warning { .warning {
border-color : @btn-warning-bg; border-color : @btn-warning-bg;
background-color : @btn-warning-bg; background-color : @btn-warning-bg;
.color-impaired-background-gradient(90deg, @btn-warning-bg); .color-impaired-background-gradient(90deg, @btn-warning-bg);
} }
.danger { .danger {
border-color : @btn-danger-bg; border-color : @btn-danger-bg;
background-color : @btn-danger-bg; background-color : @btn-danger-bg;
color: white;
.color-impaired-background-gradient(90deg, @btn-danger-bg); .color-impaired-background-gradient(90deg, @btn-danger-bg);
} }
.success { .success {
border-color : @btn-success-bg; border-color : @btn-success-bg;
background-color : @btn-success-bg; background-color : @btn-success-bg;
} }
.purple { .purple {
border-color : @nzbdronePurple; border-color : @nzbdronePurple;
background-color : @nzbdronePurple; background-color : @nzbdronePurple;
} }
.pink { .pink {
border-color : @nzbdronePink; border-color : @nzbdronePink;
background-color : @nzbdronePink; background-color : @nzbdronePink;
} }
.premiere { .premiere {
border-color : @droneTeal; border-color : @droneTeal;
background-color : @droneTeal; background-color : @droneTeal;
.color-impaired-background-gradient(90deg, @droneTeal); .color-impaired-background-gradient(90deg, @droneTeal);
} }
.unmonitored { .unmonitored {
border-color : grey; border-color : grey;
background-color : grey; background-color : grey;
.color-impaired-background-gradient(45deg, grey); .color-impaired-background-gradient(45deg, grey);
} }
.chart { .chart {
margin-top : 2px; margin-top : 2px;
margin-right : 2px; margin-right : 2px;
line-height : 12px; line-height : 12px;
} }
.legend-labels { .legend-labels {
max-width : 100%; max-width : 100%;
width : 500px; width : 500px;
@media (max-width: @screen-xs-min) { @media (max-width: @screen-xs-min) {
width : 100%; width : 100%;
} }
} }
.legend-label { .legend-label {
display : inline-block; display : inline-block;
width : 150px; width : 150px;
} }
} }
.ical { .ical {
color: @btn-link-disabled-color; color: @btn-link-disabled-color;
cursor: pointer; cursor: pointer;
} }
.ical-url { .ical-url {
input, input[readonly] { input, input[readonly] {
cursor : text; cursor : text;
} }
} }
.calendar-title { .calendar-title {
text-align : center; text-align : center;
h2 { h2 {
margin-top : 0px; margin-top : 0px;
margin-bottom : 5px; margin-bottom : 5px;
} }
} }
.calendar-toolbar { .calendar-toolbar {
.page-toolbar { .page-toolbar {
margin-bottom : 10px; margin-bottom : 10px;
} }
} }

@ -1,7 +1,7 @@
/*! /*!
* FullCalendar v2.3.2 Stylesheet * FullCalendar v3.1.0 Stylesheet
* Docs & License: http://fullcalendar.io/ * Docs & License: http://fullcalendar.io/
* (c) 2015 Adam Shaw * (c) 2016 Adam Shaw
*/ */
@ -28,7 +28,10 @@ body .fc { /* extra precedence to overcome jqui */
.fc-unthemed tbody, .fc-unthemed tbody,
.fc-unthemed .fc-divider, .fc-unthemed .fc-divider,
.fc-unthemed .fc-row, .fc-unthemed .fc-row,
.fc-unthemed .fc-popover { .fc-unthemed .fc-content, /* for gutter border */
.fc-unthemed .fc-popover,
.fc-unthemed .fc-list-view,
.fc-unthemed .fc-list-heading td {
border-color: #ddd; border-color: #ddd;
} }
@ -37,7 +40,8 @@ body .fc { /* extra precedence to overcome jqui */
} }
.fc-unthemed .fc-divider, .fc-unthemed .fc-divider,
.fc-unthemed .fc-popover .fc-header { .fc-unthemed .fc-popover .fc-header,
.fc-unthemed .fc-list-heading td {
background: #eee; background: #eee;
} }
@ -45,20 +49,18 @@ body .fc { /* extra precedence to overcome jqui */
color: #666; color: #666;
} }
.fc-unthemed .fc-today { .fc-unthemed td.fc-today {
background: #fcf8e3; background: #fcf8e3;
} }
.fc-highlight { /* when user is selecting cells */ .fc-highlight { /* when user is selecting cells */
background: #bce8f1; background: #bce8f1;
opacity: .3; opacity: .3;
filter: alpha(opacity=30); /* for IE */
} }
.fc-bgevent { /* default look for background events */ .fc-bgevent { /* default look for background events */
background: rgb(143, 223, 130); background: rgb(143, 223, 130);
opacity: .3; opacity: .3;
filter: alpha(opacity=30); /* for IE */
} }
.fc-nonbusiness { /* default look for non-business-hours areas */ .fc-nonbusiness { /* default look for non-business-hours areas */
@ -72,7 +74,6 @@ body .fc { /* extra precedence to overcome jqui */
.fc-icon { .fc-icon {
display: inline-block; display: inline-block;
width: 1em;
height: 1em; height: 1em;
line-height: 1em; line-height: 1em;
font-size: 1em; font-size: 1em;
@ -99,7 +100,6 @@ NOTE: use percentage font sizes or else old IE chokes
.fc-icon:after { .fc-icon:after {
position: relative; position: relative;
margin: 0 -1em; /* ensures character will be centered, regardless of width */
} }
.fc-icon-left-single-arrow:after { .fc-icon-left-single-arrow:after {
@ -107,7 +107,6 @@ NOTE: use percentage font sizes or else old IE chokes
font-weight: bold; font-weight: bold;
font-size: 200%; font-size: 200%;
top: -7%; top: -7%;
left: 3%;
} }
.fc-icon-right-single-arrow:after { .fc-icon-right-single-arrow:after {
@ -115,7 +114,6 @@ NOTE: use percentage font sizes or else old IE chokes
font-weight: bold; font-weight: bold;
font-size: 200%; font-size: 200%;
top: -7%; top: -7%;
left: -3%;
} }
.fc-icon-left-double-arrow:after { .fc-icon-left-double-arrow:after {
@ -134,14 +132,12 @@ NOTE: use percentage font sizes or else old IE chokes
content: "\25C4"; content: "\25C4";
font-size: 125%; font-size: 125%;
top: 3%; top: 3%;
left: -2%;
} }
.fc-icon-right-triangle:after { .fc-icon-right-triangle:after {
content: "\25BA"; content: "\25BA";
font-size: 125%; font-size: 125%;
top: 3%; top: 3%;
left: 2%;
} }
.fc-icon-down-triangle:after { .fc-icon-down-triangle:after {
@ -252,7 +248,6 @@ NOTE: use percentage font sizes or else old IE chokes
cursor: default; cursor: default;
background-image: none; background-image: none;
opacity: 0.65; opacity: 0.65;
filter: alpha(opacity=65);
box-shadow: none; box-shadow: none;
} }
@ -372,6 +367,7 @@ hr.fc-divider {
.fc table { .fc table {
width: 100%; width: 100%;
box-sizing: border-box; /* fix scrollbar issue in firefox */
table-layout: fixed; table-layout: fixed;
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
@ -395,6 +391,18 @@ hr.fc-divider {
} }
/* Internal Nav Links
--------------------------------------------------------------------------------------------------*/
a[data-goto] {
cursor: pointer;
}
a[data-goto]:hover {
text-decoration: underline;
}
/* Fake Table Rows /* Fake Table Rows
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
@ -491,15 +499,15 @@ temporary rendered events).
/* Scrolling Container /* Scrolling Container
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
.fc-scroller { /* this class goes on elements for guaranteed vertical scrollbars */ .fc-scroller {
overflow-y: scroll; -webkit-overflow-scrolling: touch;
overflow-x: hidden;
} }
.fc-scroller > * { /* we expect an immediate inner element */ /* TODO: move to agenda/basic */
.fc-scroller > .fc-day-grid,
.fc-scroller > .fc-time-grid {
position: relative; /* re-scope all positions */ position: relative; /* re-scope all positions */
width: 100%; /* hack to force re-sizing this inner element when scrollbars appear/disappear */ width: 100%; /* hack to force re-sizing this inner element when scrollbars appear/disappear */
overflow: hidden; /* don't let negative margins or absolute positioning create further scroll */
} }
@ -513,10 +521,14 @@ temporary rendered events).
line-height: 1.3; line-height: 1.3;
border-radius: 3px; border-radius: 3px;
border: 1px solid #3a87ad; /* default BORDER color */ border: 1px solid #3a87ad; /* default BORDER color */
background-color: #3a87ad; /* default BACKGROUND color */
font-weight: normal; /* undo jqui's ui-widget-header bold */ font-weight: normal; /* undo jqui's ui-widget-header bold */
} }
.fc-event,
.fc-event-dot {
background-color: #3a87ad; /* default BACKGROUND color */
}
/* overpower some of bootstrap's and jqui's styles on <a> tags */ /* overpower some of bootstrap's and jqui's styles on <a> tags */
.fc-event, .fc-event,
.fc-event:hover, .fc-event:hover,
@ -539,7 +551,6 @@ temporary rendered events).
z-index: 1; z-index: 1;
background: #fff; background: #fff;
opacity: .25; opacity: .25;
filter: alpha(opacity=25); /* for IE */
} }
.fc-event .fc-content { .fc-event .fc-content {
@ -547,15 +558,68 @@ temporary rendered events).
z-index: 2; z-index: 2;
} }
/* resizer (cursor AND touch devices) */
.fc-event .fc-resizer { .fc-event .fc-resizer {
position: absolute; position: absolute;
z-index: 3; z-index: 4;
}
/* resizer (touch devices) */
.fc-event .fc-resizer {
display: none;
}
.fc-event.fc-allow-mouse-resize .fc-resizer,
.fc-event.fc-selected .fc-resizer {
/* only show when hovering or selected (with touch) */
display: block;
}
/* hit area */
.fc-event.fc-selected .fc-resizer:before {
/* 40x40 touch area */
content: "";
position: absolute;
z-index: 9999; /* user of this util can scope within a lower z-index */
top: 50%;
left: 50%;
width: 40px;
height: 40px;
margin-left: -20px;
margin-top: -20px;
}
/* Event Selection (only for touch devices)
--------------------------------------------------------------------------------------------------*/
.fc-event.fc-selected {
z-index: 9999 !important; /* overcomes inline z-index */
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.fc-event.fc-selected.fc-dragging {
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3);
} }
/* Horizontal Events /* Horizontal Events
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
/* bigger touch area when selected */
.fc-h-event.fc-selected:before {
content: "";
position: absolute;
z-index: 3; /* below resizers */
top: -10px;
bottom: -10px;
left: 0;
right: 0;
}
/* events that are continuing to/from another week. kill rounded corners and butt up against edge */ /* events that are continuing to/from another week. kill rounded corners and butt up against edge */
.fc-ltr .fc-h-event.fc-not-start, .fc-ltr .fc-h-event.fc-not-start,
@ -576,36 +640,56 @@ temporary rendered events).
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
/* resizer */ /* resizer (cursor AND touch devices) */
.fc-h-event .fc-resizer { /* positioned it to overcome the event's borders */
top: -1px;
bottom: -1px;
left: -1px;
right: -1px;
width: 5px;
}
/* left resizer */ /* left resizer */
.fc-ltr .fc-h-event .fc-start-resizer, .fc-ltr .fc-h-event .fc-start-resizer,
.fc-ltr .fc-h-event .fc-start-resizer:before, .fc-rtl .fc-h-event .fc-end-resizer {
.fc-ltr .fc-h-event .fc-start-resizer:after,
.fc-rtl .fc-h-event .fc-end-resizer,
.fc-rtl .fc-h-event .fc-end-resizer:before,
.fc-rtl .fc-h-event .fc-end-resizer:after {
right: auto; /* ignore the right and only use the left */
cursor: w-resize; cursor: w-resize;
left: -1px; /* overcome border */
} }
/* right resizer */ /* right resizer */
.fc-ltr .fc-h-event .fc-end-resizer, .fc-ltr .fc-h-event .fc-end-resizer,
.fc-ltr .fc-h-event .fc-end-resizer:before, .fc-rtl .fc-h-event .fc-start-resizer {
.fc-ltr .fc-h-event .fc-end-resizer:after,
.fc-rtl .fc-h-event .fc-start-resizer,
.fc-rtl .fc-h-event .fc-start-resizer:before,
.fc-rtl .fc-h-event .fc-start-resizer:after {
left: auto; /* ignore the left and only use the right */
cursor: e-resize; cursor: e-resize;
right: -1px; /* overcome border */
}
/* resizer (mouse devices) */
.fc-h-event.fc-allow-mouse-resize .fc-resizer {
width: 7px;
top: -1px; /* overcome top border */
bottom: -1px; /* overcome bottom border */
}
/* resizer (touch devices) */
.fc-h-event.fc-selected .fc-resizer {
/* 8x8 little dot */
border-radius: 4px;
border-width: 1px;
width: 6px;
height: 6px;
border-style: solid;
border-color: inherit;
background: #fff;
/* vertically center */
top: 50%;
margin-top: -4px;
}
/* left resizer */
.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,
.fc-rtl .fc-h-event.fc-selected .fc-end-resizer {
margin-left: -4px; /* centers the 8x8 dot on the left edge */
}
/* right resizer */
.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,
.fc-rtl .fc-h-event.fc-selected .fc-start-resizer {
margin-right: -4px; /* centers the 8x8 dot on the right edge */
} }
@ -620,6 +704,23 @@ be a descendant of the grid when it is being dragged.
padding: 0 1px; padding: 0 1px;
} }
tr:first-child > td > .fc-day-grid-event {
margin-top: 2px; /* a little bit more space before the first event */
}
.fc-day-grid-event.fc-selected:after {
content: "";
position: absolute;
z-index: 1; /* same z-index as fc-bg, behind text */
/* overcome the borders */
top: -1px;
right: -1px;
bottom: -1px;
left: -1px;
/* darkening effect */
background: #000;
opacity: .25;
}
.fc-day-grid-event .fc-content { /* force events to be one-line tall */ .fc-day-grid-event .fc-content { /* force events to be one-line tall */
white-space: nowrap; white-space: nowrap;
@ -630,10 +731,18 @@ be a descendant of the grid when it is being dragged.
font-weight: bold; font-weight: bold;
} }
.fc-day-grid-event .fc-resizer { /* enlarge the default hit area */ /* resizer (cursor devices) */
left: -3px;
right: -3px; /* left resizer */
width: 7px; .fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,
.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer {
margin-left: -2px; /* to the day cell's edge */
}
/* right resizer */
.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,
.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer {
margin-right: -2px; /* to the day cell's edge */
} }
@ -672,14 +781,46 @@ a.fc-more:hover {
padding: 10px; padding: 10px;
} }
/* Now Indicator
--------------------------------------------------------------------------------------------------*/
.fc-now-indicator {
position: absolute;
border: 0 solid red;
}
/* Utilities
--------------------------------------------------------------------------------------------------*/
.fc-unselectable {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
/* Toolbar /* Toolbar
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
.fc-toolbar { .fc-toolbar {
text-align: center; text-align: center;
}
.fc-toolbar.fc-header-toolbar {
margin-bottom: 1em; margin-bottom: 1em;
} }
.fc-toolbar.fc-footer-toolbar {
margin-top: 1em;
}
.fc-toolbar .fc-left { .fc-toolbar .fc-left {
float: left; float: left;
} }
@ -753,6 +894,8 @@ a.fc-more:hover {
z-index: 1; z-index: 1;
} }
/* BasicView /* BasicView
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
@ -760,8 +903,7 @@ a.fc-more:hover {
.fc-basicWeek-view .fc-content-skeleton, .fc-basicWeek-view .fc-content-skeleton,
.fc-basicDay-view .fc-content-skeleton { .fc-basicDay-view .fc-content-skeleton {
/* we are sure there are no day numbers in these views, so... */ /* there may be week numbers in these views, so no padding-top */
padding-top: 1px; /* add a pixel to make sure there are 2px padding above events */
padding-bottom: 1em; /* ensure a space at bottom of cell for user selecting/clicking */ padding-bottom: 1em; /* ensure a space at bottom of cell for user selecting/clicking */
} }
@ -784,42 +926,45 @@ a.fc-more:hover {
/* week and day number styling */ /* week and day number styling */
.fc-day-top.fc-other-month {
opacity: 0.3;
}
.fc-basic-view .fc-week-number, .fc-basic-view .fc-week-number,
.fc-basic-view .fc-day-number { .fc-basic-view .fc-day-number {
padding: 0 2px; padding: 2px;
} }
.fc-basic-view td.fc-week-number span, .fc-basic-view th.fc-week-number,
.fc-basic-view td.fc-day-number { .fc-basic-view th.fc-day-number {
padding-top: 2px; padding: 0 2px; /* column headers can't have as much v space */
padding-bottom: 2px;
} }
.fc-basic-view .fc-week-number { .fc-ltr .fc-basic-view .fc-day-top .fc-day-number { float: right; }
.fc-rtl .fc-basic-view .fc-day-top .fc-day-number { float: left; }
.fc-ltr .fc-basic-view .fc-day-top .fc-week-number { float: left; border-radius: 0 0 3px 0; }
.fc-rtl .fc-basic-view .fc-day-top .fc-week-number { float: right; border-radius: 0 0 0 3px; }
.fc-basic-view .fc-day-top .fc-week-number {
min-width: 1.5em;
text-align: center; text-align: center;
background-color: #f2f2f2;
color: #808080;
} }
.fc-basic-view .fc-week-number span { /* when week/day number have own column */
/* work around the way we do column resizing and ensure a minimum width */
display: inline-block;
min-width: 1.25em;
}
.fc-ltr .fc-basic-view .fc-day-number { .fc-basic-view td.fc-week-number {
text-align: right; text-align: center;
} }
.fc-rtl .fc-basic-view .fc-day-number { .fc-basic-view td.fc-week-number > * {
text-align: left; /* work around the way we do column resizing and ensure a minimum width */
display: inline-block;
min-width: 1.25em;
} }
.fc-day-number.fc-other-month {
opacity: 0.3;
filter: alpha(opacity=30); /* for IE */
/* opacity with small font can sometimes look too faded
might want to set the 'color' property instead
making day-numbers bold also fixes the problem */
}
/* AgendaView all-day area /* AgendaView all-day area
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
@ -834,7 +979,6 @@ a.fc-more:hover {
} }
.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton { .fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton {
padding-top: 1px; /* add a pixel to make sure there are 2px padding above events */
padding-bottom: 1em; /* give space underneath events for clicking/selecting days */ padding-bottom: 1em; /* give space underneath events for clicking/selecting days */
} }
@ -888,27 +1032,46 @@ a.fc-more:hover {
z-index: 2; z-index: 2;
} }
.fc-time-grid .fc-bgevent-skeleton, .fc-time-grid .fc-content-col {
position: relative; /* because now-indicator lives directly inside */
}
.fc-time-grid .fc-content-skeleton { .fc-time-grid .fc-content-skeleton {
position: absolute; position: absolute;
z-index: 3;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
} }
.fc-time-grid .fc-bgevent-skeleton { /* divs within a cell within the fc-content-skeleton */
.fc-time-grid .fc-business-container {
position: relative;
z-index: 1;
}
.fc-time-grid .fc-bgevent-container {
position: relative;
z-index: 2;
}
.fc-time-grid .fc-highlight-container {
position: relative;
z-index: 3; z-index: 3;
} }
.fc-time-grid .fc-highlight-skeleton { .fc-time-grid .fc-event-container {
position: relative;
z-index: 4; z-index: 4;
} }
.fc-time-grid .fc-content-skeleton { .fc-time-grid .fc-now-indicator-line {
z-index: 5; z-index: 5;
} }
.fc-time-grid .fc-helper-skeleton { .fc-time-grid .fc-helper-container { /* also is fc-event-container */
position: relative;
z-index: 6; z-index: 6;
} }
@ -948,11 +1111,6 @@ a.fc-more:hover {
/* TimeGrid Event Containment /* TimeGrid Event Containment
--------------------------------------------------------------------------------------------------*/ --------------------------------------------------------------------------------------------------*/
.fc-time-grid .fc-event-container, /* a div within a cell within the fc-content-skeleton */
.fc-time-grid .fc-bgevent-container { /* a div within a cell within the fc-bgevent-skeleton */
position: relative;
}
.fc-ltr .fc-time-grid .fc-event-container { /* space on the sides of events for LTR (default) */ .fc-ltr .fc-time-grid .fc-event-container { /* space on the sides of events for LTR (default) */
margin: 0 2.5% 0 2px; margin: 0 2.5% 0 2px;
} }
@ -1008,6 +1166,20 @@ be a descendant of the grid when it is being dragged.
overflow: hidden; /* don't let the bg flow over rounded corners */ overflow: hidden; /* don't let the bg flow over rounded corners */
} }
.fc-time-grid-event.fc-selected {
/* need to allow touch resizers to extend outside event's bounding box */
/* common fc-selected styles hide the fc-bg, so don't need this anyway */
overflow: visible;
}
.fc-time-grid-event.fc-selected .fc-bg {
display: none; /* hide semi-white background, to appear darker */
}
.fc-time-grid-event .fc-content {
overflow: hidden; /* for when .fc-selected */
}
.fc-time-grid-event .fc-time, .fc-time-grid-event .fc-time,
.fc-time-grid-event .fc-title { .fc-time-grid-event .fc-title {
padding: 0 1px; padding: 0 1px;
@ -1049,9 +1221,9 @@ be a descendant of the grid when it is being dragged.
padding: 0; /* undo padding from above */ padding: 0; /* undo padding from above */
} }
/* resizer */ /* resizer (cursor device) */
.fc-time-grid-event .fc-resizer { .fc-time-grid-event.fc-allow-mouse-resize .fc-resizer {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
@ -1064,6 +1236,169 @@ be a descendant of the grid when it is being dragged.
cursor: s-resize; cursor: s-resize;
} }
.fc-time-grid-event .fc-resizer:after { .fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after {
content: "="; content: "=";
} }
/* resizer (touch device) */
.fc-time-grid-event.fc-selected .fc-resizer {
/* 10x10 dot */
border-radius: 5px;
border-width: 1px;
width: 8px;
height: 8px;
border-style: solid;
border-color: inherit;
background: #fff;
/* horizontally center */
left: 50%;
margin-left: -5px;
/* center on the bottom edge */
bottom: -5px;
}
/* Now Indicator
--------------------------------------------------------------------------------------------------*/
.fc-time-grid .fc-now-indicator-line {
border-top-width: 1px;
left: 0;
right: 0;
}
/* arrow on axis */
.fc-time-grid .fc-now-indicator-arrow {
margin-top: -5px; /* vertically center on top coordinate */
}
.fc-ltr .fc-time-grid .fc-now-indicator-arrow {
left: 0;
/* triangle pointing right... */
border-width: 5px 0 5px 6px;
border-top-color: transparent;
border-bottom-color: transparent;
}
.fc-rtl .fc-time-grid .fc-now-indicator-arrow {
right: 0;
/* triangle pointing left... */
border-width: 5px 6px 5px 0;
border-top-color: transparent;
border-bottom-color: transparent;
}
/* List View
--------------------------------------------------------------------------------------------------*/
/* possibly reusable */
.fc-event-dot {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 5px;
}
/* view wrapper */
.fc-rtl .fc-list-view {
direction: rtl; /* unlike core views, leverage browser RTL */
}
.fc-list-view {
border-width: 1px;
border-style: solid;
}
/* table resets */
.fc .fc-list-table {
table-layout: auto; /* for shrinkwrapping cell content */
}
.fc-list-table td {
border-width: 1px 0 0;
padding: 8px 14px;
}
.fc-list-table tr:first-child td {
border-top-width: 0;
}
/* day headings with the list */
.fc-list-heading {
border-bottom-width: 1px;
}
.fc-list-heading td {
font-weight: bold;
}
.fc-ltr .fc-list-heading-main { float: left; }
.fc-ltr .fc-list-heading-alt { float: right; }
.fc-rtl .fc-list-heading-main { float: right; }
.fc-rtl .fc-list-heading-alt { float: left; }
/* event list items */
.fc-list-item.fc-has-url {
cursor: pointer; /* whole row will be clickable */
}
.fc-list-item:hover td {
background-color: #f5f5f5;
}
.fc-list-item-marker,
.fc-list-item-time {
white-space: nowrap;
width: 1px;
}
/* make the dot closer to the event title */
.fc-ltr .fc-list-item-marker { padding-right: 0; }
.fc-rtl .fc-list-item-marker { padding-left: 0; }
.fc-list-item-title a {
/* every event title cell has an <a> tag */
text-decoration: none;
color: inherit;
}
.fc-list-item-title a[href]:hover {
/* hover effect only on titles with hrefs */
text-decoration: underline;
}
/* message when no events */
.fc-list-empty-wrap2 {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.fc-list-empty-wrap1 {
width: 100%;
height: 100%;
display: table;
}
.fc-list-empty {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.fc-unthemed .fc-list-empty { /* theme will provide own background */
background-color: #eee;
}

@ -21,7 +21,7 @@ Handlebars.registerHelper('StatusLevel', function() {
var start = moment(this.airDateUtc); var start = moment(this.airDateUtc);
var end = moment(this.end); var end = moment(this.end);
var monitored = this.series.monitored && this.monitored; var monitored = this.series.monitored && this.monitored;
debugger;
if (hasFile) { if (hasFile) {
return 'success'; return 'success';
} }
@ -63,4 +63,4 @@ Handlebars.registerHelper('EpisodeProgressClass', function() {
} }
return 'progress-bar-warning'; return 'progress-bar-warning';
}); });

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -6,33 +6,33 @@ var MoviesDetailsLayout = require('./Details/MoviesDetailsLayout');
var SeriesDetailsLayout = require('../Series/Details/SeriesDetailsLayout'); var SeriesDetailsLayout = require('../Series/Details/SeriesDetailsLayout');
module.exports = NzbDroneController.extend({ module.exports = NzbDroneController.extend({
_originalInit : NzbDroneController.prototype.initialize, _originalInit : NzbDroneController.prototype.initialize,
initialize : function() { initialize : function() {
this.route('', this.series); this.route('', this.series);
this.route('movies', this.series); this.route('movies', this.series);
this.route('movies/:query', this.seriesDetails); this.route('movies/:query', this.seriesDetails);
this._originalInit.apply(this, arguments); this._originalInit.apply(this, arguments);
}, },
series : function() { series : function() {
this.setTitle('Movies'); this.setTitle('Movies');
this.showMainRegion(new MoviesIndexLayout()); this.showMainRegion(new MoviesIndexLayout());
}, },
seriesDetails : function(query) { seriesDetails : function(query) {
var series = MoviesCollection.where({ titleSlug : query }); var series = MoviesCollection.where({ titleSlug : query });
if (series.length !== 0) { if (series.length !== 0) {
var targetMovie = series[0]; var targetMovie = series[0];
console.log(AppLayout.mainRegion); console.log(AppLayout.mainRegion);
this.setTitle(targetMovie.get('title')); this.setTitle(targetMovie.get('title'));
//this.showNotFound(); //this.showNotFound();
//this.showMainRegion(new SeriesDetailsLayout({model : targetMovie})); //this.showMainRegion(new SeriesDetailsLayout({model : targetMovie}));
this.showMainRegion(new MoviesDetailsLayout({ model : targetMovie })); this.showMainRegion(new MoviesDetailsLayout({ model : targetMovie }));
} else { } else {
this.showNotFound(); this.showNotFound();
} }
} }
}); });

Loading…
Cancel
Save