mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-20 21:43:33 -07:00
Merge branch 'signalr' into develop
This commit is contained in:
commit
93481728cc
233 changed files with 2205 additions and 1813 deletions
|
@ -1,9 +1,9 @@
|
|||
{{#if folder.path}}
|
||||
<div class="row well unmapped-folder-path">
|
||||
<div class="span11">
|
||||
{{folder.path}}
|
||||
</div>
|
||||
<div class="row well unmapped-folder-path">
|
||||
<div class="span11">
|
||||
{{folder.path}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="row x-search-bar">
|
||||
<div class="input-prepend nz-input-large add-series-search span11">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
|
@ -8,10 +8,19 @@ define(
|
|||
|
||||
var CommandCollection = Backbone.Collection.extend({
|
||||
url : window.ApiRoot + '/command',
|
||||
model: CommandModel
|
||||
model: CommandModel,
|
||||
|
||||
findCommand: function (command) {
|
||||
return this.find(function (model) {
|
||||
return model.isSameCommand(command);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var collection = new CommandCollection().bindSignalR();
|
||||
|
||||
collection.fetch();
|
||||
|
||||
return collection;
|
||||
});
|
||||
|
|
|
@ -1,16 +1,56 @@
|
|||
'use strict';
|
||||
define({
|
||||
Execute: function (name, properties) {
|
||||
var data = { command: name };
|
||||
define(
|
||||
[
|
||||
'Commands/CommandModel',
|
||||
'Commands/CommandCollection',
|
||||
'underscore',
|
||||
'jQuery/jquery.spin'
|
||||
], function (CommandModel, CommandCollection, _) {
|
||||
|
||||
if (properties) {
|
||||
$.extend(data, properties);
|
||||
return{
|
||||
|
||||
Execute: function (name, properties) {
|
||||
|
||||
var attr = _.extend({name: name.toLocaleLowerCase()}, properties);
|
||||
|
||||
var commandModel = new CommandModel(attr);
|
||||
|
||||
return commandModel.save().success(function () {
|
||||
CommandCollection.add(commandModel);
|
||||
});
|
||||
},
|
||||
|
||||
bindToCommand: function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var existingCommand = CommandCollection.findCommand(options.command);
|
||||
|
||||
if (existingCommand) {
|
||||
this._bindToCommandModel.call(this, existingCommand, options);
|
||||
}
|
||||
|
||||
CommandCollection.bind('add sync', function (model) {
|
||||
if (model.isSameCommand(options.command)) {
|
||||
self._bindToCommandModel.call(self, model, options);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_bindToCommandModel: function bindToCommand(model, options) {
|
||||
|
||||
if (!model.isActive()) {
|
||||
options.element.stopSpin();
|
||||
return;
|
||||
}
|
||||
|
||||
model.bind('change:state', function (model) {
|
||||
if (!model.isActive()) {
|
||||
options.element.stopSpin();
|
||||
}
|
||||
});
|
||||
|
||||
options.element.startSpin();
|
||||
}
|
||||
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url : window.ApiRoot + '/command',
|
||||
data: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
15
UI/Commands/CommandMessengerCollectionView.js
Normal file
15
UI/Commands/CommandMessengerCollectionView.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Commands/CommandCollection',
|
||||
'Commands/CommandMessengerItemView'
|
||||
], function (App, Marionette, commandCollection, CommandMessengerItemView) {
|
||||
|
||||
var CollectionView = Marionette.CollectionView.extend({
|
||||
itemView : CommandMessengerItemView
|
||||
});
|
||||
|
||||
new CollectionView({collection: commandCollection});
|
||||
});
|
48
UI/Commands/CommandMessengerItemView.js
Normal file
48
UI/Commands/CommandMessengerItemView.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Shared/Messenger'
|
||||
], function (App, Marionette, Messenger) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
},
|
||||
|
||||
|
||||
render: function () {
|
||||
if (!this.model.get('message') || !this.model.get('sendUpdatesToClient')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message = {
|
||||
type : 'info',
|
||||
message : '[{0}] {1}'.format(this.model.get('name'), this.model.get('message')),
|
||||
id : this.model.id,
|
||||
hideAfter: 0
|
||||
};
|
||||
|
||||
switch (this.model.get('state')) {
|
||||
case 'completed':
|
||||
message.hideAfter = 4;
|
||||
break;
|
||||
case 'failed':
|
||||
message.hideAfter = 4;
|
||||
message.type = 'error';
|
||||
break;
|
||||
default :
|
||||
message.hideAfter = 0;
|
||||
}
|
||||
|
||||
Messenger.show(message);
|
||||
|
||||
console.log(message.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -4,5 +4,30 @@ define(
|
|||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
url: window.ApiRoot + '/command',
|
||||
|
||||
parse: function (response) {
|
||||
response.name = response.name.toLocaleLowerCase();
|
||||
return response;
|
||||
},
|
||||
|
||||
isActive: function () {
|
||||
return this.get('state') !== 'completed' && this.get('state') !== 'failed';
|
||||
},
|
||||
|
||||
isSameCommand: function (command) {
|
||||
|
||||
if (command.name.toLocaleLowerCase() != this.get('name').toLocaleLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var key in command) {
|
||||
if (key !== 'name' && command[key] !== this.get(key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,6 @@ define(
|
|||
'Series/Details/SeriesDetailsLayout',
|
||||
'Series/SeriesCollection',
|
||||
'Missing/MissingLayout',
|
||||
'Series/SeriesModel',
|
||||
'Calendar/CalendarLayout',
|
||||
'Logs/Layout',
|
||||
'Logs/Files/Layout',
|
||||
|
@ -19,7 +18,7 @@ define(
|
|||
'SeasonPass/Layout',
|
||||
'Shared/NotFoundView',
|
||||
'Shared/Modal/Region'
|
||||
], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, SeriesCollection, MissingLayout, SeriesModel, CalendarLayout,
|
||||
], function (App, Marionette, HistoryLayout, SettingsLayout, AddSeriesLayout, SeriesIndexLayout, SeriesDetailsLayout, SeriesCollection, MissingLayout, CalendarLayout,
|
||||
LogsLayout, LogFileLayout, ReleaseLayout, SystemLayout, SeasonPassLayout, NotFoundView) {
|
||||
return Marionette.Controller.extend({
|
||||
|
||||
|
|
|
@ -7,11 +7,9 @@ define(
|
|||
'Episode/Search/ManualLayout',
|
||||
'Release/Collection',
|
||||
'Series/SeriesCollection',
|
||||
'Shared/LoadingView',
|
||||
'Shared/Messenger',
|
||||
'Shared/Actioneer',
|
||||
'Shared/FormatHelpers'
|
||||
], function (App, Marionette, ButtonsView, ManualSearchLayout, ReleaseCollection, SeriesCollection, LoadingView, Messenger, Actioneer, FormatHelpers) {
|
||||
'Commands/CommandController',
|
||||
'Shared/LoadingView'
|
||||
], function (App, Marionette, ButtonsView, ManualSearchLayout, ReleaseCollection, SeriesCollection,CommandController, LoadingView) {
|
||||
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Episode/Search/LayoutTemplate',
|
||||
|
@ -39,18 +37,8 @@ define(
|
|||
e.preventDefault();
|
||||
}
|
||||
|
||||
var series = SeriesCollection.get(this.model.get('seriesId'));
|
||||
var seriesTitle = series.get('title');
|
||||
var season = this.model.get('seasonNumber');
|
||||
var episode = this.model.get('episodeNumber');
|
||||
var message = seriesTitle + ' - ' + season + 'x' + FormatHelpers.pad(episode, 2);
|
||||
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'episodeSearch',
|
||||
properties: {
|
||||
episodeId: this.model.get('id')
|
||||
},
|
||||
errorMessage: 'Search failed for: ' + message
|
||||
CommandController.Execute('episodeSearch', {
|
||||
episodeId: this.model.get('id')
|
||||
});
|
||||
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
|
|
|
@ -5,59 +5,29 @@ define(
|
|||
], function () {
|
||||
|
||||
_.extend(Backbone.Collection.prototype, {
|
||||
bindSignalR: function (options) {
|
||||
bindSignalR: function () {
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
var collection = this;
|
||||
|
||||
if (!options.url) {
|
||||
console.assert(this.url, 'url must be provided or collection must have url');
|
||||
options.url = this.url.replace('api', 'signalr');
|
||||
}
|
||||
var processMessage = function (options) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var _getStatus = function (status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'connecting';
|
||||
case 1:
|
||||
return 'connected';
|
||||
case 2:
|
||||
return 'reconnecting';
|
||||
case 4:
|
||||
return 'disconnected';
|
||||
default:
|
||||
throw 'invalid status ' + status;
|
||||
}
|
||||
var model = new collection.model(options.resource, {parse: true});
|
||||
collection.add(model, {merge: true});
|
||||
console.log(options.action + ": %O", options.resource);
|
||||
};
|
||||
|
||||
this.signalRconnection = $.connection(options.url);
|
||||
|
||||
this.signalRconnection.stateChanged(function (change) {
|
||||
console.debug('{0} [{1}]'.format(options.url, _getStatus(change.newState)));
|
||||
});
|
||||
|
||||
this.signalRconnection.received(function (message) {
|
||||
console.debug(message);
|
||||
self.fetch();
|
||||
});
|
||||
|
||||
this.signalRconnection.start({ transport:
|
||||
require(
|
||||
[
|
||||
'longPolling'
|
||||
] });
|
||||
'app'
|
||||
], function (app) {
|
||||
collection.listenTo(app.vent, 'server:' + collection.url.replace('/api/', ''), processMessage)
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
unbindSignalR: function () {
|
||||
|
||||
if(this.signalRconnection){
|
||||
this.signalRconnection.stop();
|
||||
delete this.signalRconnection;
|
||||
}
|
||||
|
||||
}});
|
||||
});
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'backbone',
|
||||
'ProgressMessaging/ProgressMessageModel',
|
||||
'Shared/Messenger',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (Backbone, ProgressMessageModel, Messenger) {
|
||||
], function (App, Backbone, Messenger) {
|
||||
|
||||
var ProgressMessageCollection = Backbone.Collection.extend({
|
||||
url : window.ApiRoot + '/progressmessage',
|
||||
model: ProgressMessageModel
|
||||
model: Backbone.Model,
|
||||
|
||||
initialize: function(){
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var collection = new ProgressMessageCollection().bindSignalR();
|
||||
var collection = new ProgressMessageCollection();//.bindSignalR();
|
||||
|
||||
collection.signalRconnection.received(function (message) {
|
||||
/* collection.signalRconnection.received(function (message) {
|
||||
|
||||
var type = getMessengerType(message.status);
|
||||
var hideAfter = type === 'info' ? 60 : 5;
|
||||
var type = getMessengerType(message.status);
|
||||
var hideAfter = type === 'info' ? 60 :5;
|
||||
|
||||
Messenger.show({
|
||||
id : message.commandId,
|
||||
message : message.message,
|
||||
type : type,
|
||||
hideAfter: hideAfter
|
||||
});
|
||||
});
|
||||
Messenger.show({
|
||||
id : message.commandId,
|
||||
message : message.message,
|
||||
type : type,
|
||||
hideAfter: hideAfter
|
||||
});
|
||||
});*/
|
||||
|
||||
var getMessengerType = function (status) {
|
||||
switch (status) {
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone'
|
||||
], function (Backbone) {
|
||||
return Backbone.Model.extend({
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
|
|
14
UI/Router.js
14
UI/Router.js
|
@ -6,11 +6,11 @@ require(
|
|||
'Controller',
|
||||
'Series/SeriesCollection',
|
||||
'ProgressMessaging/ProgressMessageCollection',
|
||||
'Shared/Actioneer',
|
||||
'Commands/CommandMessengerCollectionView',
|
||||
'Navbar/NavbarView',
|
||||
'jQuery/RouteBinder',
|
||||
'jquery'
|
||||
], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, Actioneer, NavbarView, RouterBinder, $) {
|
||||
], function (App, Marionette, Controller, SeriesCollection, ProgressMessageCollection, CommandMessengerCollectionView, NavbarView, RouterBinder, $) {
|
||||
|
||||
var Router = Marionette.AppRouter.extend({
|
||||
|
||||
|
@ -40,11 +40,11 @@ require(
|
|||
App.Router = new Router();
|
||||
|
||||
SeriesCollection.fetch().done(function () {
|
||||
Backbone.history.start({ pushState: true });
|
||||
RouterBinder.bind(App.Router);
|
||||
App.navbarRegion.show(new NavbarView());
|
||||
$('body').addClass('started');
|
||||
});
|
||||
Backbone.history.start({ pushState: true });
|
||||
RouterBinder.bind(App.Router);
|
||||
App.navbarRegion.show(new NavbarView());
|
||||
$('body').addClass('started');
|
||||
});
|
||||
});
|
||||
|
||||
return App.Router;
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Series/SeasonCollection',
|
||||
'Shared/Actioneer'
|
||||
], function (Marionette, SeasonCollection, Actioneer) {
|
||||
'backgrid',
|
||||
'Series/SeasonCollection'
|
||||
], function (Marionette, Backgrid, SeasonCollection) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'SeasonPass/SeriesLayoutTemplate',
|
||||
|
||||
|
@ -111,11 +111,9 @@ define(
|
|||
|
||||
this.model.setSeasonMonitored(seasonNumber);
|
||||
|
||||
Actioneer.SaveModel({
|
||||
element: element,
|
||||
context: this,
|
||||
always : this._afterToggleSeasonMonitored
|
||||
});
|
||||
var savePromise =this.model.save()
|
||||
.always(this.render.bind(this));
|
||||
element.spinForPromise(savePromise);
|
||||
},
|
||||
|
||||
_afterToggleSeasonMonitored: function () {
|
||||
|
|
|
@ -5,6 +5,10 @@ define(
|
|||
], function (Marionette) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Series/Details/InfoViewTemplate'
|
||||
template: 'Series/Details/InfoViewTemplate',
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'change', this.render);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,9 +8,9 @@ define(
|
|||
'Cells/EpisodeTitleCell',
|
||||
'Cells/RelativeDateCell',
|
||||
'Cells/EpisodeStatusCell',
|
||||
'Shared/Actioneer',
|
||||
'Commands/CommandController',
|
||||
'moment'
|
||||
], function (App, Marionette, Backgrid, ToggleCell, EpisodeTitleCell, RelativeDateCell, EpisodeStatusCell, Actioneer, Moment) {
|
||||
], function (App, Marionette, Backgrid, ToggleCell, EpisodeTitleCell, RelativeDateCell, EpisodeStatusCell, CommandController, Moment) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Series/Details/SeasonLayoutTemplate',
|
||||
|
||||
|
@ -21,11 +21,11 @@ define(
|
|||
},
|
||||
|
||||
events: {
|
||||
'click .x-season-search' : '_seasonSearch',
|
||||
'click .x-season-monitored' : '_seasonMonitored',
|
||||
'click .x-season-rename' : '_seasonRename',
|
||||
'click .x-show-hide-episodes' : '_showHideEpisodes',
|
||||
'dblclick .series-season h2' : '_showHideEpisodes'
|
||||
'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: {
|
||||
|
@ -51,11 +51,11 @@ define(
|
|||
})
|
||||
},
|
||||
{
|
||||
name : 'this',
|
||||
label : 'Title',
|
||||
hideSeriesLink : true,
|
||||
cell : EpisodeTitleCell,
|
||||
sortable: false
|
||||
name : 'this',
|
||||
label : 'Title',
|
||||
hideSeriesLink: true,
|
||||
cell : EpisodeTitleCell,
|
||||
sortable : false
|
||||
},
|
||||
{
|
||||
name : 'airDateUtc',
|
||||
|
@ -76,41 +76,58 @@ define(
|
|||
throw 'episodeCollection is needed';
|
||||
}
|
||||
|
||||
this.templateHelpers = {};
|
||||
this.episodeCollection = options.episodeCollection.bySeason(this.model.get('seasonNumber'));
|
||||
this.series = options.series;
|
||||
|
||||
this.showingEpisodes = this._shouldShowEpisodes();
|
||||
this._generateTemplateHelpers();
|
||||
|
||||
this.listenTo(this.model, 'sync', function () {
|
||||
this._afterSeasonMonitored();
|
||||
}, this);
|
||||
|
||||
this.listenTo(this.episodeCollection, 'sync', function () {
|
||||
this.render();
|
||||
}, this);
|
||||
this.listenTo(this.model, 'sync', this._afterSeasonMonitored);
|
||||
this.listenTo(this.episodeCollection, 'sync', this.render);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
|
||||
|
||||
if (this.showingEpisodes) {
|
||||
this._showEpisodes();
|
||||
}
|
||||
|
||||
this._setSeasonMonitoredState();
|
||||
|
||||
CommandController.bindToCommand({
|
||||
element: this.ui.seasonSearch,
|
||||
command: {
|
||||
name : 'seasonSearch',
|
||||
seriesId : this.series.id,
|
||||
seasonNumber: this.model.get('seasonNumber')
|
||||
}
|
||||
});
|
||||
|
||||
CommandController.bindToCommand({
|
||||
element: this.ui.seasonRename,
|
||||
command: {
|
||||
name : 'renameSeason',
|
||||
seriesId : this.series.id,
|
||||
seasonNumber: this.model.get('seasonNumber')
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_seasonSearch: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'seasonSearch',
|
||||
properties : {
|
||||
seriesId : this.model.get('seriesId'),
|
||||
seasonNumber: this.model.get('seasonNumber')
|
||||
},
|
||||
element : this.ui.seasonSearch,
|
||||
errorMessage : 'Search for season {0} failed'.format(this.model.get('seasonNumber')),
|
||||
startMessage : 'Search for season {0} started'.format(this.model.get('seasonNumber')),
|
||||
successMessage: 'Search for season {0} completed'.format(this.model.get('seasonNumber'))
|
||||
|
||||
CommandController.Execute('seasonSearch', {
|
||||
name : 'seasonSearch',
|
||||
seriesId : this.series.id,
|
||||
seasonNumber: this.model.get('seasonNumber')
|
||||
});
|
||||
},
|
||||
|
||||
_seasonRename: function () {
|
||||
|
||||
CommandController.Execute('renameSeason', {
|
||||
name : 'renameSeason',
|
||||
seriesId : this.series.id,
|
||||
seasonNumber: this.model.get('seasonNumber')
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -119,12 +136,9 @@ define(
|
|||
this.model.set(name, !this.model.get(name));
|
||||
this.series.setSeasonMonitored(this.model.get('seasonNumber'));
|
||||
|
||||
Actioneer.SaveModel({
|
||||
model : this.series,
|
||||
context: this,
|
||||
element: this.ui.seasonMonitored,
|
||||
always : this._afterSeasonMonitored
|
||||
});
|
||||
var savePromise = this.series.save().always(this._afterSeasonMonitored.bind(this));
|
||||
|
||||
this.ui.seasonMonitored.spinForPromise(savePromise);
|
||||
},
|
||||
|
||||
_afterSeasonMonitored: function () {
|
||||
|
@ -150,19 +164,6 @@ define(
|
|||
}
|
||||
},
|
||||
|
||||
_seasonRename: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'renameSeason',
|
||||
properties : {
|
||||
seriesId : this.model.get('seriesId'),
|
||||
seasonNumber: this.model.get('seasonNumber')
|
||||
},
|
||||
element : this.ui.seasonRename,
|
||||
errorMessage: 'Season rename failed',
|
||||
context : this,
|
||||
onSuccess : this._afterRename
|
||||
});
|
||||
},
|
||||
|
||||
_afterRename: function () {
|
||||
App.vent.trigger(App.Events.SeasonRenamed, { series: this.series, seasonNumber: this.model.get('seasonNumber') });
|
||||
|
@ -180,12 +181,11 @@ define(
|
|||
var startDate = Moment().add('month', -1);
|
||||
var endDate = Moment().add('year', 1);
|
||||
|
||||
var result = 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)) {
|
||||
|
@ -195,15 +195,12 @@ define(
|
|||
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
_generateTemplateHelpers: function () {
|
||||
this.templateHelpers.showingEpisodes = this.showingEpisodes;
|
||||
templateHelpers: function () {
|
||||
|
||||
var episodeCount = this.episodeCollection.filter(function (episode) {
|
||||
return (episode.get('monitored') && Moment(episode.get('airDateUtc')).isBefore(Moment())) || episode.get('hasFile');
|
||||
return episode.get('hasFile') || (episode.get('monitored') && Moment(episode.get('airDateUtc')).isBefore(Moment()));
|
||||
}).length;
|
||||
|
||||
var episodeFileCount = this.episodeCollection.where({ hasFile: true }).length;
|
||||
|
@ -213,22 +210,22 @@ define(
|
|||
percentOfEpisodes = episodeFileCount / episodeCount * 100;
|
||||
}
|
||||
|
||||
this.templateHelpers.episodeCount = episodeCount;
|
||||
this.templateHelpers.episodeFileCount = episodeFileCount;
|
||||
this.templateHelpers.percentOfEpisodes = percentOfEpisodes;
|
||||
return {
|
||||
showingEpisodes : this.showingEpisodes,
|
||||
episodeCount : episodeCount,
|
||||
episodeFileCount : episodeFileCount,
|
||||
percentOfEpisodes: percentOfEpisodes
|
||||
};
|
||||
},
|
||||
|
||||
_showHideEpisodes: function () {
|
||||
if (this.showingEpisodes) {
|
||||
this.showingEpisodes = false;
|
||||
this.episodeGrid.$el.slideUp();
|
||||
this.episodeGrid.close();
|
||||
}
|
||||
|
||||
else {
|
||||
this.showingEpisodes = true;
|
||||
this._showEpisodes();
|
||||
this.episodeGrid.$el.slideDown();
|
||||
}
|
||||
|
||||
this.templateHelpers.showingEpisodes = this.showingEpisodes;
|
||||
|
|
|
@ -1,35 +1,41 @@
|
|||
<div class="series-season" id="season-{{seasonNumber}}">
|
||||
<h2>
|
||||
<i class="x-season-monitored clickable" title="Toggle season monitored status"/>
|
||||
|
||||
|
||||
{{#if seasonNumber}}
|
||||
Season {{seasonNumber}}
|
||||
Season {{seasonNumber}}
|
||||
{{else}}
|
||||
Specials
|
||||
Specials
|
||||
{{/if}}
|
||||
|
||||
{{#if_eq episodeCount compare=0}}
|
||||
<i class="icon-nd-status season-status status-primary" title="No aired episodes"/>
|
||||
<i class="icon-nd-status season-status status-primary" title="No aired episodes"/>
|
||||
{{else}}
|
||||
{{#if_eq percentOfEpisodes compare=100}}
|
||||
<i class="icon-nd-status season-status status-success" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{else}}
|
||||
<i class="icon-nd-status season-status status-danger" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{/if_eq}}
|
||||
{{#if_eq percentOfEpisodes compare=100}}
|
||||
<i class="icon-nd-status season-status status-success" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{else}}
|
||||
<i class="icon-nd-status season-status status-danger" title="{{episodeFileCount}}/{{episodeCount}} episodes downloaded"/>
|
||||
{{/if_eq}}
|
||||
{{/if_eq}}
|
||||
|
||||
<span class="season-actions pull-right">
|
||||
<i class="icon-nd-rename x-season-rename" title="Rename all episodes in season {{seasonNumber}}"/>
|
||||
<i class="icon-search x-season-search" title="Search for all episodes in season {{seasonNumber}}"/>
|
||||
<div class="x-season-rename">
|
||||
<i class="icon-nd-rename" title="Rename all episodes in season {{seasonNumber}}"/>
|
||||
</div>
|
||||
<div class="x-season-search">
|
||||
<i class="icon-search" title="Search for all episodes in season {{seasonNumber}}"/>
|
||||
</div>
|
||||
</span>
|
||||
</h2>
|
||||
<div class="x-episode-grid"></div>
|
||||
<div class="show-hide-episodes x-show-hide-episodes">
|
||||
<h4>
|
||||
{{#if showingEpisodes}}
|
||||
<i class="icon-chevron-sign-up"/> Hide Episodes
|
||||
<i class="icon-chevron-sign-up"/>
|
||||
Hide Episodes
|
||||
{{else}}
|
||||
<i class="icon-chevron-sign-down"/> Show Episodes
|
||||
<i class="icon-chevron-sign-down"/>
|
||||
Show Episodes
|
||||
{{/if}}
|
||||
</h4>
|
||||
</div>
|
||||
|
|
|
@ -8,27 +8,19 @@ define(
|
|||
'Series/SeasonCollection',
|
||||
'Series/Details/SeasonCollectionView',
|
||||
'Series/Details/InfoView',
|
||||
'Commands/CommandController',
|
||||
'Shared/LoadingView',
|
||||
'Shared/Actioneer',
|
||||
'backstrech',
|
||||
'Mixins/backbone.signalr.mixin'
|
||||
], function (App,
|
||||
Marionette,
|
||||
EpisodeCollection,
|
||||
EpisodeFileCollection,
|
||||
SeasonCollection,
|
||||
SeasonCollectionView,
|
||||
InfoView,
|
||||
LoadingView,
|
||||
Actioneer) {
|
||||
], function (App, Marionette, EpisodeCollection, EpisodeFileCollection, SeasonCollection, SeasonCollectionView, InfoView, CommandController, LoadingView) {
|
||||
return Marionette.Layout.extend({
|
||||
|
||||
itemViewContainer: '.x-series-seasons',
|
||||
template : 'Series/Details/SeriesDetailsTemplate',
|
||||
|
||||
regions: {
|
||||
seasons : '#seasons',
|
||||
info : '#info'
|
||||
seasons: '#seasons',
|
||||
info : '#info'
|
||||
},
|
||||
|
||||
ui: {
|
||||
|
@ -51,11 +43,7 @@ define(
|
|||
initialize: function () {
|
||||
$('body').addClass('backdrop');
|
||||
|
||||
this.listenTo(this.model, 'sync', function () {
|
||||
this._setMonitoredState();
|
||||
this._showInfo();
|
||||
}, this);
|
||||
|
||||
this.listenTo(this.model, 'change:monitored', this._setMonitoredState);
|
||||
this.listenTo(App.vent, App.Events.SeriesDeleted, this._onSeriesDeleted);
|
||||
this.listenTo(App.vent, App.Events.SeasonRenamed, this._onSeasonRenamed);
|
||||
},
|
||||
|
@ -73,6 +61,31 @@ define(
|
|||
this._showSeasons();
|
||||
this._setMonitoredState();
|
||||
this._showInfo();
|
||||
|
||||
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
CommandController.bindToCommand({
|
||||
element: this.ui.refresh,
|
||||
command: {
|
||||
name: 'refreshSeries'
|
||||
}
|
||||
});
|
||||
|
||||
CommandController.bindToCommand({
|
||||
element: this.ui.search,
|
||||
command: {
|
||||
name: 'seriesSearch'
|
||||
}
|
||||
});
|
||||
|
||||
CommandController.bindToCommand({
|
||||
element: this.ui.rename,
|
||||
command: {
|
||||
name: 'renameSeries'
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getFanArt: function () {
|
||||
|
@ -97,22 +110,19 @@ define(
|
|||
},
|
||||
|
||||
_toggleMonitored: function () {
|
||||
var name = 'monitored';
|
||||
this.model.set(name, !this.model.get(name), { silent: true });
|
||||
|
||||
Actioneer.SaveModel({
|
||||
context: this,
|
||||
element: this.ui.monitored,
|
||||
always : this._setMonitoredState()
|
||||
var savePromise = this.model.save('monitored', !this.model.get('monitored'), {
|
||||
wait: true
|
||||
});
|
||||
|
||||
this.ui.monitored.spinForPromise(savePromise);
|
||||
},
|
||||
|
||||
_setMonitoredState: function () {
|
||||
var monitored = this.model.get('monitored');
|
||||
|
||||
this.ui.monitored.removeClass('icon-spin icon-spinner');
|
||||
this.ui.monitored.removeAttr('data-idle-icon');
|
||||
|
||||
if (this.model.get('monitored')) {
|
||||
if (monitored) {
|
||||
this.ui.monitored.addClass('icon-nd-monitored');
|
||||
this.ui.monitored.removeClass('icon-nd-unmonitored');
|
||||
}
|
||||
|
@ -127,15 +137,9 @@ define(
|
|||
},
|
||||
|
||||
_refreshSeries: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'refreshSeries',
|
||||
properties: {
|
||||
seriesId: this.model.get('id')
|
||||
},
|
||||
element : this.ui.refresh,
|
||||
leaveIcon : true,
|
||||
context : this,
|
||||
onSuccess : this._showSeasons
|
||||
CommandController.Execute('refreshSeries', {
|
||||
name : 'refreshSeries',
|
||||
seriesId: this.model.id
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -147,27 +151,18 @@ define(
|
|||
},
|
||||
|
||||
_renameSeries: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'renameSeries',
|
||||
properties : {
|
||||
seriesId: this.model.get('id')
|
||||
},
|
||||
element : this.ui.rename,
|
||||
context : this,
|
||||
onSuccess : this._refetchEpisodeFiles,
|
||||
errorMessage: 'Series search failed'
|
||||
|
||||
CommandController.Execute('renameSeries', {
|
||||
name : 'renameSeries',
|
||||
seriesId: this.model.id
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_seriesSearch: function () {
|
||||
Actioneer.ExecuteCommand({
|
||||
command : 'seriesSearch',
|
||||
properties : {
|
||||
seriesId: this.model.get('id')
|
||||
},
|
||||
element : this.ui.search,
|
||||
errorMessage: 'Series search failed',
|
||||
startMessage: 'Search for {0} started'.format(this.model.get('title'))
|
||||
CommandController.Execute('seriesSearch', {
|
||||
name : 'seriesSearch',
|
||||
seriesId: this.model.id
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -187,15 +182,10 @@ define(
|
|||
series : self.model
|
||||
});
|
||||
|
||||
App.reqres.setHandler(App.Reqres.GetEpisodeFileById, function(episodeFileId){
|
||||
App.reqres.setHandler(App.Reqres.GetEpisodeFileById, function (episodeFileId) {
|
||||
return self.episodeFileCollection.get(episodeFileId);
|
||||
});
|
||||
|
||||
/* self.episodeCollection.bindSignalR({
|
||||
onReceived: seasonCollectionView.onEpisodeGrabbed,
|
||||
context : seasonCollectionView
|
||||
});*/
|
||||
|
||||
self.seasons.show(seasonCollectionView);
|
||||
});
|
||||
},
|
||||
|
@ -204,13 +194,9 @@ define(
|
|||
this.info.show(new InfoView({ model: this.model }));
|
||||
},
|
||||
|
||||
_refetchEpisodeFiles: function () {
|
||||
this.episodeFileCollection.fetch();
|
||||
},
|
||||
|
||||
_onSeasonRenamed: function(event) {
|
||||
_onSeasonRenamed: function (event) {
|
||||
if (this.model.get('id') === event.series.get('id')) {
|
||||
this._refetchEpisodeFiles();
|
||||
this.episodeFileCollection.fetch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,14 +2,23 @@
|
|||
<div class="span11">
|
||||
<div class="row">
|
||||
<h1>
|
||||
<i class="icon-bookmark x-monitored clickable" title="Toggle monitored state for entire series"/>
|
||||
<i class="x-monitored clickable series-monitor-toggle" title="Toggle monitored state for entire series"/>
|
||||
{{title}}
|
||||
<span class="series-actions pull-right">
|
||||
<i class="icon-refresh x-refresh" title="Update series info and scan disk"/>
|
||||
<i class="icon-nd-rename x-rename" title="Rename all episodes in this series"/>
|
||||
<i class="icon-search x-search" title="Search for all episodes in this series"/>
|
||||
<i class="icon-nd-edit x-edit" title="Edit series"/>
|
||||
</span>
|
||||
<div class="series-actions pull-right">
|
||||
<div class="x-refresh">
|
||||
<i class="icon-refresh icon-can-spin" title="Update series info and scan disk"/>
|
||||
</div>
|
||||
<div class="x-rename">
|
||||
<i class="icon-nd-rename" title="Rename all episodes in this series"/>
|
||||
</div>
|
||||
<div class="x-search">
|
||||
<i class="icon-search" title="Search for all episodes in this series"/>
|
||||
</div>
|
||||
<div class="x-edit">
|
||||
<i class="icon-nd-edit" title="Edit series"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row series-detail-overview">
|
||||
|
|
|
@ -6,18 +6,18 @@
|
|||
overflow : visible;
|
||||
|
||||
.series-poster {
|
||||
padding-left: 20px;
|
||||
width : 168px;
|
||||
padding-left : 20px;
|
||||
width : 168px;
|
||||
}
|
||||
|
||||
.form-horizontal {
|
||||
margin-top: 10px;
|
||||
margin-top : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-series-modal {
|
||||
.path {
|
||||
margin-left: 30px;
|
||||
margin-left : 30px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
|||
|
||||
.show-hide-episodes {
|
||||
.clickable();
|
||||
text-align: center;
|
||||
text-align : center;
|
||||
|
||||
i {
|
||||
.clickable();
|
||||
|
@ -84,24 +84,24 @@
|
|||
text-align : center;
|
||||
|
||||
.progress {
|
||||
text-align: left;
|
||||
text-align : left;
|
||||
margin-top : 5px;
|
||||
left: 0px;
|
||||
width: 170px;
|
||||
left : 0px;
|
||||
width : 170px;
|
||||
|
||||
.progressbar-front-text {
|
||||
width: 170px;
|
||||
width : 170px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title-container {
|
||||
position: relative;
|
||||
position : relative;
|
||||
|
||||
.title {
|
||||
position : absolute;
|
||||
top : -100px;
|
||||
opacity: 0.0;
|
||||
top : -100px;
|
||||
opacity : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@
|
|||
.episode-detail-modal {
|
||||
|
||||
.episode-info {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
|
||||
.episode-overview {
|
||||
|
@ -221,35 +221,41 @@
|
|||
}
|
||||
|
||||
.season-actions, .series-actions {
|
||||
font-size : 24px;
|
||||
|
||||
div {
|
||||
display : inline-block
|
||||
}
|
||||
|
||||
text-transform : none;
|
||||
|
||||
i {
|
||||
.clickable;
|
||||
font-size : 24px;
|
||||
padding-left : 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.series-stats {
|
||||
font-size: 11px;
|
||||
font-size : 11px;
|
||||
}
|
||||
|
||||
.series-legend {
|
||||
padding-top: 5px;
|
||||
padding-top : 5px;
|
||||
}
|
||||
|
||||
.seasonpass-series {
|
||||
.card;
|
||||
margin : 20px 0px;
|
||||
margin : 20px 0px;
|
||||
|
||||
.title {
|
||||
font-weight: 300;
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
margin-left: 5px;
|
||||
font-weight : 300;
|
||||
font-size : 24px;
|
||||
line-height : 30px;
|
||||
margin-left : 5px;
|
||||
}
|
||||
|
||||
.season-select {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom : 0px;
|
||||
}
|
||||
|
||||
.expander {
|
||||
|
@ -260,11 +266,11 @@
|
|||
}
|
||||
|
||||
.season-grid {
|
||||
margin-top: 10px;
|
||||
margin-top : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.season-status {
|
||||
font-size: 16px;
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
font-size : 16px;
|
||||
vertical-align : middle !important;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,11 @@ define([
|
|||
'Settings/Notifications/Model',
|
||||
'Settings/Notifications/DeleteView',
|
||||
'Shared/Messenger',
|
||||
'Shared/Actioneer',
|
||||
'Commands/CommandController',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Form/FormBuilder'
|
||||
|
||||
], function (App, Marionette, NotificationModel, DeleteView, Messenger, Actioneer, AsModelBoundView) {
|
||||
], function (App, Marionette, NotificationModel, DeleteView, Messenger, CommandController, AsModelBoundView) {
|
||||
|
||||
var model = Marionette.ItemView.extend({
|
||||
template: 'Settings/Notifications/EditTemplate',
|
||||
|
@ -76,16 +76,8 @@ define([
|
|||
properties[field.name] = field.value;
|
||||
});
|
||||
|
||||
Actioneer.ExecuteCommand({
|
||||
command : testCommand,
|
||||
properties : properties,
|
||||
button : this.ui.testButton,
|
||||
element : this.ui.testIcon,
|
||||
errorMessage : 'Failed to test notification settings',
|
||||
successMessage: 'Notification settings tested successfully',
|
||||
always : this._testOnAlways,
|
||||
context : this
|
||||
});
|
||||
|
||||
CommandController.Execute(testCommand, properties);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,198 +0,0 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Commands/CommandController',
|
||||
'Commands/CommandCollection',
|
||||
'Shared/Messenger'],
|
||||
function(CommandController, CommandCollection, Messenger) {
|
||||
|
||||
var actioneer = Marionette.AppRouter.extend({
|
||||
|
||||
initialize: function () {
|
||||
this.trackedCommands = [];
|
||||
CommandCollection.fetch();
|
||||
this.listenTo(CommandCollection, 'sync', this._handleCommands);
|
||||
},
|
||||
|
||||
ExecuteCommand: function (options) {
|
||||
options.iconClass = this._getIconClass(options.element);
|
||||
|
||||
if (options.button) {
|
||||
options.button.addClass('disable');
|
||||
}
|
||||
|
||||
this._setSpinnerOnElement(options);
|
||||
|
||||
var promise = CommandController.Execute(options.command, options.properties);
|
||||
this._showStartMessage(options, promise);
|
||||
},
|
||||
|
||||
SaveModel: function (options) {
|
||||
options.iconClass = this._getIconClass(options.element);
|
||||
|
||||
this._showStartMessage(options);
|
||||
this._setSpinnerOnElement(options);
|
||||
|
||||
var model = options.model ? options.model : options.context.model;
|
||||
|
||||
var promise = model.save();
|
||||
this._handlePromise(promise, options);
|
||||
},
|
||||
|
||||
_handlePromise: function (promise, options) {
|
||||
var self = this;
|
||||
|
||||
promise.done(function () {
|
||||
self._onSuccess(options);
|
||||
});
|
||||
|
||||
promise.fail(function (ajaxOptions) {
|
||||
if (ajaxOptions.readyState === 0 || ajaxOptions.status === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._onError(options);
|
||||
});
|
||||
|
||||
promise.always(function () {
|
||||
self._onComplete(options);
|
||||
});
|
||||
},
|
||||
|
||||
_handleCommands: function () {
|
||||
var self = this;
|
||||
|
||||
_.each(this.trackedCommands, function (trackedCommand){
|
||||
if (trackedCommand.completed === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
var options = trackedCommand.options;
|
||||
var command = CommandCollection.find({ 'id': trackedCommand.id });
|
||||
|
||||
if (!command) {
|
||||
trackedCommand.completed = true;
|
||||
|
||||
self._onError(options, trackedCommand.id);
|
||||
self._onComplete(options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.get('state') === 'completed') {
|
||||
trackedCommand.completed = true;
|
||||
|
||||
self._onSuccess(options, command.get('id'));
|
||||
self._onComplete(options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.get('state') === 'failed') {
|
||||
trackedCommand.completed = true;
|
||||
|
||||
self._onError(options, command.get('id'));
|
||||
self._onComplete(options);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getIconClass: function(element) {
|
||||
if (!element) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return element.attr('class').match(/(?:^|\s)icon\-.+?(?:$|\s)/)[0];
|
||||
},
|
||||
|
||||
_setSpinnerOnElement: function (options) {
|
||||
if (!options.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.leaveIcon) {
|
||||
options.element.addClass('icon-spin');
|
||||
}
|
||||
|
||||
else {
|
||||
options.element.removeClass(options.iconClass);
|
||||
options.element.addClass('icon-nd-spinner');
|
||||
}
|
||||
},
|
||||
|
||||
_onSuccess: function (options, id) {
|
||||
if (options.successMessage) {
|
||||
Messenger.show({
|
||||
id : id,
|
||||
message: options.successMessage,
|
||||
type : 'success'
|
||||
});
|
||||
}
|
||||
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess.call(options.context);
|
||||
}
|
||||
},
|
||||
|
||||
_onError: function (options, id) {
|
||||
if (options.errorMessage) {
|
||||
Messenger.show({
|
||||
id : id,
|
||||
message: options.errorMessage,
|
||||
type : 'error'
|
||||
});
|
||||
}
|
||||
|
||||
if (options.onError) {
|
||||
options.onError.call(options.context);
|
||||
}
|
||||
},
|
||||
|
||||
_onComplete: function (options) {
|
||||
if (options.button) {
|
||||
options.button.removeClass('disable');
|
||||
}
|
||||
|
||||
if (options.element) {
|
||||
if (options.leaveIcon) {
|
||||
options.element.removeClass('icon-spin');
|
||||
}
|
||||
|
||||
else {
|
||||
options.element.addClass(options.iconClass);
|
||||
options.element.removeClass('icon-nd-spinner');
|
||||
options.element.removeClass('icon-spin');
|
||||
}
|
||||
}
|
||||
|
||||
if (options.always) {
|
||||
options.always.call(options.context);
|
||||
}
|
||||
},
|
||||
|
||||
_showStartMessage: function (options, promise) {
|
||||
var self = this;
|
||||
|
||||
if (!promise) {
|
||||
if (options.startMessage) {
|
||||
Messenger.show({
|
||||
message: options.startMessage
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
promise.done(function (data) {
|
||||
self.trackedCommands.push({ id: data.id, options: options });
|
||||
|
||||
if (options.startMessage) {
|
||||
Messenger.show({
|
||||
id : data.id,
|
||||
message: options.startMessage
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return new actioneer();
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette'
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
'use strict';
|
||||
define(function () {
|
||||
return {
|
||||
|
||||
show: function (options) {
|
||||
|
||||
if (!options.type) {
|
||||
options.type = 'info';
|
||||
}
|
||||
|
||||
if (!options.hideAfter) {
|
||||
if (options.hideAfter === undefined) {
|
||||
switch (options.type) {
|
||||
case 'info':
|
||||
options.hideAfter = 5;
|
||||
|
@ -18,7 +19,7 @@ define(function () {
|
|||
break;
|
||||
|
||||
default :
|
||||
options.hideAfter = 0;
|
||||
options.hideAfter = 5;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +32,7 @@ define(function () {
|
|||
});
|
||||
},
|
||||
|
||||
|
||||
monitor: function (options) {
|
||||
|
||||
if (!options.promise) {
|
||||
|
|
51
UI/Shared/SignalRBroadcaster.js
Normal file
51
UI/Shared/SignalRBroadcaster.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'signalR'
|
||||
], function () {
|
||||
return {
|
||||
|
||||
appInitializer: function () {
|
||||
console.log('starting signalR');
|
||||
|
||||
var getStatus = function (status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return 'connecting';
|
||||
case 1:
|
||||
return 'connected';
|
||||
case 2:
|
||||
return 'reconnecting';
|
||||
case 4:
|
||||
return 'disconnected';
|
||||
default:
|
||||
throw 'invalid status ' + status;
|
||||
}
|
||||
};
|
||||
|
||||
this.signalRconnection = $.connection("/signalr");
|
||||
|
||||
this.signalRconnection.stateChanged(function (change) {
|
||||
console.debug('SignalR: [{0}]'.format(getStatus(change.newState)));
|
||||
});
|
||||
|
||||
this.signalRconnection.received(function (message) {
|
||||
require(
|
||||
[
|
||||
'app'
|
||||
], function (app) {
|
||||
app.vent.trigger('server:' + message.name, message.body);
|
||||
})
|
||||
});
|
||||
|
||||
this.signalRconnection.start({ transport:
|
||||
[
|
||||
'longPolling'
|
||||
] });
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -3,9 +3,8 @@ define(
|
|||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Shared/Actioneer',
|
||||
'Shared/Messenger'
|
||||
], function (App, Marionette, Actioneer, Messenger) {
|
||||
'Commands/CommandController'
|
||||
], function (App, Marionette, CommandController) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template : 'Shared/Toolbar/ButtonTemplate',
|
||||
|
@ -15,13 +14,8 @@ define(
|
|||
'click': 'onClick'
|
||||
},
|
||||
|
||||
ui: {
|
||||
icon: '.x-icon'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.storageKey = this.model.get('menuKey') + ':' + this.model.get('key');
|
||||
this.idle = true;
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
|
@ -30,33 +24,34 @@ define(
|
|||
this.invokeCallback();
|
||||
}
|
||||
|
||||
if(!this.model.get('title')){
|
||||
if (!this.model.get('title')) {
|
||||
this.$el.addClass('btn-icon-only');
|
||||
}
|
||||
|
||||
var command = this.model.get('command');
|
||||
if (command) {
|
||||
CommandController.bindToCommand({
|
||||
command: {name: command},
|
||||
element: this.$el
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function () {
|
||||
if (this.idle) {
|
||||
this.invokeCallback();
|
||||
this.invokeRoute();
|
||||
this.invokeCommand();
|
||||
|
||||
if (this.$el.hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.invokeCallback();
|
||||
this.invokeRoute();
|
||||
this.invokeCommand();
|
||||
},
|
||||
|
||||
invokeCommand: function () {
|
||||
var command = this.model.get('command');
|
||||
if (command) {
|
||||
this.idle = false;
|
||||
|
||||
Actioneer.ExecuteCommand({
|
||||
command : command,
|
||||
button : this.$el,
|
||||
element : this.ui.icon,
|
||||
errorMessage : this.model.get('errorMessage'),
|
||||
successMessage: this.model.get('successMessage'),
|
||||
always : this._commandAlways,
|
||||
context : this
|
||||
});
|
||||
CommandController.Execute(command);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -83,12 +78,6 @@ define(
|
|||
if (callback) {
|
||||
callback.call(this.model.ownerContext);
|
||||
}
|
||||
},
|
||||
|
||||
_commandAlways: function () {
|
||||
if (!this.isClosed) {
|
||||
this.idle = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
'use strict';
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
|
@ -6,15 +6,13 @@ define(
|
|||
'System/StatusModel',
|
||||
'System/About/View',
|
||||
'Logs/Layout',
|
||||
'Shared/Toolbar/ToolbarLayout',
|
||||
'Shared/LoadingView'
|
||||
'Shared/Toolbar/ToolbarLayout'
|
||||
], function (App,
|
||||
Marionette,
|
||||
StatusModel,
|
||||
AboutView,
|
||||
LogsLayout,
|
||||
ToolbarLayout,
|
||||
LoadingView) {
|
||||
ToolbarLayout) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'System/LayoutTemplate',
|
||||
|
||||
|
@ -38,21 +36,7 @@ define(
|
|||
title : 'Check for Update',
|
||||
icon : 'icon-nd-update',
|
||||
command: 'applicationUpdate'
|
||||
},
|
||||
// {
|
||||
// title : 'Restart',
|
||||
// icon : 'icon-repeat',
|
||||
// command : 'restart',
|
||||
// successMessage: 'NzbDrone restart has been triggered',
|
||||
// errorMessage : 'Failed to restart NzbDrone'
|
||||
// },
|
||||
// {
|
||||
// title : 'Shutdown',
|
||||
// icon : 'icon-power-off',
|
||||
// command : 'shutdown',
|
||||
// successMessage: 'NzbDrone shutdown has started',
|
||||
// errorMessage : 'Failed to shutdown NzbDrone'
|
||||
// }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
|
|
@ -181,8 +181,9 @@ require.config({
|
|||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Shared/SignalRBroadcaster',
|
||||
'Instrumentation/StringFormat'
|
||||
], function (Marionette) {
|
||||
], function (Marionette, SignalRBroadcaster) {
|
||||
|
||||
var app = new Marionette.Application();
|
||||
|
||||
|
@ -210,6 +211,8 @@ define(
|
|||
console.log('starting application');
|
||||
});
|
||||
|
||||
app.addInitializer(SignalRBroadcaster.appInitializer, {app: app});
|
||||
|
||||
app.addRegions({
|
||||
navbarRegion: '#nav-region',
|
||||
mainRegion : '#main-region',
|
||||
|
|
58
UI/jQuery/jquery.spin.js
Normal file
58
UI/jQuery/jquery.spin.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
define(
|
||||
[
|
||||
'jquery'
|
||||
], function ($) {
|
||||
'use strict';
|
||||
|
||||
$.fn.spinForPromise = function (promise) {
|
||||
var self = this;
|
||||
|
||||
if (!promise || promise.state() !== 'pending') {
|
||||
return this;
|
||||
}
|
||||
promise.always(function () {
|
||||
self.stopSpin();
|
||||
});
|
||||
|
||||
return this.startSpin();
|
||||
};
|
||||
|
||||
$.fn.startSpin = function () {
|
||||
|
||||
var icon = this.find('i').andSelf('i');
|
||||
|
||||
var iconClasses = icon.attr('class').match(/(?:^|\s)icon\-.+?(?:$|\s)/);
|
||||
|
||||
if (iconClasses.length === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var iconClass = $.trim(iconClasses[0]);
|
||||
|
||||
this.addClass('disabled');
|
||||
|
||||
if (icon.hasClass('icon-can-spin')) {
|
||||
icon.addClass('icon-spin');
|
||||
}
|
||||
else {
|
||||
icon.attr('data-idle-icon', iconClass);
|
||||
icon.removeClass(iconClass);
|
||||
icon.addClass('icon-nd-spinner');
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
$.fn.stopSpin = function () {
|
||||
var icon = this.find('i').andSelf('i');
|
||||
|
||||
this.removeClass('disabled');
|
||||
icon.removeClass('icon-spin icon-nd-spinner');
|
||||
var idleIcon = icon.attr('data-idle-icon');
|
||||
|
||||
if (idleIcon) {
|
||||
icon.addClass(idleIcon);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue