removed backbone from VS solution,

renamed NzbDrone.Backbone to UI
This commit is contained in:
kay.one 2013-03-29 12:18:44 -07:00
commit 663160c06a
230 changed files with 57 additions and 386 deletions

View file

@ -0,0 +1,109 @@
"use strict";
define([
'app',
'AddSeries/RootFolders/RootFolderCollection',
'Quality/QualityProfileCollection',
'AddSeries/RootFolders/RootFolderView',
'AddSeries/New/AddNewSeriesView',
'AddSeries/Existing/ImportSeriesView'
],
function (app, rootFolderCollection, qualityProfileCollection) {
NzbDrone.AddSeries.AddSeriesLayout = Backbone.Marionette.Layout.extend({
template: 'AddSeries/addSeriesLayoutTemplate',
regions: {
addNew: '#add-new',
importExisting: '#import-existing',
rootFolders: '#root-folders'
},
ui: {
addNewTab: '.x-add-new-tab',
importExistingTab: '.x-import-existing-tab',
rootFoldersTab: '.x-root-folders-tab'
},
events: {
'click .x-add-new-tab': 'showAddNew',
'click .x-import-existing-tab': 'showImport',
'click .x-root-folders-tab': 'showRootFolders'
},
showAddNew: function (e) {
if (e) {
e.preventDefault();
}
this.ui.addNewTab.tab('show');
NzbDrone.Router.navigate('series/add/new');
},
showImport: function (e) {
if (e) {
e.preventDefault();
}
this.ui.importExistingTab.tab('show');
NzbDrone.Router.navigate('series/add/import');
},
showRootFolders: function (e) {
if (e) {
e.preventDefault();
}
this.ui.rootFoldersTab.tab('show');
NzbDrone.Router.navigate('series/add/rootfolders');
},
initialize: function (context, action, query) {
if (action) {
this.action = action.toLowerCase();
}
if (query) {
this.query = query.toLowerCase();
}
},
onRender: function () {
rootFolderCollection.fetch();
qualityProfileCollection.fetch();
this.addNew.show(new NzbDrone.AddSeries.New.AddNewSeriesView());
this.importExisting.show(new NzbDrone.AddSeries.Existing.ImportSeriesView());
this.rootFolders.show(new NzbDrone.AddSeries.RootDirView());
this.listenTo(rootFolderCollection, 'add', this.evaluateActions, this);
this.listenTo(rootFolderCollection, 'remove', this.evaluateActions, this);
this.listenTo(rootFolderCollection, 'reset', this.evaluateActions, this);
},
onShow: function () {
switch (this.action) {
case 'import':
this.showImport();
break;
case 'rootfolders':
this.showRootFolders();
break;
default:
this.showAddNew();
}
},
evaluateActions: function () {
if (rootFolderCollection.length === 0) {
this.ui.addNewTab.hide();
this.ui.importExistingTab.hide();
this.showRootFolders();
} else {
this.ui.addNewTab.show();
this.ui.importExistingTab.show();
}
}
});
});

View file

@ -0,0 +1,9 @@
<div class="line row folder-match-result-view">
<div class="span6">
{{title}} {{seriesYear}}
</div>
<div class="btn btn-success x-btn-add">
<icon class="icon-plus "></icon>
</div>
</div>

View file

