: Replaced _.clone with _.deepClone
+ this.changed = {};
+ }
+ current = this.attributes, prev = this._previousAttributes;
+
+ // Check for changes of `id`.
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
+
+ //
+ attrs = objToPaths(attrs);
+ //
+
+ // For each `set` attribute, update or delete the current value.
+ for (attr in attrs) {
+ val = attrs[attr];
+
+ //: Using getNested, setNested and deleteNested
+ if (!_.isEqual(getNested(current, attr), val)) changes.push(attr);
+ if (!_.isEqual(getNested(prev, attr), val)) {
+ setNested(this.changed, attr, val);
+ } else {
+ deleteNested(this.changed, attr);
+ }
+ unset ? deleteNested(current, attr) : setNested(current, attr, val);
+ //
+ }
+
+ // Trigger all relevant attribute changes.
+ if (!silent) {
+ if (changes.length) this._pending = true;
+
+ //
+ var separator = DeepModel.keyPathSeparator;
+
+ for (var i = 0, l = changes.length; i < l; i++) {
+ var key = changes[i];
+
+ this.trigger('change:' + key, this, getNested(current, key), options);
+
+ var fields = key.split(separator);
+
+ //Trigger change events for parent keys with wildcard (*) notation
+ for(var n = fields.length - 1; n > 0; n--) {
+ var parentKey = _.first(fields, n).join(separator),
+ wildcardKey = parentKey + separator + '*';
+
+ this.trigger('change:' + wildcardKey, this, getNested(current, parentKey), options);
+ }
+ //
+ }
+ }
+
+ if (changing) return this;
+ if (!silent) {
+ while (this._pending) {
+ this._pending = false;
+ this.trigger('change', this, options);
+ }
+ }
+ this._pending = false;
+ this._changing = false;
+ return this;
+ },
+
+ // Clear all attributes on the model, firing `"change"` unless you choose
+ // to silence it.
+ clear: function(options) {
+ var attrs = {};
+ var shallowAttributes = objToPaths(this.attributes);
+ for (var key in shallowAttributes) attrs[key] = void 0;
+ return this.set(attrs, _.extend({}, options, {unset: true}));
+ },
+
+ // Determine if the model has changed since the last `"change"` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged: function(attr) {
+ if (attr == null) return !_.isEmpty(this.changed);
+ return getNested(this.changed, attr) !== undefined;
+ },
+
+ // Return an object containing all the attributes that have changed, or
+ // false if there are no changed attributes. Useful for determining what
+ // parts of a view need to be updated and/or what attributes need to be
+ // persisted to the server. Unset attributes will be set to undefined.
+ // You can also pass an attributes object to diff against the model,
+ // determining if there *would be* a change.
+ changedAttributes: function(diff) {
+ //: objToPaths
+ if (!diff) return this.hasChanged() ? objToPaths(this.changed) : false;
+ //
+
+ var old = this._changing ? this._previousAttributes : this.attributes;
+
+ //
+ diff = objToPaths(diff);
+ old = objToPaths(old);
+ //
+
+ var val, changed = false;
+ for (var attr in diff) {
+ if (_.isEqual(old[attr], (val = diff[attr]))) continue;
+ (changed || (changed = {}))[attr] = val;
+ }
+ return changed;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous: function(attr) {
+ if (attr == null || !this._previousAttributes) return null;
+
+ //
+ return getNested(this._previousAttributes, attr);
+ //
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes: function() {
+ //
+ return _.deepClone(this._previousAttributes);
+ //
+ }
+ });
+
+
+ //Config; override in your app to customise
+ DeepModel.keyPathSeparator = '.';
+
+
+ //Exports
+ Backbone.DeepModel = DeepModel;
+
+ //For use in NodeJS
+ if (typeof module != 'undefined') module.exports = DeepModel;
+
+ return Backbone;
+
+}));
+
diff --git a/UI/Mixins/backbone.marionette.templates.js b/UI/Mixins/backbone.marionette.templates.js
index 876cbd58b..48342d1a2 100644
--- a/UI/Mixins/backbone.marionette.templates.js
+++ b/UI/Mixins/backbone.marionette.templates.js
@@ -4,7 +4,7 @@ Marionette.TemplateCache.get = function (templateId) {
var templateKey = templateId.toLowerCase();
- var templateFunction = window.Templates[templateKey.toLowerCase()];
+ var templateFunction = window.Templates[templateKey];
if (!templateFunction) {
throw 'couldn\'t find pre-compiled template ' + templateKey;
diff --git a/UI/Mixins/handlebars.mixin.js b/UI/Mixins/handlebars.mixin.js
new file mode 100644
index 000000000..322a8f8cc
--- /dev/null
+++ b/UI/Mixins/handlebars.mixin.js
@@ -0,0 +1,20 @@
+'use strict';
+
+Handlebars.registerHelper('partial', function(templateName, context){
+ //TODO: We should be able to pass in the context, either an object or a property
+
+ var templateFunction = Marionette.TemplateCache.get(templateName);
+ return new Handlebars.SafeString(templateFunction(this));
+});
+
+Handlebars.registerHelper("debug", function(optionalValue) {
+ console.log("Current Context");
+ console.log("====================");
+ console.log(this);
+
+ if (optionalValue) {
+ console.log("Value");
+ console.log("====================");
+ console.log(optionalValue);
+ }
+});
\ No newline at end of file
diff --git a/UI/Settings/Indexers/Collection.js b/UI/Settings/Indexers/Collection.js
new file mode 100644
index 000000000..fec1cead2
--- /dev/null
+++ b/UI/Settings/Indexers/Collection.js
@@ -0,0 +1,6 @@
+define(['app', 'Settings/Indexers/Model'], function () {
+ NzbDrone.Settings.Indexers.Collection = Backbone.Collection.extend({
+ url : NzbDrone.Constants.ApiRoot + '/indexer',
+ model: NzbDrone.Settings.Indexers.Model
+ });
+});
\ No newline at end of file
diff --git a/UI/Settings/Indexers/CollectionTemplate.html b/UI/Settings/Indexers/CollectionTemplate.html
new file mode 100644
index 000000000..c42cbaa79
--- /dev/null
+++ b/UI/Settings/Indexers/CollectionTemplate.html
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/UI/Settings/Indexers/CollectionView.js b/UI/Settings/Indexers/CollectionView.js
new file mode 100644
index 000000000..51023fc28
--- /dev/null
+++ b/UI/Settings/Indexers/CollectionView.js
@@ -0,0 +1,10 @@
+'use strict';
+
+define(['app', 'Settings/Indexers/ItemView'], function (app) {
+
+ NzbDrone.Settings.Indexers.CollectionView = Backbone.Marionette.CompositeView.extend({
+ itemView : NzbDrone.Settings.Indexers.ItemView,
+ itemViewContainer : '#x-indexers',
+ template : 'Settings/Indexers/CollectionTemplate'
+ });
+});
\ No newline at end of file
diff --git a/UI/Settings/Indexers/IndexersTemplate.html b/UI/Settings/Indexers/IndexersTemplate.html
deleted file mode 100644
index 70e312099..000000000
--- a/UI/Settings/Indexers/IndexersTemplate.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Indexer settings will go here
-
\ No newline at end of file
diff --git a/UI/Settings/Indexers/IndexersView.js b/UI/Settings/Indexers/IndexersView.js
deleted file mode 100644
index 2592b802e..000000000
--- a/UI/Settings/Indexers/IndexersView.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-define([
- 'app', 'Settings/SettingsModel'
-
-], function () {
-
- NzbDrone.Settings.Indexers.IndexersView = Backbone.Marionette.ItemView.extend({
- template: 'Settings/Indexers/IndexersTemplate'
- });
-});
diff --git a/UI/Settings/Indexers/ItemTemplate.html b/UI/Settings/Indexers/ItemTemplate.html
new file mode 100644
index 000000000..0a1536002
--- /dev/null
+++ b/UI/Settings/Indexers/ItemTemplate.html
@@ -0,0 +1,38 @@
+
\ No newline at end of file
diff --git a/UI/Settings/Indexers/ItemView.js b/UI/Settings/Indexers/ItemView.js
new file mode 100644
index 000000000..24d9feae5
--- /dev/null
+++ b/UI/Settings/Indexers/ItemView.js
@@ -0,0 +1,38 @@
+"use strict";
+
+define([
+ 'app',
+ 'Settings/Indexers/Collection'
+
+], function () {
+
+ NzbDrone.Settings.Indexers.ItemView = Backbone.Marionette.ItemView.extend({
+ template: 'Settings/Indexers/ItemTemplate',
+ initialize: function () {
+ this.model.set('fields', [
+ { key: 'username', title: 'Username', helpText: 'HALP!', value: 'mark' },
+ { key: 'apiKey', title: 'API Key', helpText: 'HALP!', value: 'private' }
+ ]);
+
+ NzbDrone.vent.on(NzbDrone.Commands.SaveSettings, this.saveSettings, this);
+ },
+
+ saveSettings: function () {
+ var test = 1;
+ //this.model.save(undefined, this.syncNotification("Naming Settings Saved", "Couldn't Save Naming Settings"));
+ },
+
+
+ syncNotification: function (success, error) {
+ return {
+ success: function () {
+ window.alert(success);
+ },
+
+ error: function () {
+ window.alert(error);
+ }
+ };
+ }
+ });
+});
diff --git a/UI/Settings/Indexers/Model.js b/UI/Settings/Indexers/Model.js
new file mode 100644
index 000000000..f1cf3792a
--- /dev/null
+++ b/UI/Settings/Indexers/Model.js
@@ -0,0 +1,13 @@
+"use strict";
+define(['app'], function (app) {
+ NzbDrone.Settings.Indexers.Model = Backbone.DeepModel.extend({
+ mutators: {
+ fields: function () {
+ return [
+ { key: 'username', title: 'Username', helpText: 'HALP!', value: 'mark', index: 0 },
+ { key: 'apiKey', title: 'API Key', helpText: 'HALP!', value: '', index: 1 }
+ ];
+ }
+ }
+ });
+});
diff --git a/UI/Settings/SettingsLayout.js b/UI/Settings/SettingsLayout.js
index fcfca04f1..4a3e6da9f 100644
--- a/UI/Settings/SettingsLayout.js
+++ b/UI/Settings/SettingsLayout.js
@@ -3,7 +3,7 @@ define([
'app',
'Settings/Naming/NamingView',
'Settings/Quality/QualityLayout',
- 'Settings/Indexers/IndexersView',
+ 'Settings/Indexers/CollectionView',
'Settings/DownloadClient/DownloadClientView',
'Settings/Notifications/NotificationsView',
'Settings/System/SystemView',
@@ -112,6 +112,9 @@ define([
this.namingSettings = new NzbDrone.Settings.Naming.NamingModel();
this.namingSettings.fetch();
+ this.indexerSettings = new NzbDrone.Settings.Indexers.Collection();
+ this.indexerSettings.fetch();
+
if (options.action) {
this.action = options.action.toLowerCase();
}
@@ -120,7 +123,7 @@ define([
onRender: function () {
this.naming.show(new NzbDrone.Settings.Naming.NamingView());
this.quality.show(new NzbDrone.Settings.Quality.QualityLayout({settings: this.settings}));
- this.indexers.show(new NzbDrone.Settings.Indexers.IndexersView({model: this.settings}));
+ this.indexers.show(new NzbDrone.Settings.Indexers.CollectionView({collection: this.indexerSettings}));
this.downloadClient.show(new NzbDrone.Settings.DownloadClient.DownloadClientView({model: this.settings}));
this.notifications.show(new NzbDrone.Settings.Notifications.NotificationsView({model: this.settings}));
this.system.show(new NzbDrone.Settings.System.SystemView({model: this.settings}));