@ -0,0 +1,134 @@
'use strict';
define([
'app', 'AddSeries/RootFolders/RootFolderCollection', 'Quality/QualityProfileCollection', 'Shared/NotificationCollection', 'AddSeries/Existing/UnmappedFolderModel', 'AddSeries/SearchResultCollection', 'Series/SeriesModel'], function (app, rootFolders, qualityProfileCollection, notificationCollection) {
NzbDrone.AddSeries.Existing.FolderMatchResultView = Backbone.Marionette.ItemView.extend({
template: 'AddSeries/Existing/FolderMatchResultViewTemplatate',
events: {
'click .x-btn-add': 'addSeries'
},
addSeries: function () {
var self = this;
var seriesId = this.model.get('tvDbId');
var title = this.model.get('title');
var quality = this.options.qualityProfile.val();
var path = this.options.folder.path;
var model = new NzbDrone.Series.SeriesModel({
tvDbId : seriesId,
title : title,
qualityProfileId: quality,
path : path
});
var seriesCollection = new NzbDrone.Series.SeriesCollection();
seriesCollection.add(model);
model.save(undefined, {
success: function () {
var notificationModel = new NzbDrone.Shared.NotificationModel({
tvDbId : seriesId,
title : 'Added',
message: title,
level : 'success'
});
notificationCollection.push(notificationModel);
self.close();
}
});
}
});
NzbDrone.AddSeries.Existing.UnmappedFolderCompositeView = Backbone.Marionette.CompositeView.extend({
template : 'AddSeries/Existing/UnmappedFolderCompositeViewTemplatate',
itemViewContainer: '.x-folder-name-match-results',
itemView : NzbDrone.AddSeries.Existing.FolderMatchResultView,
events: {
'click .x-btn-search': 'search'
},
ui: {
searchButton: '.x-btn-search',
searchText : '.x-txt-search',
profileList : '.x-lst-quality-profile'
},
initialize: function () {
this.collection = new NzbDrone.AddSeries.SearchResultCollection();
},
search: function () {
var icon = this.ui.searchButton.find('icon');
icon.removeClass('icon-search').addClass('icon-spin icon-spinner disabled');
this.collection.fetch({
data : { term: this.ui.searchText.val() },
success: function () {
icon.removeClass('icon-spin icon-spinner disabled').addClass('icon-search');
},
fail : function () {
icon.removeClass('icon-spin icon-spinner disabled').addClass('icon-search');
}
});
},
itemViewOptions: function () {
return {
qualityProfile: this.ui.profileList,
rootFolder : this.model.get('rootFolder'),
folder : this.model.get('folder')
};
}
});
NzbDrone.AddSeries.Existing.RootFolderCompositeView = Backbone.Marionette.CompositeView.extend({
template : "AddSeries/Existing/RootFolderCompositeViewTemplate",
itemViewContainer: ".x-existing-folder-container",
itemView : NzbDrone.AddSeries.Existing.UnmappedFolderCompositeView,
initialize: function () {
if (!this.model) {
throw "model is required.";
}
this.collection = new NzbDrone.AddSeries.Existing.UnmappedFolderCollection();
this.refreshItems();
this.listenTo(qualityProfileCollection, 'reset', this.refreshItems, this);
},
refreshItems: function () {
this.collection.importItems(this.model);
}
});
NzbDrone.AddSeries.Existing.ImportSeriesView = Backbone.Marionette.CollectionView.extend({
itemView: NzbDrone.AddSeries.Existing.RootFolderCompositeView,
initialize: function () {
this.collection = rootFolders;
}
});
});

View file

@ -0,0 +1,6 @@
<div class="row">
<div class="accordion result-list span12 existing-root-folder-view">
<h1>{{path}}</h1>
<div class="x-existing-folder-container" />
</div>
</div>

View file

@ -0,0 +1,19 @@
<div class="row unmapped-folder-view">
<div class="span11">
<div class="row folder-header">
<input class="x-txt-search input-xlarge" type="text" value="{{folder.name}}" placeholder="{{folder.name}}"></input>
<select class="span2 x-lst-quality-profile">
{{#each quality.models}}
<option value="{{id}}">{{attributes.name}}</option>
{{/each}}
</select>
<div class="btn btn-primary x-btn-search pull-right">
<icon class="icon-search "></icon>
</div>
<div class="row">
<div class="x-folder-name-match-results folder-name-matches span11" />
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,26 @@
'use strict';
define(['app', 'Quality/QualityProfileCollection'], function (app, qualityProfiles) {
NzbDrone.AddSeries.Existing.UnmappedFolderModel = Backbone.Model.extend({
defaults: {
quality: qualityProfiles
}
});
NzbDrone.AddSeries.Existing.UnmappedFolderCollection = Backbone.Collection.extend({
model: NzbDrone.AddSeries.Existing.UnmappedFolderModel,
importItems: function (rootFolderModel) {
this.reset();
var rootFolder = rootFolderModel.get('path');
_.each(rootFolderModel.get('unmappedFolders'), function (folder) {
this.push(new NzbDrone.AddSeries.Existing.UnmappedFolderModel({ rootFolder: rootFolder, folder: folder}));
}, this);
}
});
});

View file

@ -0,0 +1,11 @@
<div class="tab-pane" id="add-new">
<div class="row">
<div class="input-prepend nz-input-large search span11">
<i class="add-on icon-search"></i>
<input type="text" class="input-block-level" placeholder="Start typing the name of series you want to add ...">
</div>
</div>
<div class="row">
<div id="search-result" class="result-list span12" />
</div>
</div>

View file

@ -0,0 +1,60 @@
"use strict";
define(['app', 'AddSeries/RootFolders/RootFolderCollection', 'AddSeries/New/SearchResultView', 'Shared/SpinnerView'], function () {
NzbDrone.AddSeries.New.AddNewSeriesView = Backbone.Marionette.Layout.extend({
template: 'AddSeries/New/AddNewSeriesTemplate',
route: 'Series/add/new',
ui: {
seriesSearch: '.search input'
},
regions: {
searchResult: '#search-result'
},
collection: new NzbDrone.AddSeries.SearchResultCollection(),
onRender: function () {
console.log('binding auto complete');
var self = this;
this.ui.seriesSearch
.data('timeout', null)
.keyup(function () {
window.clearTimeout(self.$el.data('timeout'));
self.$el.data('timeout', window.setTimeout(self.search, 500, self));
});
this.resultView = new NzbDrone.AddSeries.SearchResultView({ collection: this.collection });
},
search: function (context) {
context.abortExistingRequest();
var term = context.ui.seriesSearch.val();
context.collection.reset();
if (term === '') {
context.searchResult.close();
} else {
context.searchResult.show(new NzbDrone.Shared.SpinnerView());
context.currentSearchRequest = context.collection.fetch({
data: { term: term },
success: function () {
context.searchResult.show(context.resultView);
}
});
}
},
abortExistingRequest: function () {
if (this.currentSearchRequest && this.currentSearchRequest.readyState > 0 && this.currentSearchRequest.readyState < 4) {
console.log('aborting previous pending search request.');
this.currentSearchRequest.abort();
}
}
});
});

View file

@ -0,0 +1,22 @@
<div class="accordion-group">
<div class="accordion-heading">
<a href="http://thetvdb.com/?tab=series&id={{tvDbId}}" target="_blank" class="icon-info-sign pull-left"></a>
<a class="accordion-toggle" data-toggle="collapse" href="#{{tvDbId}}">{{title}} {{seriesYear}}</a>
</div>
<div id="{{tvDbId}}" class="accordion-body collapse">
<div class="accordion-inner">
<select class="span7 x-root-folder">
{{#each rootFolders.models}}
<option value="{{id}}">{{attributes.path}}</option>
{{/each}}
</select>
<select class="span2 x-quality-profile">
{{#each qualityProfiles.models}}
<option value="{{id}}">{{attributes.name}}</option>
{{/each}}
</select>
<div class="btn btn-success pull-right icon-plus x-add">
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,70 @@
'use strict';
define(['app', 'Shared/NotificationCollection', 'AddSeries/SearchResultCollection', 'AddSeries/SearchResultModel', 'Series/SeriesCollection'], function (app, notificationCollection) {
NzbDrone.AddSeries.New.SearchItemView = Backbone.Marionette.ItemView.extend({
template: "AddSeries/New/SearchResultTemplate",
className: 'search-item',
ui: {
qualityProfile: '.x-quality-profile',
rootFolder: '.x-root-folder',
addButton: '.x-add'
},
events: {
'click .x-add': 'add'
},
onRender: function () {
this.listenTo(this.model, 'change', this.render);
},
add: function () {
var seriesId = this.model.get('tvDbId');
var title = this.model.get('title');
var quality = this.ui.qualityProfile.val();
var rootFolderId = this.ui.rootFolder.val();
//Todo: This will create an invalid path on linux...
var rootPath = this.model.get('rootFolders').get(rootFolderId).get('path');
var path = rootPath + "\\" + title;
var model = new NzbDrone.Series.SeriesModel({
tvdbId: seriesId,
title: title,
qualityProfileId: quality,
path: path
});
var self = this;
var seriesCollection = new NzbDrone.Series.SeriesCollection();
seriesCollection.push(model);
model.save(undefined, {
success: function () {
var notificationModel = new NzbDrone.Shared.NotificationModel({
title: 'Added',
message: title,
level: 'success'
});
notificationCollection.push(notificationModel);
self.close();
}
});
}
});
NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend({
itemView: NzbDrone.AddSeries.New.SearchItemView,
className: 'accordion',
initialize: function () {
this.listenTo(this.collection, 'reset', this.render);
}
});
});

View file

@ -0,0 +1,10 @@
"use strict";
define(['app', 'AddSeries/RootFolders/RootFolderModel'], function () {
var rootFolderCollection = Backbone.Collection.extend({
url: NzbDrone.Constants.ApiRoot + '/rootfolder',
model: NzbDrone.AddSeries.RootFolders.RootFolderModel
});
return new rootFolderCollection();
});

View file

@ -0,0 +1,8 @@
<td name="path" class="span10"/>
<td class="span3">
<span name="freeSpaceString"></span>
</td>
<td class="span1 nz-row-action">
<div class="btn btn-danger icon-minus x-remove">
</div>
</td>

View file

@ -0,0 +1,14 @@
"use strict";
define(['app'], function () {
NzbDrone.AddSeries.RootFolders.RootFolderModel = Backbone.Model.extend({
mutators: {
freeSpaceString: function () {
return this.get('freeSpace').bytes(2) + " Free";
}
},
defaults: {
freeSpace: 0,
}
});
});

View file

@ -0,0 +1,8 @@
<div class="tab-pane row" id="root-dir">
<div class="input-prepend input-append nz-input-large x-path span10">
<i class="add-on icon-folder-open"></i>
<input class="input-block-level" type="text" placeholder="Path of the folder to add ...">
<div class="btn icon-plus btn-success x-add" />
</div>
<div class="span12 result-list" id="current-dirs" />
</div>

View file

@ -0,0 +1,87 @@
"use strict";
define(['app', 'AddSeries/RootFolders/RootFolderCollection', 'Mixins/AutoComplete'], function (app,rootFolders) {
NzbDrone.AddSeries.RootFolderItemView = Backbone.Marionette.ItemView.extend({
template: 'AddSeries/RootFolders/RootFolderItemTemplate',
tagName: 'tr',
events: {
'click .x-remove': 'removeFolder'
},
onRender: function () {
NzbDrone.ModelBinder.bind(this.model, this.el);
},
removeFolder: function () {
this.model.destroy({ wait: true });
this.model.collection.remove(this.model);
}
});
NzbDrone.AddSeries.RootDirListView = Backbone.Marionette.CollectionView.extend({
itemView: NzbDrone.AddSeries.RootFolderItemView,
tagName: 'table',
className: 'table table-hover'
});
NzbDrone.AddSeries.RootDirView = Backbone.Marionette.Layout.extend({
template: 'AddSeries/RootFolders/RootFolderTemplate',
route: 'series/add/rootdir',
ui: {
pathInput: '.x-path input'
},
regions: {
currentDirs: '#current-dirs'
},
events: {
'click .x-add': 'addFolder'
},
onRender: function () {
this.collection = rootFolders;
this.currentDirs.show(new NzbDrone.AddSeries.RootDirListView({ collection: this.collection }));
this.ui.pathInput.autoComplete('/directories');
},
addFolder: function () {
var newDir = new NzbDrone.AddSeries.RootFolders.RootFolderModel(
{
Path: this.ui.pathInput.val()
});
var self = this;
this.collection.create(newDir, {
wait: true, success: function () {
self.collection.fetch();
}
});
},
search: function (context) {
var term = context.ui.seriesSearch.val();
if (term === "") {
context.collection.reset();
} else {
console.log(term);
context.collection.fetch({ data: { term: term } });
}
}
});
});

View file

@ -0,0 +1,10 @@
"use strict";
define(['app', 'AddSeries/SearchResultModel'], function () {
NzbDrone.AddSeries.SearchResultCollection = Backbone.Collection.extend({
url: NzbDrone.Constants.ApiRoot + '/series/lookup',
model: NzbDrone.AddSeries.SearchResultModel
});
});

View file

@ -0,0 +1,25 @@
define(['app', 'AddSeries/RootFolders/RootFolderCollection', 'Quality/QualityProfileCollection'],
function (app, rootFolderCollection, qualityProfileCollection) {
NzbDrone.AddSeries.SearchResultModel = Backbone.Model.extend({
mutators: {
seriesYear: function () {
var date = Date.utc.create(this.get('firstAired')).format('({yyyy})');
//don't append year, if the series name already has the name appended.
if (this.get('title').endsWith(date)) {
return "";
} else {
return date;
}
}
},
defaults: {
qualityProfiles: qualityProfileCollection,
rootFolders: rootFolderCollection
}
});
});

View file

@ -0,0 +1,81 @@
.nz-input-large {
margin-top: 20px;
margin-bottom: 40px;
}
.nz-input-large input {
height: 50px;
}
.nz-input-large *[class*='icon-'] {
font-size: 28px;
height: 30px;
width: 40px;
padding-top: 14px;
padding-left: 10px;
}
.result-list {
font-size: 18px;
text-align: left;
}
.result-list .accordion-heading:hover {
background-color: #fcf8e3;
}
.search-item .accordion-heading *[class*='icon-'] {
padding-right: 5px;
padding-top: 10px;
padding-left: 10px;
}
.search-item .accordion-body .in {
background-color: #fcf8e3;
}
.result-list .result-item a {
font-size: 20px;
}
.search-item a:hover {
text-decoration: none;
}
.search-item a {
color: #343434;
}
.search-item select {
font-size: 16px;
}
/*Existing*/
.unmapped-folder-view {
background: #fcf8e3;
margin-top: 20px;
padding: 20px;
}
.unmapped-folder-view .folder-header {
font-weight: 300;
padding-left: 20px;
}
.unmapped-folder-view .folder-header input {
margin-bottom: 0;
}
.unmapped-folder-view .folder-header select {
margin-bottom: 0;
font-size: 16px;
}
.existing-root-folder-view h1 {
padding: 10px 0 20px 0;
}
.folder-name-matches {
padding-left: 20px;
padding-top: 10px;
}

View file

@ -0,0 +1,13 @@
<!--<div class="alert alert-info">
<strong>Heads up!</strong> you need to add at least one TV folder.
</div>-->
<ul class="nav nav-tabs" id="myTab">
<li><a href="#add-new" class="x-add-new-tab">Add New Series</a></li>
<li><a href="#import-existing" class="x-import-existing-tab">Import Existing Series</a></li>
<li><a href="#root-folders" class ="x-root-folders-tab">TV Folders</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="add-new">Add new series.</div>
<div class="tab-pane" id="import-existing">Import existing.</div>
<div class="tab-pane" id="root-folders">Manage root folders</div>
</div>