updated backbone, marionette, sugar, underscore, modelbinder, bootstrap.switch

pull/3113/head
Keivan Beigi 11 years ago
parent 99cd327002
commit 549b497770

@ -6,14 +6,14 @@ module.exports = function(grunt) {
curl: { curl: {
'UI/JsLibraries/backbone.collectionbinder.js': 'http://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.CollectionBinder.js', 'UI/JsLibraries/backbone.collectionbinder.js': 'http://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.CollectionBinder.js',
'UI/JsLibraries/backbone.js': 'http://raw.github.com/documentcloud/backbone/master/backbone.js', 'UI/JsLibraries/backbone.js': 'http://backbonejs.org/backbone.js',
'UI/JsLibraries/backbone.marionette.js': 'http://raw.github.com/marionettejs/backbone.marionette/master/lib/backbone.marionette.js', 'UI/JsLibraries/backbone.marionette.js': 'http://marionettejs.com/downloads/backbone.marionette.js',
'UI/JsLibraries/backbone.modelbinder.js': 'http://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.ModelBinder.js', 'UI/JsLibraries/backbone.modelbinder.js': 'http://raw.github.com/theironcook/Backbone.ModelBinder/master/Backbone.ModelBinder.js',
'UI/JsLibraries/backbone.mutators.js': 'http://raw.github.com/asciidisco/Backbone.Mutators/master/backbone.mutators.js', 'UI/JsLibraries/backbone.mutators.js': 'http://raw.github.com/asciidisco/Backbone.Mutators/master/backbone.mutators.js',
'UI/JsLibraries/backbone.shortcuts.js': 'http://raw.github.com/bry4n/backbone-shortcuts/master/backbone.shortcuts.js', 'UI/JsLibraries/backbone.shortcuts.js': 'http://raw.github.com/bry4n/backbone-shortcuts/master/backbone.shortcuts.js',
//'UI/JsLibraries/bootstrap.js': //'UI/JsLibraries/bootstrap.js':
//'UI/JsLibraries/bootstrap.slider.js': //'UI/JsLibraries/bootstrap.slider.js':
'UI/JsLibraries/bootstrap.swtitch.js': 'http://raw.github.com/nostalgiaz/bootstrap-switch/master/static/js/bootstrapSwitch.js', 'UI/JsLibraries/bootstrap.switch.js': 'http://raw.github.com/nostalgiaz/bootstrap-switch/master/static/js/bootstrapSwitch.js',
'UI/JsLibraries/handlebars.runtime.js': 'http://raw.github.com/wycats/handlebars.js/master/dist/handlebars.runtime.js', 'UI/JsLibraries/handlebars.runtime.js': 'http://raw.github.com/wycats/handlebars.js/master/dist/handlebars.runtime.js',
'UI/JsLibraries/jquery.cookie.js': 'http://raw.github.com/carhartl/jquery-cookie/master/jquery.cookie.js', 'UI/JsLibraries/jquery.cookie.js': 'http://raw.github.com/carhartl/jquery-cookie/master/jquery.cookie.js',
'UI/JsLibraries/jquery.js': 'http://code.jquery.com/jquery.js', 'UI/JsLibraries/jquery.js': 'http://code.jquery.com/jquery.js',
@ -21,7 +21,7 @@ module.exports = function(grunt) {
//'NzbDrone.Backbone/JsLibraries/jquery.tablesorter.js': //'NzbDrone.Backbone/JsLibraries/jquery.tablesorter.js':
'UI/JsLibraries/require.js': 'http://raw.github.com/jrburke/requirejs/master/require.js', 'UI/JsLibraries/require.js': 'http://raw.github.com/jrburke/requirejs/master/require.js',
'UI/JsLibraries/sugar.js': 'http://raw.github.com/andrewplummer/Sugar/master/release/sugar-full.development.js', 'UI/JsLibraries/sugar.js': 'http://raw.github.com/andrewplummer/Sugar/master/release/sugar-full.development.js',
'UI/JsLibraries/underscore.js': 'http://raw.github.com/documentcloud/underscore/master/underscore.js' 'UI/JsLibraries/underscore.js': 'http://underscorejs.org/underscore.js'
}, },
uglify: { uglify: {
@ -43,6 +43,7 @@ module.exports = function(grunt) {
handlebars: { handlebars: {
options: { options: {
namespace: "Templates", namespace: "Templates",
wrapped: true,
processName: function(fileName){ processName: function(fileName){
return fileName return fileName
.replace('UI/','') .replace('UI/','')
@ -123,5 +124,6 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-curl'); grunt.loadNpmTasks('grunt-curl');
// Default task(s). // Default task(s).
grunt.registerTask('default', ['copy','less:bootstrap','handlebars', 'watch']); grunt.registerTask('default', ['copy','less:bootstrap','handlebars', 'watch']);
grunt.registerTask('update', ['curl']);
}; };

@ -57,13 +57,9 @@ define([
}, },
initialize: function (context, action, query) { initialize: function (options) {
if (action) { if (options.action) {
this.action = action.toLowerCase(); this.action = options.action.toLowerCase();
}
if (query) {
this.query = query.toLowerCase();
} }
}, },

@ -10,8 +10,8 @@ define(['app', 'Calendar/CalendarItemView'], function () {
calendar: '#calendar' calendar: '#calendar'
}, },
initialize : function (context, action, query, collection) { initialize : function () {
this.collection = collection; //should use this.collection?
this.calendar = new NzbDrone.Calendar.CalendarCollection(); this.calendar = new NzbDrone.Calendar.CalendarCollection();
}, },
onCompositeCollectionRendered: function () { onCompositeCollectionRendered: function () {

@ -9,69 +9,69 @@ define(['app', 'Shared/ModalRegion', 'AddSeries/AddSeriesLayout',
var controller = Backbone.Marionette.Controller.extend({ var controller = Backbone.Marionette.Controller.extend({
addSeries: function (action, query) {
this.setTitle('Add Series');
NzbDrone.mainRegion.show(new NzbDrone.AddSeries.AddSeriesLayout(this, action, query));
},
series: function () { series: function () {
this.setTitle('NzbDrone'); this._setTitle('NzbDrone');
NzbDrone.mainRegion.show(new NzbDrone.Series.Index.SeriesIndexCollectionView()); NzbDrone.mainRegion.show(new NzbDrone.Series.Index.SeriesIndexCollectionView());
}, },
upcoming: function (action, query) {
this.setTitle('Upcoming');
NzbDrone.mainRegion.show(new NzbDrone.Upcoming.UpcomingCollectionView(this, action, query));
},
calendar: function (action, query) {
this.setTitle('Calendar');
var calendarCollection = new NzbDrone.Calendar.CalendarCollection();
calendarCollection.fetch();
NzbDrone.mainRegion.show(new NzbDrone.Calendar.CalendarCollectionView(this, action, query, calendarCollection));
},
seriesDetails: function (query) { seriesDetails: function (query) {
var self = this; var self = this;
this.setTitle('Loading Series'); this._setTitle('Loading Series');
var series = new NzbDrone.Series.SeriesModel({ id: query }); var series = new NzbDrone.Series.SeriesModel({ id: query });
series.fetch({ series.fetch({
success: function (seriesModel) { success: function (seriesModel) {
self.setTitle(seriesModel.get('title')); self._setTitle(seriesModel.get('title'));
NzbDrone.mainRegion.show(new NzbDrone.Series.Details.SeriesDetailsView({ model: seriesModel })); NzbDrone.mainRegion.show(new NzbDrone.Series.Details.SeriesDetailsView({ model: seriesModel }));
} }
}); });
}, },
settings: function (action, query) { addSeries: function (action) {
this.setTitle('Settings'); this._setTitle('Add Series');
NzbDrone.mainRegion.show(new NzbDrone.AddSeries.AddSeriesLayout({action: action}));
},
upcoming: function () {
this._setTitle('Upcoming');
NzbDrone.mainRegion.show(new NzbDrone.Upcoming.UpcomingCollectionView());
},
calendar: function () {
this._setTitle('Calendar');
var calendarCollection = new NzbDrone.Calendar.CalendarCollection();
calendarCollection.fetch();
NzbDrone.mainRegion.show(new NzbDrone.Calendar.CalendarCollectionView({collection: calendarCollection}));
},
settings: function (action) {
this._setTitle('Settings');
var settingsModel = new NzbDrone.Settings.SettingsModel(); var settingsModel = new NzbDrone.Settings.SettingsModel();
settingsModel.fetch({ settingsModel.fetch({
success: function (settings) { success: function (settings) {
NzbDrone.mainRegion.show(new NzbDrone.Settings.SettingsLayout(this, action, query, settings)); NzbDrone.mainRegion.show(new NzbDrone.Settings.SettingsLayout({settings: settings, action: action}));
} }
}); });
}, },
missing: function (action, query) { missing: function () {
this.setTitle('Missing'); this._setTitle('Missing');
var missingCollection = new NzbDrone.Missing.MissingCollection(); var missingCollection = new NzbDrone.Missing.MissingCollection();
missingCollection.fetch({ missingCollection.fetch({
success: function (missing) { success: function () {
NzbDrone.mainRegion.show(new NzbDrone.Missing.MissingCollectionView(this, action, query, missing)); NzbDrone.mainRegion.show(new NzbDrone.Missing.MissingCollectionView({collection: missingCollection}));
} }
}); });
}, },
notFound: function () { notFound: function () {
this.setTitle('Not Found'); this._setTitle('Not Found');
NzbDrone.mainRegion.show(new NzbDrone.Shared.NotFoundView(this)); NzbDrone.mainRegion.show(new NzbDrone.Shared.NotFoundView(this));
}, },
setTitle: function (title) { _setTitle: function (title) {
$('#title-region').html(title); $('#title-region').html(title);
if (title.toLocaleLowerCase() === 'nzbdrone') { if (title.toLocaleLowerCase() === 'nzbdrone') {

@ -92,7 +92,7 @@
<script src="/JsLibraries/jquery.js"></script> <script src="/JsLibraries/jquery.js"></script>
<script src="/JsLibraries/bootstrap.js"></script> <script src="/JsLibraries/bootstrap.js"></script>
<script src="/JsLibraries/bootstrap.slider.js"></script> <script src="/JsLibraries/bootstrap.slider.js"></script>
<script src="/JsLibraries/bootstrapSwitch.js"></script> <script src="/JsLibraries/bootstrap.switch.js"></script>
<script src="/JsLibraries/underscore.js"></script> <script src="/JsLibraries/underscore.js"></script>
<script src="/JsLibraries/handlebars.runtime.js"></script> <script src="/JsLibraries/handlebars.runtime.js"></script>
<script src="/JsLibraries/backbone.js"></script> <script src="/JsLibraries/backbone.js"></script>

@ -1,23 +1,23 @@
// Backbone.CollectionBinder v0.1.1 // Backbone.CollectionBinder v1.0.0
// (c) 2012 Bart Wood // (c) 2013 Bart Wood
// Distributed Under MIT License // Distributed Under MIT License
(function () { (function(){
if (!Backbone) { if(!Backbone){
throw 'Please include Backbone.js before Backbone.ModelBinder.js'; throw 'Please include Backbone.js before Backbone.ModelBinder.js';
} }
if (!Backbone.ModelBinder) { if(!Backbone.ModelBinder){
throw 'Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js'; throw 'Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js';
} }
Backbone.CollectionBinder = function (elManagerFactory, options) { Backbone.CollectionBinder = function(elManagerFactory, options){
_.bindAll(this); _.bindAll(this);
this._elManagers = {}; this._elManagers = {};
this._elManagerFactory = elManagerFactory; this._elManagerFactory = elManagerFactory;
if (!this._elManagerFactory) throw 'elManagerFactory must be defined.'; if(!this._elManagerFactory) throw 'elManagerFactory must be defined.';
// Let the factory just use the trigger function on the view binder // Let the factory just use the trigger function on the view binder
this._elManagerFactory.trigger = this.trigger; this._elManagerFactory.trigger = this.trigger;
@ -25,14 +25,14 @@
this._options = options || {}; this._options = options || {};
}; };
Backbone.CollectionBinder.VERSION = '0.1.1'; Backbone.CollectionBinder.VERSION = '1.0.0';
_.extend(Backbone.CollectionBinder.prototype, Backbone.Events, { _.extend(Backbone.CollectionBinder.prototype, Backbone.Events, {
bind: function (collection, parentEl) { bind: function(collection, parentEl){
this.unbind(); this.unbind();
if (!collection) throw 'collection must be defined'; if(!collection) throw 'collection must be defined';
if (!parentEl) throw 'parentEl must be defined'; if(!parentEl) throw 'parentEl must be defined';
this._collection = collection; this._collection = collection;
this._elManagerFactory.setParentEl(parentEl); this._elManagerFactory.setParentEl(parentEl);
@ -45,8 +45,8 @@
}, },
unbind: function () { unbind: function(){
if (this._collection !== undefined) { if(this._collection !== undefined){
this._collection.off('add', this._onCollectionAdd); this._collection.off('add', this._onCollectionAdd);
this._collection.off('remove', this._onCollectionRemove); this._collection.off('remove', this._onCollectionRemove);
this._collection.off('reset', this._onCollectionReset); this._collection.off('reset', this._onCollectionReset);
@ -55,13 +55,13 @@
this._removeAllElManagers(); this._removeAllElManagers();
}, },
getManagerForEl: function (el) { getManagerForEl: function(el){
var i, elManager, elManagers = _.values(this._elManagers); var i, elManager, elManagers = _.values(this._elManagers);
for (i = 0; i < elManagers.length; i++) { for(i = 0; i < elManagers.length; i++){
elManager = elManagers[i]; elManager = elManagers[i];
if (elManager.isElContained(el)) { if(elManager.isElContained(el)){
return elManager; return elManager;
} }
} }
@ -69,13 +69,13 @@
return undefined; return undefined;
}, },
getManagerForModel: function (model) { getManagerForModel: function(model){
var i, elManager, elManagers = _.values(this._elManagers); var i, elManager, elManagers = _.values(this._elManagers);
for (i = 0; i < elManagers.length; i++) { for(i = 0; i < elManagers.length; i++){
elManager = elManagers[i]; elManager = elManagers[i];
if (elManager.getModel() === model) { if(elManager.getModel() === model){
return elManager; return elManager;
} }
} }
@ -83,31 +83,31 @@
return undefined; return undefined;
}, },
_onCollectionAdd: function (model) { _onCollectionAdd: function(model){
this._elManagers[model.cid] = this._elManagerFactory.makeElManager(model); this._elManagers[model.cid] = this._elManagerFactory.makeElManager(model);
this._elManagers[model.cid].createEl(); this._elManagers[model.cid].createEl();
if (this._options['autoSort']) { if(this._options['autoSort']){
this.sortRootEls(); this.sortRootEls();
} }
}, },
_onCollectionRemove: function (model) { _onCollectionRemove: function(model){
this._removeElManager(model); this._removeElManager(model);
}, },
_onCollectionReset: function () { _onCollectionReset: function(){
this._removeAllElManagers(); this._removeAllElManagers();
this._collection.each(function (model) { this._collection.each(function(model){
this._onCollectionAdd(model); this._onCollectionAdd(model);
}, this); }, this);
this.trigger('elsReset', this._collection); this.trigger('elsReset', this._collection);
}, },
_removeAllElManagers: function () { _removeAllElManagers: function(){
_.each(this._elManagers, function (elManager) { _.each(this._elManagers, function(elManager){
elManager.removeEl(); elManager.removeEl();
delete this._elManagers[elManager._model.cid]; delete this._elManagers[elManager._model.cid];
}, this); }, this);
@ -116,21 +116,21 @@
this._elManagers = {}; this._elManagers = {};
}, },
_removeElManager: function (model) { _removeElManager: function(model){
if (this._elManagers[model.cid] !== undefined) { if(this._elManagers[model.cid] !== undefined){
this._elManagers[model.cid].removeEl(); this._elManagers[model.cid].removeEl();
delete this._elManagers[model.cid]; delete this._elManagers[model.cid];
} }
}, },
sortRootEls: function () { sortRootEls: function(){
this._collection.each(function (model, modelIndex) { this._collection.each(function(model, modelIndex){
var modelElManager = this.getManagerForModel(model); var modelElManager = this.getManagerForModel(model);
if (modelElManager) { if(modelElManager){
var modelEl = modelElManager.getEl(); var modelEl = modelElManager.getEl();
var currentRootEls = this._elManagerFactory.getParentEl().children(); var currentRootEls = $(this._elManagerFactory.getParentEl()).children();
if (currentRootEls[modelIndex] !== modelEl[0]) { if(currentRootEls[modelIndex] !== modelEl[0]){
modelEl.detach(); modelEl.detach();
modelEl.insertBefore(currentRootEls[modelIndex]); modelEl.insertBefore(currentRootEls[modelIndex]);
} }
@ -142,40 +142,40 @@
// The ElManagerFactory is used for els that are just html templates // The ElManagerFactory is used for els that are just html templates
// elHtml - how the model's html will be rendered. Must have a single root element (div,span). // elHtml - how the model's html will be rendered. Must have a single root element (div,span).
// bindings (optional) - either a string which is the binding attribute (name, id, data-name, etc.) or a normal bindings hash // bindings (optional) - either a string which is the binding attribute (name, id, data-name, etc.) or a normal bindings hash
Backbone.CollectionBinder.ElManagerFactory = function (elHtml, bindings) { Backbone.CollectionBinder.ElManagerFactory = function(elHtml, bindings){
_.bindAll(this); _.bindAll(this);
this._elHtml = elHtml; this._elHtml = elHtml;
this._bindings = bindings; this._bindings = bindings;
if (!_.isString(this._elHtml)) throw 'elHtml must be a valid html string'; if(! _.isString(this._elHtml)) throw 'elHtml must be a valid html string';
}; };
_.extend(Backbone.CollectionBinder.ElManagerFactory.prototype, { _.extend(Backbone.CollectionBinder.ElManagerFactory.prototype, {
setParentEl: function (parentEl) { setParentEl: function(parentEl){
this._parentEl = parentEl; this._parentEl = parentEl;
}, },
getParentEl: function () { getParentEl: function(){
return this._parentEl; return this._parentEl;
}, },
makeElManager: function (model) { makeElManager: function(model){
var elManager = { var elManager = {
_model: model, _model: model,
createEl: function () { createEl: function(){
this._el = $(this._elHtml); this._el = $(this._elHtml);
$(this._parentEl).append(this._el); $(this._parentEl).append(this._el);
if (this._bindings) { if(this._bindings){
if (_.isString(this._bindings)) { if(_.isString(this._bindings)){
this._modelBinder = new Backbone.ModelBinder(); this._modelBinder = new Backbone.ModelBinder();
this._modelBinder.bind(this._model, this._el, Backbone.ModelBinder.createDefaultBindings(this._el, this._bindings)); this._modelBinder.bind(this._model, this._el, Backbone.ModelBinder.createDefaultBindings(this._el, this._bindings));
} }
else if (_.isObject(this._bindings)) { else if(_.isObject(this._bindings)){
this._modelBinder = new Backbone.ModelBinder(); this._modelBinder = new Backbone.ModelBinder();
this._modelBinder.bind(this._model, this._el, this._bindings); this._modelBinder.bind(this._model, this._el, this._bindings);
} }
@ -187,8 +187,8 @@
this.trigger('elCreated', this._model, this._el); this.trigger('elCreated', this._model, this._el);
}, },
removeEl: function () { removeEl: function(){
if (this._modelBinder !== undefined) { if(this._modelBinder !== undefined){
this._modelBinder.unbind(); this._modelBinder.unbind();
} }
@ -196,15 +196,15 @@
this.trigger('elRemoved', this._model, this._el); this.trigger('elRemoved', this._model, this._el);
}, },
isElContained: function (findEl) { isElContained: function(findEl){
return this._el === findEl || $(this._el).has(findEl).length > 0; return this._el === findEl || $(this._el).has(findEl).length > 0;
}, },
getModel: function () { getModel: function(){
return this._model; return this._model;
}, },
getEl: function () { getEl: function(){
return this._el; return this._el;
} }
}; };
@ -218,36 +218,36 @@
// The ViewManagerFactory is used for els that are created and owned by backbone views. // The ViewManagerFactory is used for els that are created and owned by backbone views.
// There is no bindings option because the view made by the viewCreator should take care of any binding // There is no bindings option because the view made by the viewCreator should take care of any binding
// viewCreator - a callback that will create backbone view instances for a model passed to the callback // viewCreator - a callback that will create backbone view instances for a model passed to the callback
Backbone.CollectionBinder.ViewManagerFactory = function (viewCreator) { Backbone.CollectionBinder.ViewManagerFactory = function(viewCreator){
_.bindAll(this); _.bindAll(this);
this._viewCreator = viewCreator; this._viewCreator = viewCreator;
if (!_.isFunction(this._viewCreator)) throw 'viewCreator must be a valid function that accepts a model and returns a backbone view'; if(!_.isFunction(this._viewCreator)) throw 'viewCreator must be a valid function that accepts a model and returns a backbone view';
}; };
_.extend(Backbone.CollectionBinder.ViewManagerFactory.prototype, { _.extend(Backbone.CollectionBinder.ViewManagerFactory.prototype, {
setParentEl: function (parentEl) { setParentEl: function(parentEl){
this._parentEl = parentEl; this._parentEl = parentEl;
}, },
getParentEl: function () { getParentEl: function(){
return this._parentEl; return this._parentEl;
}, },
makeElManager: function (model) { makeElManager: function(model){
var elManager = { var elManager = {
_model: model, _model: model,
createEl: function () { createEl: function(){
this._view = this._viewCreator(model); this._view = this._viewCreator(model);
$(this._parentEl).append(this._view.render(this._model).el); $(this._parentEl).append(this._view.render(this._model).el);
this.trigger('elCreated', this._model, this._view); this.trigger('elCreated', this._model, this._view);
}, },
removeEl: function () { removeEl: function(){
if (this._view.close !== undefined) { if(this._view.close !== undefined){
this._view.close(); this._view.close();
} }
else { else {
@ -258,19 +258,19 @@
this.trigger('elRemoved', this._model, this._view); this.trigger('elRemoved', this._model, this._view);
}, },
isElContained: function (findEl) { isElContained: function(findEl){
return this._view.el === findEl || this._view.$el.has(findEl).length > 0; return this._view.el === findEl || this._view.$el.has(findEl).length > 0;
}, },
getModel: function () { getModel: function(){
return this._model; return this._model;
}, },
getView: function () { getView: function(){
return this._view; return this._view;
}, },
getEl: function () { getEl: function(){
return this._view.$el; return this._view.$el;
} }
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,5 +1,5 @@
// Backbone.ModelBinder v0.1.6 // Backbone.ModelBinder v1.0.1
// (c) 2012 Bart Wood // (c) 2013 Bart Wood
// Distributed Under MIT License // Distributed Under MIT License
(function (factory) { (function (factory) {
@ -10,36 +10,40 @@
// Browser globals // Browser globals
factory(_, $, Backbone); factory(_, $, Backbone);
} }
}(function (_, $, Backbone) { }(function(_, $, Backbone){
if (!Backbone) { if(!Backbone){
throw 'Please include Backbone.js before Backbone.ModelBinder.js'; throw 'Please include Backbone.js before Backbone.ModelBinder.js';
} }
Backbone.ModelBinder = function (modelSetOptions) { Backbone.ModelBinder = function(){
_.bindAll(this); _.bindAll(this);
this._modelSetOptions = modelSetOptions || {}; };
// Static setter for class level options
Backbone.ModelBinder.SetOptions = function(options){
Backbone.ModelBinder.options = options;
}; };
// Current version of the library. // Current version of the library.
Backbone.ModelBinder.VERSION = '0.1.6'; Backbone.ModelBinder.VERSION = '1.0.1';
Backbone.ModelBinder.Constants = {}; Backbone.ModelBinder.Constants = {};
Backbone.ModelBinder.Constants.ModelToView = 'ModelToView'; Backbone.ModelBinder.Constants.ModelToView = 'ModelToView';
Backbone.ModelBinder.Constants.ViewToModel = 'ViewToModel'; Backbone.ModelBinder.Constants.ViewToModel = 'ViewToModel';
_.extend(Backbone.ModelBinder.prototype, { _.extend(Backbone.ModelBinder.prototype, {
bind: function (model, rootEl, attributeBindings, modelSetOptions) { bind:function (model, rootEl, attributeBindings, options) {
this.unbind(); this.unbind();
this._model = model; this._model = model;
this._rootEl = rootEl; this._rootEl = rootEl;
this._modelSetOptions = _.extend({}, this._modelSetOptions, modelSetOptions); this._setOptions(options);
if (!this._model) throw 'model must be specified'; if (!this._model) this._throwException('model must be specified');
if (!this._rootEl) throw 'rootEl must be specified'; if (!this._rootEl) this._throwException('rootEl must be specified');
if (attributeBindings) { if(attributeBindings){
// Create a deep clone of the attribute bindings // Create a deep clone of the attribute bindings
this._attributeBindings = $.extend(true, {}, attributeBindings); this._attributeBindings = $.extend(true, {}, attributeBindings);
@ -54,43 +58,61 @@
this._bindViewToModel(); this._bindViewToModel();
}, },
bindCustomTriggers: function (model, rootEl, triggers, attributeBindings, modelSetOptions) { bindCustomTriggers: function (model, rootEl, triggers, attributeBindings, modelSetOptions) {
this._triggers = triggers; this._triggers = triggers;
this.bind(model, rootEl, attributeBindings, modelSetOptions) this.bind(model, rootEl, attributeBindings, modelSetOptions)
}, },
unbind: function () { unbind:function () {
this._unbindModelToView(); this._unbindModelToView();
this._unbindViewToModel(); this._unbindViewToModel();
if (this._attributeBindings) { if(this._attributeBindings){
delete this._attributeBindings; delete this._attributeBindings;
this._attributeBindings = undefined; this._attributeBindings = undefined;
} }
}, },
_setOptions: function(options){
this._options = _.extend({}, Backbone.ModelBinder.options, options);
// initialize default options
if(!this._options['modelSetOptions']){
this._options['modelSetOptions'] = {};
}
this._options['modelSetOptions'].changeSource = 'ModelBinder';
if(!this._options['changeTriggers']){
this._options['changeTriggers'] = {'': 'change', '[contenteditable]': 'blur'};
}
if(!this._options['initialCopyDirection']){
this._options['initialCopyDirection'] = Backbone.ModelBinder.Constants.ModelToView;
}
},
// Converts the input bindings, which might just be empty or strings, to binding objects // Converts the input bindings, which might just be empty or strings, to binding objects
_initializeAttributeBindings: function () { _initializeAttributeBindings:function () {
var attributeBindingKey, inputBinding, attributeBinding, elementBindingCount, elementBinding; var attributeBindingKey, inputBinding, attributeBinding, elementBindingCount, elementBinding;
for (attributeBindingKey in this._attributeBindings) { for (attributeBindingKey in this._attributeBindings) {
inputBinding = this._attributeBindings[attributeBindingKey]; inputBinding = this._attributeBindings[attributeBindingKey];
if (_.isString(inputBinding)) { if (_.isString(inputBinding)) {
attributeBinding = { elementBindings: [{ selector: inputBinding }] }; attributeBinding = {elementBindings: [{selector: inputBinding}]};
} }
else if (_.isArray(inputBinding)) { else if (_.isArray(inputBinding)) {
attributeBinding = { elementBindings: inputBinding }; attributeBinding = {elementBindings: inputBinding};
} }
else if (_.isObject(inputBinding)) { else if(_.isObject(inputBinding)){
attributeBinding = { elementBindings: [inputBinding] }; attributeBinding = {elementBindings: [inputBinding]};
} }
else { else {
throw 'Unsupported type passed to Model Binder ' + attributeBinding; this._throwException('Unsupported type passed to Model Binder ' + attributeBinding);
} }
// Add a linkage from the element binding back to the attribute binding // Add a linkage from the element binding back to the attribute binding
for (elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++) { for(elementBindingCount = 0; elementBindingCount < attributeBinding.elementBindings.length; elementBindingCount++){
elementBinding = attributeBinding.elementBindings[elementBindingCount]; elementBinding = attributeBinding.elementBindings[elementBindingCount];
elementBinding.attributeBinding = attributeBinding; elementBinding.attributeBinding = attributeBinding;
} }
@ -101,28 +123,28 @@
}, },
// If the bindings are not specified, the default binding is performed on the name attribute // If the bindings are not specified, the default binding is performed on the name attribute
_initializeDefaultBindings: function () { _initializeDefaultBindings: function(){
var elCount, namedEls, namedEl, name, attributeBinding; var elCount, namedEls, namedEl, name, attributeBinding;
this._attributeBindings = {}; this._attributeBindings = {};
namedEls = $('[name]', this._rootEl); namedEls = $('[name]', this._rootEl);
for (elCount = 0; elCount < namedEls.length; elCount++) { for(elCount = 0; elCount < namedEls.length; elCount++){
namedEl = namedEls[elCount]; namedEl = namedEls[elCount];
name = $(namedEl).attr('name'); name = $(namedEl).attr('name');
// For elements like radio buttons we only want a single attribute binding with possibly multiple element bindings // For elements like radio buttons we only want a single attribute binding with possibly multiple element bindings
if (!this._attributeBindings[name]) { if(!this._attributeBindings[name]){
attributeBinding = { attributeName: name }; attributeBinding = {attributeName: name};
attributeBinding.elementBindings = [{ attributeBinding: attributeBinding, boundEls: [namedEl] }]; attributeBinding.elementBindings = [{attributeBinding: attributeBinding, boundEls: [namedEl]}];
this._attributeBindings[name] = attributeBinding; this._attributeBindings[name] = attributeBinding;
} }
else { else{
this._attributeBindings[name].elementBindings.push({ attributeBinding: this._attributeBindings[name], boundEls: [namedEl] }); this._attributeBindings[name].elementBindings.push({attributeBinding: this._attributeBindings[name], boundEls: [namedEl]});
} }
} }
}, },
_initializeElBindings: function () { _initializeElBindings:function () {
var bindingKey, attributeBinding, bindingCount, elementBinding, foundEls, elCount, el; var bindingKey, attributeBinding, bindingCount, elementBinding, foundEls, elCount, el;
for (bindingKey in this._attributeBindings) { for (bindingKey in this._attributeBindings) {
attributeBinding = this._attributeBindings[bindingKey]; attributeBinding = this._attributeBindings[bindingKey];
@ -137,7 +159,7 @@
} }
if (foundEls.length === 0) { if (foundEls.length === 0) {
throw 'Bad binding found. No elements returned for binding selector ' + elementBinding.selector; this._throwException('Bad binding found. No elements returned for binding selector ' + elementBinding.selector);
} }
else { else {
elementBinding.boundEls = []; elementBinding.boundEls = [];
@ -153,40 +175,67 @@
_bindModelToView: function () { _bindModelToView: function () {
this._model.on('change', this._onModelChange, this); this._model.on('change', this._onModelChange, this);
this.copyModelAttributesToView(); if(this._options['initialCopyDirection'] === Backbone.ModelBinder.Constants.ModelToView){
this.copyModelAttributesToView();
}
}, },
// attributesToCopy is an optional parameter - if empty, all attributes // attributesToCopy is an optional parameter - if empty, all attributes
// that are bound will be copied. Otherwise, only attributeBindings specified // that are bound will be copied. Otherwise, only attributeBindings specified
// in the attributesToCopy are copied. // in the attributesToCopy are copied.
copyModelAttributesToView: function (attributesToCopy) { copyModelAttributesToView: function(attributesToCopy){
var attributeName, attributeBinding; var attributeName, attributeBinding;
for (attributeName in this._attributeBindings) { for (attributeName in this._attributeBindings) {
if (attributesToCopy === undefined || _.indexOf(attributesToCopy, attributeName) !== -1) { if(attributesToCopy === undefined || _.indexOf(attributesToCopy, attributeName) !== -1){
attributeBinding = this._attributeBindings[attributeName]; attributeBinding = this._attributeBindings[attributeName];
this._copyModelToView(attributeBinding); this._copyModelToView(attributeBinding);
} }
} }
}, },
_unbindModelToView: function () { copyViewValuesToModel: function(){
if (this._model) { var bindingKey, attributeBinding, bindingCount, elementBinding, elCount, el;
for (bindingKey in this._attributeBindings) {
attributeBinding = this._attributeBindings[bindingKey];
for (bindingCount = 0; bindingCount < attributeBinding.elementBindings.length; bindingCount++) {
elementBinding = attributeBinding.elementBindings[bindingCount];
if(this._isBindingUserEditable(elementBinding)){
if(this._isBindingRadioGroup(elementBinding)){
el = this._getRadioButtonGroupCheckedEl(elementBinding);
if(el){
this._copyViewToModel(elementBinding, el);
}
}
else {
for(elCount = 0; elCount < elementBinding.boundEls.length; elCount++){
el = $(elementBinding.boundEls[elCount]);
if(this._isElUserEditable(el)){
this._copyViewToModel(elementBinding, el);
}
}
}
}
}
}
},
_unbindModelToView: function(){
if(this._model){
this._model.off('change', this._onModelChange); this._model.off('change', this._onModelChange);
this._model = undefined; this._model = undefined;
} }
}, },
_bindViewToModel: function () { _bindViewToModel: function () {
if (this._triggers) { _.each(this._options['changeTriggers'], function (event, selector) {
_.each(this._triggers, function (event, selector) { $(this._rootEl).delegate(selector, event, this._onElChanged);
$(this._rootEl).delegate(selector, event, this._onElChanged); }, this);
}, this);
} if(this._options['initialCopyDirection'] === Backbone.ModelBinder.Constants.ViewToModel){
else { this.copyViewValuesToModel();
$(this._rootEl).delegate('', 'change', this._onElChanged);
// The change event doesn't work properly for contenteditable elements - but blur does
$(this._rootEl).delegate('[contenteditable]', 'blur', this._onElChanged);
} }
}, },
@ -204,13 +253,13 @@
} }
}, },
_onElChanged: function (event) { _onElChanged:function (event) {
var el, elBindings, elBindingCount, elBinding; var el, elBindings, elBindingCount, elBinding;
el = $(event.target)[0]; el = $(event.target)[0];
elBindings = this._getElBindings(el); elBindings = this._getElBindings(el);
for (elBindingCount = 0; elBindingCount < elBindings.length; elBindingCount++) { for(elBindingCount = 0; elBindingCount < elBindings.length; elBindingCount++){
elBinding = elBindings[elBindingCount]; elBinding = elBindings[elBindingCount];
if (this._isBindingUserEditable(elBinding)) { if (this._isBindingUserEditable(elBinding)) {
this._copyViewToModel(elBinding, el); this._copyViewToModel(elBinding, el);
@ -218,13 +267,44 @@
} }
}, },
_isBindingUserEditable: function (elBinding) { _isBindingUserEditable: function(elBinding){
return elBinding.elAttribute === undefined || return elBinding.elAttribute === undefined ||
elBinding.elAttribute === 'text' || elBinding.elAttribute === 'text' ||
elBinding.elAttribute === 'html'; elBinding.elAttribute === 'html';
}, },
_getElBindings: function (findEl) { _isElUserEditable: function(el){
var isContentEditable = el.attr('contenteditable');
return isContentEditable || el.is('input') || el.is('select') || el.is('textarea');
},
_isBindingRadioGroup: function(elBinding){
var elCount, el;
var isAllRadioButtons = elBinding.boundEls.length > 0;
for(elCount = 0; elCount < elBinding.boundEls.length; elCount++){
el = $(elBinding.boundEls[elCount]);
if(el.attr('type') !== 'radio'){
isAllRadioButtons = false;
break;
}
}
return isAllRadioButtons;
},
_getRadioButtonGroupCheckedEl: function(elBinding){
var elCount, el;
for(elCount = 0; elCount < elBinding.boundEls.length; elCount++){
el = $(elBinding.boundEls[elCount]);
if(el.attr('type') === 'radio' && el.attr('checked')){
return el;
}
}
return undefined;
},
_getElBindings:function (findEl) {
var attributeName, attributeBinding, elementBindingCount, elementBinding, boundElCount, boundEl; var attributeName, attributeBinding, elementBindingCount, elementBinding, boundElCount, boundEl;
var elBindings = []; var elBindings = [];
@ -247,7 +327,7 @@
return elBindings; return elBindings;
}, },
_onModelChange: function () { _onModelChange:function () {
var changedAttribute, attributeBinding; var changedAttribute, attributeBinding;
for (changedAttribute in this._model.changedAttributes()) { for (changedAttribute in this._model.changedAttributes()) {
@ -259,7 +339,7 @@
} }
}, },
_copyModelToView: function (attributeBinding) { _copyModelToView:function (attributeBinding) {
var elementBindingCount, elementBinding, boundElCount, boundEl, value, convertedValue; var elementBindingCount, elementBinding, boundElCount, boundEl, value, convertedValue;
value = this._model.get(attributeBinding.attributeName); value = this._model.get(attributeBinding.attributeName);
@ -270,7 +350,7 @@
for (boundElCount = 0; boundElCount < elementBinding.boundEls.length; boundElCount++) { for (boundElCount = 0; boundElCount < elementBinding.boundEls.length; boundElCount++) {
boundEl = elementBinding.boundEls[boundElCount]; boundEl = elementBinding.boundEls[boundElCount];
if (!boundEl._isSetting) { if(!boundEl._isSetting){
convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value); convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value);
this._setEl($(boundEl), elementBinding, convertedValue); this._setEl($(boundEl), elementBinding, convertedValue);
} }
@ -287,7 +367,7 @@
} }
}, },
_setElAttribute: function (el, elementBinding, convertedValue) { _setElAttribute:function (el, elementBinding, convertedValue) {
switch (elementBinding.elAttribute) { switch (elementBinding.elAttribute) {
case 'html': case 'html':
el.html(convertedValue); el.html(convertedValue);
@ -309,12 +389,12 @@
break; break;
case 'class': case 'class':
var previousValue = this._model.previous(elementBinding.attributeBinding.attributeName); var previousValue = this._model.previous(elementBinding.attributeBinding.attributeName);
if (!_.isUndefined(previousValue)) { if(!_.isUndefined(previousValue)){
previousValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, previousValue); previousValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, previousValue);
el.removeClass(previousValue); el.removeClass(previousValue);
} }
if (convertedValue) { if(convertedValue){
el.addClass(convertedValue); el.addClass(convertedValue);
} }
break; break;
@ -323,13 +403,16 @@
} }
}, },
_setElValue: function (el, convertedValue) { _setElValue:function (el, convertedValue) {
if (el.attr('type')) { if(el.attr('type')){
switch (el.attr('type')) { switch (el.attr('type')) {
case 'radio': case 'radio':
if (el.val() === convertedValue) { if (el.val() === convertedValue) {
el.attr('checked', 'checked'); el.attr('checked', 'checked');
} }
else {
el.removeAttr('checked');
}
break; break;
case 'checkbox': case 'checkbox':
if (convertedValue) { if (convertedValue) {
@ -339,28 +422,30 @@
el.removeAttr('checked'); el.removeAttr('checked');
} }
break; break;
case 'file':
break;
default: default:
el.val(convertedValue); el.val(convertedValue);
} }
} }
else if (el.is('input') || el.is('select') || el.is('textarea')) { else if(el.is('input') || el.is('select') || el.is('textarea')){
el.val(convertedValue); el.val(convertedValue || (convertedValue === 0 ? '0' : ''));
} }
else { else {
el.text(convertedValue); el.text(convertedValue || (convertedValue === 0 ? '0' : ''));
} }
}, },
_copyViewToModel: function (elementBinding, el) { _copyViewToModel: function (elementBinding, el) {
var value, convertedValue; var result, value, convertedValue;
if (!el._isSetting) { if (!el._isSetting) {
el._isSetting = true; el._isSetting = true;
this._setModel(elementBinding, $(el)); result = this._setModel(elementBinding, $(el));
el._isSetting = false; el._isSetting = false;
if (elementBinding.converter) { if(result && elementBinding.converter){
value = this._model.get(elementBinding.attributeBinding.attributeName); value = this._model.get(elementBinding.attributeBinding.attributeName);
convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value); convertedValue = this._getConvertedValue(Backbone.ModelBinder.Constants.ModelToView, elementBinding, value);
this._setEl($(el), elementBinding, convertedValue); this._setEl($(el), elementBinding, convertedValue);
@ -368,12 +453,12 @@
} }
}, },
_getElValue: function (elementBinding, el) { _getElValue: function(elementBinding, el){
switch (el.attr('type')) { switch (el.attr('type')) {
case 'checkbox': case 'checkbox':
return el.prop('checked') ? true : false; return el.prop('checked') ? true : false;
default: default:
if (el.attr('contenteditable') !== undefined) { if(el.attr('contenteditable') !== undefined){
return el.html(); return el.html();
} }
else { else {
@ -387,30 +472,40 @@
var elVal = this._getElValue(elementBinding, el); var elVal = this._getElValue(elementBinding, el);
elVal = this._getConvertedValue(Backbone.ModelBinder.Constants.ViewToModel, elementBinding, elVal); elVal = this._getConvertedValue(Backbone.ModelBinder.Constants.ViewToModel, elementBinding, elVal);
data[elementBinding.attributeBinding.attributeName] = elVal; data[elementBinding.attributeBinding.attributeName] = elVal;
var opts = _.extend({}, this._modelSetOptions, { changeSource: 'ModelBinder' }); return this._model.set(data, this._options['modelSetOptions']);
this._model.set(data, opts);
}, },
_getConvertedValue: function (direction, elementBinding, value) { _getConvertedValue: function (direction, elementBinding, value) {
if (elementBinding.converter) { if (elementBinding.converter) {
value = elementBinding.converter(direction, value, elementBinding.attributeBinding.attributeName, this._model); value = elementBinding.converter(direction, value, elementBinding.attributeBinding.attributeName, this._model, elementBinding.boundEls);
} }
return value; return value;
},
_throwException: function(message){
if(this._options.suppressThrows){
if(console && console.error){
console.error(message);
}
}
else {
throw message;
}
} }
}); });
Backbone.ModelBinder.CollectionConverter = function (collection) { Backbone.ModelBinder.CollectionConverter = function(collection){
this._collection = collection; this._collection = collection;
if (!this._collection) { if(!this._collection){
throw 'Collection must be defined'; throw 'Collection must be defined';
} }
_.bindAll(this, 'convert'); _.bindAll(this, 'convert');
}; };
_.extend(Backbone.ModelBinder.CollectionConverter.prototype, { _.extend(Backbone.ModelBinder.CollectionConverter.prototype, {
convert: function (direction, value) { convert: function(direction, value){
if (direction === Backbone.ModelBinder.Constants.ModelToView) { if (direction === Backbone.ModelBinder.Constants.ModelToView) {
return value ? value.id : undefined; return value ? value.id : undefined;
} }
@ -425,25 +520,25 @@
// attributeType - probably 'name' or 'id' in most cases // attributeType - probably 'name' or 'id' in most cases
// converter(optional) - the default converter you want applied to all your bindings // converter(optional) - the default converter you want applied to all your bindings
// elAttribute(optional) - the default elAttribute you want applied to all your bindings // elAttribute(optional) - the default elAttribute you want applied to all your bindings
Backbone.ModelBinder.createDefaultBindings = function (rootEl, attributeType, converter, elAttribute) { Backbone.ModelBinder.createDefaultBindings = function(rootEl, attributeType, converter, elAttribute){
var foundEls, elCount, foundEl, attributeName; var foundEls, elCount, foundEl, attributeName;
var bindings = {}; var bindings = {};
foundEls = $('[' + attributeType + ']', rootEl); foundEls = $('[' + attributeType + ']', rootEl);
for (elCount = 0; elCount < foundEls.length; elCount++) { for(elCount = 0; elCount < foundEls.length; elCount++){
foundEl = foundEls[elCount]; foundEl = foundEls[elCount];
attributeName = $(foundEl).attr(attributeType); attributeName = $(foundEl).attr(attributeType);
if (!bindings[attributeName]) { if(!bindings[attributeName]){
var attributeBinding = { selector: '[' + attributeType + '="' + attributeName + '"]' }; var attributeBinding = {selector: '[' + attributeType + '="' + attributeName + '"]'};
bindings[attributeName] = attributeBinding; bindings[attributeName] = attributeBinding;
if (converter) { if(converter){
bindings[attributeName].converter = converter; bindings[attributeName].converter = converter;
} }
if (elAttribute) { if(elAttribute){
bindings[attributeName].elAttribute = elAttribute; bindings[attributeName].elAttribute = elAttribute;
} }
} }
@ -453,19 +548,19 @@
}; };
// Helps you to combine 2 sets of bindings // Helps you to combine 2 sets of bindings
Backbone.ModelBinder.combineBindings = function (destination, source) { Backbone.ModelBinder.combineBindings = function(destination, source){
_.each(source, function (value, key) { _.each(source, function(value, key){
var elementBinding = { selector: value.selector }; var elementBinding = {selector: value.selector};
if (value.converter) { if(value.converter){
elementBinding.converter = value.converter; elementBinding.converter = value.converter;
} }
if (value.elAttribute) { if(value.elAttribute){
elementBinding.elAttribute = value.elAttribute; elementBinding.elAttribute = value.elAttribute;
} }
if (!destination[key]) { if(!destination[key]){
destination[key] = elementBinding; destination[key] = elementBinding;
} }
else { else {

@ -1,5 +1,5 @@
/* ============================================================ /* ============================================================
* bootstrapSwitch v1.2 by Larentis Mattia @spiritualGuru * bootstrapSwitch v1.3 by Larentis Mattia @spiritualGuru
* http://www.larentis.eu/switch/ * http://www.larentis.eu/switch/
* ============================================================ * ============================================================
* Licensed under the Apache License, Version 2.0 * Licensed under the Apache License, Version 2.0
@ -23,7 +23,8 @@
, color , color
, moving , moving
, onLabel = "ON" , onLabel = "ON"
, offLabel = "OFF"; , offLabel = "OFF"
, icon = false;
$.each(['switch-mini', 'switch-small', 'switch-large'], function (i, el) { $.each(['switch-mini', 'switch-small', 'switch-large'], function (i, el) {
if (classes.indexOf(el) >= 0) if (classes.indexOf(el) >= 0)
@ -41,6 +42,9 @@
if ($element.data('off-label') !== undefined) if ($element.data('off-label') !== undefined)
offLabel = $element.data('off-label'); offLabel = $element.data('off-label');
if ($element.data('icon') !== undefined)
icon = $element.data('icon');
$switchLeft = $('<span>') $switchLeft = $('<span>')
.addClass("switch-left") .addClass("switch-left")
.addClass(myClasses) .addClass(myClasses)
@ -62,6 +66,10 @@
.addClass(myClasses) .addClass(myClasses)
.attr('for', $element.find('input').attr('id')); .attr('for', $element.find('input').attr('id'));
if (icon) {
$label.html('<i class="icon icon-' + icon + '"></i>');
}
$div = $element.find(':checkbox').wrap($('<div>')).parent().data('animated', false); $div = $element.find(':checkbox').wrap($('<div>')).parent().data('animated', false);
if ($element.data('animated') !== false) if ($element.data('animated') !== false)
@ -100,21 +108,26 @@
}); });
$element.find('input').on('change', function (e) { $element.find('input').on('change', function (e) {
var $element = $(this).parent(); var $this = $(this)
, $element = $this.parent()
, thisState = $this.is(':checked')
, state = $element.is('.switch-off');
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation();
$element.css('left', ''); $element.css('left', '');
if ($(this).is(':checked')) if (state === thisState) {
$element.removeClass('switch-off').addClass('switch-on');
else $element.removeClass('switch-on').addClass('switch-off');
if ($element.data('animated') !== false) if (thisState)
$element.addClass("switch-animate"); $element.removeClass('switch-off').addClass('switch-on');
else $element.removeClass('switch-on').addClass('switch-off');
$element.parent().trigger('switch-change', {'el': $(this), 'value': $(this).is(':checked')}) if ($element.data('animated') !== false)
$element.addClass("switch-animate");
$element.parent().trigger('switch-change', {'el': $this, 'value': thisState})
}
}); });
$element.find('label').on('mousedown touchstart', function (e) { $element.find('label').on('mousedown touchstart', function (e) {

@ -123,7 +123,7 @@
function batchMethodExecute(klass, args, fn) { function batchMethodExecute(klass, args, fn) {
var all = args.length === 0, methods = multiArgs(args), changed = false; var all = args.length === 0, methods = multiArgs(args), changed = false;
iterateOverObject(klass['SugarMethods'], function(name, m) { iterateOverObject(klass['SugarMethods'], function(name, m) {
if(all || methods.indexOf(name) > -1) { if(all || methods.indexOf(name) !== -1) {
changed = true; changed = true;
fn(m.instance ? klass.prototype : klass, name, m); fn(m.instance ? klass.prototype : klass, name, m);
} }
@ -211,6 +211,12 @@
} }
} }
function simpleRepeat(n, fn) {
for(var i = 0; i < n; i++) {
fn(i);
}
}
function simpleMerge(target, source) { function simpleMerge(target, source) {
iterateOverObject(source, function(key) { iterateOverObject(source, function(key) {
target[key] = source[key]; target[key] = source[key];
@ -228,16 +234,6 @@
// Number helpers // Number helpers
function getRange(start, stop, fn, step) {
var arr = [], i = parseInt(start), down = step < 0;
while((!down && i <= stop) || (down && i >= stop)) {
arr.push(i);
if(fn) fn.call(this, i);
i += step || 1;
}
return arr;
}
function round(val, precision, method) { function round(val, precision, method) {
var fn = math[method || 'round']; var fn = math[method || 'round'];
var multiplier = math.pow(10, math.abs(precision || 0)); var multiplier = math.pow(10, math.abs(precision || 0));
@ -253,6 +249,8 @@
return round(val, precision, 'floor'); return round(val, precision, 'floor');
} }
// Used by Number and Date
function padNumber(num, place, sign, base) { function padNumber(num, place, sign, base) {
var str = math.abs(num).toString(base || 10); var str = math.abs(num).toString(base || 10);
str = repeatString(place - str.replace(/\.\d+/, '').length, '0') + str; str = repeatString(place - str.replace(/\.\d+/, '').length, '0') + str;
@ -304,6 +302,31 @@
} }
// Date helpers
function callDateGet(d, method) {
return d['get' + (d._utc ? 'UTC' : '') + method]();
}
function callDateSet(d, method, value) {
return d['set' + (d._utc && method != 'ISOWeek' ? 'UTC' : '') + method](value);
}
function extractDurationFromString(str) {
var match, val, unit;
match = str.toLowerCase().match(/^(\d+)?\s?(\w+?)s?$/i);
val = parseInt(match[1]) || 1;
unit = match[2].slice(0,1).toUpperCase() + match[2].slice(1);
if(unit.match(/hour|minute|second/i)) {
unit += 's';
} else if(unit === 'Year') {
unit = 'FullYear';
} else if(unit === 'Day') {
unit = 'Date';
}
return [val, unit];
}
// Specialized helpers // Specialized helpers
@ -798,7 +821,7 @@
* *
***/ ***/
'bind': function(scope) { 'bind': function(scope) {
var fn = this, args = multiArgs(arguments).slice(1), nop, bound; var fn = this, args = multiArgs(arguments).slice(1), bound;
if(!isFunction(this)) { if(!isFunction(this)) {
throw new TypeError('Function.prototype.bind called on a non-function'); throw new TypeError('Function.prototype.bind called on a non-function');
} }
@ -1651,11 +1674,11 @@
var arr = this; var arr = this;
var result = []; var result = [];
var divisor = ceil(this.length / num); var divisor = ceil(this.length / num);
getRange(0, num - 1, function(i) { simpleRepeat(num, function(i) {
var index = i * divisor; var index = i * divisor;
var group = arr.slice(index, index + divisor); var group = arr.slice(index, index + divisor);
if(pad && group.length < divisor) { if(pad && group.length < divisor) {
getRange(1, divisor - group.length, function() { simpleRepeat(divisor - group.length, function() {
group = group.add(padding); group = group.add(padding);
}); });
} }
@ -1680,7 +1703,7 @@
if(len === 0 || num === 0) return arr; if(len === 0 || num === 0) return arr;
if(isUndefined(num)) num = 1; if(isUndefined(num)) num = 1;
if(isUndefined(padding)) padding = null; if(isUndefined(padding)) padding = null;
getRange(0, ceil(len / num) - 1, function(i) { simpleRepeat(ceil(len / num), function(i) {
group = arr.slice(num * i, num * i + num); group = arr.slice(num * i, num * i + num);
while(group.length < num) { while(group.length < num) {
group.push(padding); group.push(padding);
@ -2224,6 +2247,7 @@
}, },
{ {
unit: 'month', unit: 'month',
error: 0.919, // Feb 1-28 over 1 month
method: 'Month', method: 'Month',
ambiguous: true, ambiguous: true,
multiplier: function(d, ms) { multiplier: function(d, ms) {
@ -2235,8 +2259,7 @@
} }
} }
return days * 24 * 60 * 60 * 1000; return days * 24 * 60 * 60 * 1000;
}, }
error: 0.919
}, },
{ {
unit: 'week', unit: 'week',
@ -2247,6 +2270,7 @@
}, },
{ {
unit: 'day', unit: 'day',
error: 0.958, // DST traversal over 1 day
method: 'Date', method: 'Date',
ambiguous: true, ambiguous: true,
multiplier: function() { multiplier: function() {
@ -2542,7 +2566,7 @@
if(loc['monthSuffix']) { if(loc['monthSuffix']) {
loc['month'] = getDigit(1,2); loc['month'] = getDigit(1,2);
loc['months'] = getRange(1, 12).map(function(n) { return n + loc['monthSuffix']; }); loc['months'] = '1,2,3,4,5,6,7,8,9,10,11,12'.split(',').map(function(n) { return n + loc['monthSuffix']; });
} }
loc['full_month'] = getDigit(1,2) + '|' + arrayToAlternates(loc['months']); loc['full_month'] = getDigit(1,2) + '|' + arrayToAlternates(loc['months']);
@ -2919,9 +2943,9 @@
return [value, unit, ms]; return [value, unit, ms];
} }
function getAdjustedUnitWithMonthFallback(date) { function getRelativeWithMonthFallback(date) {
var adu = getAdjustedUnit(date.millisecondsFromNow()); var adu = getAdjustedUnit(date.millisecondsFromNow());
if(adu[1] === 6) { if(allowMonthFallback(date, adu)) {
// If the adjusted unit is in months, then better to use // If the adjusted unit is in months, then better to use
// the "monthsfromNow" which applies a special error margin // the "monthsfromNow" which applies a special error margin
// for edge cases such as Jan-09 - Mar-09 being less than // for edge cases such as Jan-09 - Mar-09 being less than
@ -2929,10 +2953,18 @@
// The third "ms" element in the array will handle the sign // The third "ms" element in the array will handle the sign
// (past or future), so simply take the absolute value here. // (past or future), so simply take the absolute value here.
adu[0] = math.abs(date.monthsFromNow()); adu[0] = math.abs(date.monthsFromNow());
adu[1] = 6;
} }
return adu; return adu;
} }
function allowMonthFallback(date, adu) {
// Allow falling back to monthsFromNow if the unit is in months...
return adu[1] === 6 ||
// ...or if it's === 4 weeks and there are more days than in the given month
(adu[1] === 5 && adu[0] === 4 && date.daysFromNow() >= new Date().daysInMonth());
}
// Date formatting helpers // Date formatting helpers
@ -2943,11 +2975,11 @@
} else if(Date[format]) { } else if(Date[format]) {
format = Date[format]; format = Date[format];
} else if(isFunction(format)) { } else if(isFunction(format)) {
adu = getAdjustedUnitWithMonthFallback(date); adu = getRelativeWithMonthFallback(date);
format = format.apply(date, adu.concat(loc)); format = format.apply(date, adu.concat(loc));
} }
if(!format && relative) { if(!format && relative) {
adu = adu || getAdjustedUnitWithMonthFallback(date); adu = adu || getRelativeWithMonthFallback(date);
// Adjust up if time is in ms, as this doesn't // Adjust up if time is in ms, as this doesn't
// look very good for a standard relative date. // look very good for a standard relative date.
if(adu[1] === 0) { if(adu[1] === 0) {
@ -3132,14 +3164,6 @@
return d; return d;
} }
function callDateGet(d, method) {
return d['get' + (d._utc ? 'UTC' : '') + method]();
}
function callDateSet(d, method, value) {
return d['set' + (d._utc && method != 'ISOWeek' ? 'UTC' : '') + method](value);
}
// The ISO format allows times strung together without a demarcating ":", so make sure // The ISO format allows times strung together without a demarcating ":", so make sure
// that these markers are now optional. // that these markers are now optional.
function prepareTime(format, loc, iso) { function prepareTime(format, loc, iso) {
@ -3418,7 +3442,7 @@
if(fraction && math.abs(fraction % 1) > error) { if(fraction && math.abs(fraction % 1) > error) {
num = round(num); num = round(num);
} }
return parseInt(num); return num < 0 ? ceil(num) : floor(num);
} }
since = function(f, localeCode) { since = function(f, localeCode) {
return applyErrorMargin(this.getTime() - date.create(f, localeCode).getTime()); return applyErrorMargin(this.getTime() - date.create(f, localeCode).getTime());
@ -4374,77 +4398,132 @@
setDateProperties(); setDateProperties();
/*** /***
* @package DateRange * @package Range
* @dependency date * @dependency core
* @description Date Ranges define a range of time. They can enumerate over specific points within that range, and be manipulated and compared. * @description Ranges allow creating spans of numbers, strings, or dates. They can enumerate over specific points within that range, and be manipulated and compared.
* *
***/ ***/
var DateRange = function(start, end) { function Range(start, end) {
this.start = date.create(start); this.start = cloneRangeMember(start);
this.end = date.create(end); this.end = cloneRangeMember(end);
}; };
// 'toString' doesn't appear in a for..in loop in IE even though function getRangeMemberNumericValue(m) {
return isString(m) ? m.charCodeAt(0) : m;
}
function getRangeMemberPrimitiveValue(m) {
if(m == null) return m;
return isDate(m) ? m.getTime() : m.valueOf();
}
function cloneRangeMember(m) {
if(isDate(m)) {
return new date(m.getTime());
} else {
return getRangeMemberPrimitiveValue(m);
}
}
function isValidRangeMember(m) {
var val = getRangeMemberPrimitiveValue(m);
return !!val || val === 0;
}
function incrementRangeMember(obj, increment) {
if(isDate(obj)) {
return advanceDate(obj, increment);
} else if(isString(obj)) {
return string.fromCharCode(m.charCodeAt(0) + increment);
} else if(isNumber(obj)) {
return obj + increment;
}
}
function advanceDate(current, increment) {
var unit, amt = increment, tmp, val, d;
if(isString(increment)) {
tmp = extractDurationFromString(increment);
amt = tmp[0];
unit = tmp[1];
val = callDateGet(current, unit);
d = new date(current.getTime());
callDateSet(d, unit, val + amt);
return d;
} else {
return new date(current.getTime() + increment);
}
}
/***
* @method toString()
* @returns String
* @short Returns a string representation of the range.
* @example
*
* Number.range(1, 5).toString() -> 1..5
* Date.range(new Date(2003, 0), new Date(2005, 0)).toString() -> January 1, 2003..January 1, 2005
*
***/
// Note: 'toString' doesn't appear in a for..in loop in IE even though
// hasOwnProperty reports true, so extend() can't be used here. // hasOwnProperty reports true, so extend() can't be used here.
// Also tried simply setting the prototype = {} up front for all // Also tried simply setting the prototype = {} up front for all
// methods but GCC very oddly started dropping properties in the // methods but GCC very oddly started dropping properties in the
// object randomly (maybe because of the global scope?) hence // object randomly (maybe because of the global scope?) hence
// the need for the split logic here. // the need for the split logic here.
DateRange.prototype.toString = function() { Range.prototype.toString = function() {
/*** return this.isValid() ? this.start + ".." + this.end : 'Invalid Range';
* @method toString()
* @returns String
* @short Returns a string representation of the DateRange.
* @example
*
* Date.range('2003', '2005').toString() -> January 1, 2003..January 1, 2005
*
***/
return this.isValid() ? this.start.full() + '..' + this.end.full() : 'Invalid DateRange';
}; };
extend(DateRange, true, false, { extend(Range, true, false, {
/*** /***
* @method isValid() * @method isValid()
* @returns Boolean * @returns Boolean
* @short Returns true if the DateRange is valid, false otherwise. * @short Returns true if the range is valid, false otherwise.
* @example * @example
* *
* Date.range('2003', '2005').isValid() -> true * Date.range(new Date(2003, 0), new Date(2005, 0)).isValid() -> true
* Date.range('2005', '2003').isValid() -> false * Number.range(NaN, NaN).isValid() -> false
* *
***/ ***/
'isValid': function() { 'isValid': function() {
return this.start < this.end; return isValidRangeMember(this.start) && isValidRangeMember(this.end) && typeof this.start === typeof this.end;
}, },
/*** /***
* @method duration() * @method span()
* @returns Number * @returns Number
* @short Return the duration of the DateRange in milliseconds. * @short Return the span of the range. If the range is a date range, the value is in milliseconds.
* @extra The span includes both the start and the end.
* @example * @example
* *
* Date.range('2003', '2005').duration() -> 94694400000 * Number.range(5, 10).span() -> 6
* Date.range(new Date(2003, 0), new Date(2005, 0)).span() -> 94694400000
* *
***/ ***/
'duration': function() { 'span': function() {
return this.isValid() ? this.end.getTime() - this.start.getTime() : NaN; return this.isValid() ? getRangeMemberNumericValue(this.end) - getRangeMemberNumericValue(this.start) + 1 : NaN;
}, },
/*** /***
* @method contains(<d>) * @method contains(<obj>)
* @returns Boolean * @returns Boolean
* @short Returns true if <d> is contained inside the DateRange. <d> may be a date or another DateRange. * @short Returns true if <obj> is contained inside the range. <obj> may be a value or another range.
* @example * @example
* *
* Date.range('2003', '2005').contains(Date.create('2004')) -> true * Number.range(5, 10).contains(7) -> true
* Date.range(new Date(2003, 0), new Date(2005, 0)).contains(new Date(2004, 0)) -> true
* *
***/ ***/
'contains': function(obj) { 'contains': function(obj) {
var self = this, arr = obj.start && obj.end ? [obj.start, obj.end] : [obj]; var self = this, arr;
if(obj == null) return false;
arr = obj.start && obj.end ? [obj.start, obj.end] : [obj];
return arr.every(function(d) { return arr.every(function(d) {
return d >= self.start && d <= self.end; return d >= self.start && d <= self.end;
}); });
@ -4453,37 +4532,36 @@
/*** /***
* @method every(<increment>, [fn]) * @method every(<increment>, [fn])
* @returns Array * @returns Array
* @short Iterates through the DateRange for every <increment>, calling [fn] if it is passed. Returns an array of each increment visited. * @short Iterates through the range for every <increment>, calling [fn] if it is passed. Returns an array of each increment visited.
* @extra When <increment> is a number, increments will be to the exact millisecond. <increment> can also be a string in the format %{number} {unit}s%, in which case it will increment in the unit specified. Note that a discrepancy exists in the case of months, as %(2).months()% is an approximation. Stepping through the actual months by passing %"2 months"% is usually preferable in this case. * @extra When <increment> is a number, increments will be to the exact millisecond. In the case of date ranges, <increment> can also be a string in the format %{number} {unit}s%, in which case it will increment in the unit specified. Note that a discrepancy exists in the case of months, as %(2).months()% is an approximation. Stepping through the actual months by passing %"2 months"% is usually preferable in this case.
* @example * @example
* *
* Date.range('2003-01', '2003-03').every("2 months") -> [...] * Number.range(2, 8).every(2) -> [2,4,6,8]
* Date.range(new Date(2003, 1), new Date(2003,3)).every("2 months") -> [...]
* *
***/ ***/
'every': function(increment, fn) { 'every': function(increment, fn) {
var current = this.start.clone(), result = [], index = 0, params, isDay; var start = this.start,
if(isString(increment)) { end = this.end,
current.advance(getDateParamsFromString(increment, 0), true); inverse = end < start,
params = getDateParamsFromString(increment); current = start,
isDay = increment.toLowerCase() === 'day'; index = 0,
} else { result = [];
params = { 'milliseconds': increment }; if(isFunction(increment)) {
} fn = increment;
while(current <= this.end) { increment = null;
}
increment = increment || 1;
// Avoiding infinite loops
if(inverse && increment > 0) {
increment *= -1;
}
while(inverse ? current >= end : current <= end) {
result.push(current); result.push(current);
if(fn) fn(current, index); if(fn) {
if(isDay && callDateGet(current, 'Hours') === 23) { fn(current, index);
// When DST traversal happens at 00:00 hours, the time is effectively
// pushed back to 23:00, meaning 1) 00:00 for that day does not exist,
// and 2) there is no difference between 23:00 and 00:00, as you are
// "jumping" around in time. Hours here will be reset before the date
// is advanced and the date will never in fact advance, so set the hours
// directly ahead to the next day to avoid this problem.
current = current.clone();
callDateSet(current, 'Hours', 48);
} else {
current = current.clone().advance(params, true);
} }
current = incrementRangeMember(current, increment);
index++; index++;
} }
return result; return result;
@ -4491,15 +4569,16 @@
/*** /***
* @method union(<range>) * @method union(<range>)
* @returns DateRange * @returns Range
* @short Returns a new DateRange with the earliest starting point as its start, and the latest ending point as its end. If the two ranges do not intersect this will effectively remove the "gap" between them. * @short Returns a new range with the earliest starting point as its start, and the latest ending point as its end. If the two ranges do not intersect this will effectively remove the "gap" between them.
* @example * @example
* *
* Date.range('2003=01', '2005-01').union(Date.range('2004-01', '2006-01')) -> Jan 1, 2003..Jan 1, 2006 * Number.range(1, 3).union(Number.range(2, 5) -> 1..5
* Date.range(new Date(2003, 1), new Date(2005, 1)).union(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2003..Jan 1, 2006
* *
***/ ***/
'union': function(range) { 'union': function(range) {
return new DateRange( return new Range(
this.start < range.start ? this.start : range.start, this.start < range.start ? this.start : range.start,
this.end > range.end ? this.end : range.end this.end > range.end ? this.end : range.end
); );
@ -4507,15 +4586,19 @@
/*** /***
* @method intersect(<range>) * @method intersect(<range>)
* @returns DateRange * @returns Range
* @short Returns a new DateRange with the latest starting point as its start, and the earliest ending point as its end. If the two ranges do not intersect this will effectively produce an invalid range. * @short Returns a new range with the latest starting point as its start, and the earliest ending point as its end. If the two ranges do not intersect this will effectively produce an invalid range.
* @example * @example
* *
* Date.range('2003-01', '2005-01').intersect(Date.range('2004-01', '2006-01')) -> Jan 1, 2004..Jan 1, 2005 * Number.range(1, 5).intersect(Number.range(4, 8) -> 4..5
* Date.range(new Date(2003, 1), new Date(2005, 1)).intersect(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2004..Jan 1, 2005
* *
***/ ***/
'intersect': function(range) { 'intersect': function(range) {
return new DateRange( if(range.start > this.end || range.end < this.start) {
return new Range(NaN, NaN);
}
return new Range(
this.start > range.start ? this.start : range.start, this.start > range.start ? this.start : range.start,
this.end < range.end ? this.end : range.end this.end < range.end ? this.end : range.end
); );
@ -4523,60 +4606,161 @@
/*** /***
* @method clone() * @method clone()
* @returns DateRange * @returns Range
* @short Clones the DateRange. * @short Clones the range.
* @extra Members of the range will also be cloned.
* @example * @example
* *
* Date.range('2003-01', '2005-01').intersect(Date.range('2004-01', '2006-01')) -> Jan 1, 2004..Jan 1, 2005 * Number.range(1, 5).clone() -> Returns a copy of the range.
* *
***/ ***/
'clone': function(range) { 'clone': function(range) {
return new DateRange(this.start, this.end); return new Range(this.start, this.end);
},
/***
* @method clamp(<obj>)
* @returns Mixed
* @short Clamps <obj> to be within the range if it falls outside.
* @example
*
* Number.range(1, 5).clamp(8) -> 5
* Date.range(new Date(2010, 0), new Date(2012, 0)).clamp(new Date(2013, 0)) -> 2012-01
*
***/
'clamp': function(obj) {
var clamped,
start = this.start,
end = this.end,
min = end < start ? end : start,
max = start > end ? start : end;
if(obj < min) {
clamped = min;
} else if(obj > max) {
clamped = max;
} else {
clamped = obj;
}
return cloneRangeMember(clamped);
} }
}); });
extend(Range, true, false, {
/***
* @method step()
* @returns Array
* @short Alias for %every%.
*
* @example
*
+ range.step(fn); -> iterates over every step
+ range.step(2, fn); -> iterates over every 2 steps
*
***/
'step': Range.prototype.every
});
/*** /***
* @method each[Unit]([fn]) * Date module
* @returns Date
* @short Increments through the date range for each [unit], calling [fn] if it is passed. Returns an array of each increment visited.
*
* @set
* eachMillisecond
* eachSecond
* eachMinute
* eachHour
* eachDay
* eachWeek
* eachMonth
* eachYear
*
* @example
*
* Date.range('2003-01', '2003-02').eachMonth() -> [...]
* Date.range('2003-01-15', '2003-01-16').eachDay() -> [...]
*
***/ ***/
extendSimilar(DateRange, true, false, 'Millisecond,Second,Minute,Hour,Day,Week,Month,Year', function(methods, name) {
methods['each' + name] = function(fn) { return this.every(name, fn); }
});
[number, string, date].forEach(function(klass) {
extend(klass, false, false, {
/***
* @method Number.range([start], [end])
* @returns Range
* @short Creates a new range between [start] and [end].
*
***
* @method String.range([start], [end])
* @returns Range
* @short Creates a new range between [start] and [end].
*
***
* @method Date.range([start], [end])
* @returns Range
* @short Creates a new range between [start] and [end].
* @extra If either [start] or [end] are null, they will default to the current date.
*
***/
'range': function(start, end) {
if(klass.create) {
start = klass.create(start);
end = klass.create(end);
}
return new Range(start, end);
}
});
});
/*** /***
* Date module * Number module
*
***/ ***/
extend(date, false, false, { var numberRangeStep = function(num, fn, step) {
return number.range(this, num).step(step, fn);
};
number.extend({
/***
* @method upto(<num>, [fn], [step] = 1)
* @returns Array
* @short Returns an array containing numbers from the number up to <num>.
* @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
* @example
*
* (2).upto(6) -> [2, 3, 4, 5, 6]
* (2).upto(6, function(n) {
* // This function is called 5 times receiving n as the value.
* });
* (2).upto(8, null, 2) -> [2, 4, 6, 8]
*
***/
'upto': numberRangeStep,
/***
* @method downto(<num>, [fn], [step] = 1)
* @returns Array
* @short Returns an array containing numbers from the number down to <num>.
* @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
* @example
*
* (8).downto(3) -> [8, 7, 6, 5, 4, 3]
* (8).downto(3, function(n) {
* // This function is called 6 times receiving n as the value.
* });
* (8).downto(2, null, 2) -> [8, 6, 4, 2]
*
***/
'downto': numberRangeStep,
/*** /***
* @method Date.range([start], [end]) * @method clamp([start], [end])
* @returns DateRange * @returns Number
* @short Creates a new date range. * @short Constrains the number so that it is between [start] and [end].
* @extra If either [start] or [end] are null, they will default to the current date. * @extra This alias will build a range object that can be accessed directly using %Number.range% and has an equivalent %clamp% method.
* *
***/ ***/
'range': function(start, end) { 'clamp': function(start, end) {
return new DateRange(start, end); return new Range(start, end).clamp(this);
}
});
extend(array, false, function(a) { return a instanceof Range; }, {
'create': function(range) {
return range.step();
} }
}); });
@ -5044,42 +5228,6 @@
return this.pad(pad || 1, false, 16); return this.pad(pad || 1, false, 16);
}, },
/***
* @method upto(<num>, [fn], [step] = 1)
* @returns Array
* @short Returns an array containing numbers from the number up to <num>.
* @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
* @example
*
* (2).upto(6) -> [2, 3, 4, 5, 6]
* (2).upto(6, function(n) {
* // This function is called 5 times receiving n as the value.
* });
* (2).upto(8, null, 2) -> [2, 4, 6, 8]
*
***/
'upto': function(num, fn, step) {
return getRange(this, num, fn, step || 1);
},
/***
* @method downto(<num>, [fn], [step] = 1)
* @returns Array
* @short Returns an array containing numbers from the number down to <num>.
* @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
* @example
*
* (8).downto(3) -> [8, 7, 6, 5, 4, 3]
* (8).downto(3, function(n) {
* // This function is called 6 times receiving n as the value.
* });
* (8).downto(2, null, 2) -> [8, 6, 4, 2]
*
***/
'downto': function(num, fn, step) {
return getRange(this, num, fn, -(step || 1));
},
/*** /***
* @method times(<fn>) * @method times(<fn>)
* @returns Number * @returns Number
@ -5599,7 +5747,7 @@
}, },
/*** /***
* @method Object.toQueryString(<obj>, [namespace] = true) * @method Object.toQueryString(<obj>, [namespace] = null)
* @returns Object * @returns Object
* @short Converts the object into a query string. * @short Converts the object into a query string.
* @extra Accepts deep nested objects and arrays. If [namespace] is passed, it will be prefixed to all param names. * @extra Accepts deep nested objects and arrays. If [namespace] is passed, it will be prefixed to all param names.
@ -6019,7 +6167,7 @@
* @method encodeBase64() * @method encodeBase64()
* @returns String * @returns String
* @short Encodes the string into base64 encoding. * @short Encodes the string into base64 encoding.
* @extra This method wraps the browser native %btoa% when available, and uses a custom implementation when not available. * @extra This method wraps the browser native %btoa% when available, and uses a custom implementation when not available. It can also handle Unicode string encodings.
* @example * @example
* *
* 'gonna get encoded!'.encodeBase64() -> 'Z29ubmEgZ2V0IGVuY29kZWQh' * 'gonna get encoded!'.encodeBase64() -> 'Z29ubmEgZ2V0IGVuY29kZWQh'
@ -6027,14 +6175,14 @@
* *
***/ ***/
'encodeBase64': function() { 'encodeBase64': function() {
return btoa(this); return btoa(unescape(encodeURIComponent(this)));
}, },
/*** /***
* @method decodeBase64() * @method decodeBase64()
* @returns String * @returns String
* @short Decodes the string from base64 encoding. * @short Decodes the string from base64 encoding.
* @extra This method wraps the browser native %atob% when available, and uses a custom implementation when not available. * @extra This method wraps the browser native %atob% when available, and uses a custom implementation when not available. It can also handle Unicode string encodings.
* @example * @example
* *
* 'aHR0cDovL3R3aXR0ZXIuY29tLw=='.decodeBase64() -> 'http://twitter.com/' * 'aHR0cDovL3R3aXR0ZXIuY29tLw=='.decodeBase64() -> 'http://twitter.com/'
@ -6042,7 +6190,7 @@
* *
***/ ***/
'decodeBase64': function() { 'decodeBase64': function() {
return atob(this); return decodeURIComponent(escape(atob(this)));
}, },
/*** /***
@ -7220,7 +7368,8 @@
'hankaku': {} 'hankaku': {}
}; };
widthConversionRanges.forEach(function(r) { widthConversionRanges.forEach(function(r) {
getRange(r.start, r.end, function(n) { simpleRepeat(r.end - r.start + 1, function(n) {
n += r.start;
setWidthConversion(r.type, chr(n), chr(n + r.shift)); setWidthConversion(r.type, chr(n), chr(n + r.shift));
}); });
}); });

@ -1,12 +1,13 @@
// Underscore.js 1.4.4 // Underscore.js 1.4.4
// http://underscorejs.org // ===================
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
(function() { // > http://underscorejs.org
// > (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// > Underscore may be freely distributed under the MIT license.
// Baseline setup // Baseline setup
// -------------- // --------------
(function() {
// Establish the root object, `window` in the browser, or `global` on the server. // Establish the root object, `window` in the browser, or `global` on the server.
var root = this; var root = this;

@ -10,9 +10,6 @@ define(['app', 'Missing/MissingItemView'], function () {
pager: '.x-missing-table-pager' pager: '.x-missing-table-pager'
}, },
initialize : function (context, action, query, collection) {
this.collection = collection;
},
onCompositeCollectionRendered: function () { onCompositeCollectionRendered: function () {
this.ui.table.trigger('update'); this.ui.table.trigger('update');

@ -17,7 +17,7 @@ Marionette.TemplateCache.get = function (templateId) {
return templateFunction(data); return templateFunction(data);
} }
catch (error) { catch (error) {
console.error('template render failed for ' + templateKey + ' ' + error.message); console.error('template render failed for ' + templateKey + ' ' + error);
console.error(data); console.error(data);
} }
}; };

@ -1,10 +1,7 @@
'use strict'; 'use strict';
define([ define([
'app', 'Settings/SettingsModel' 'app', 'Settings/SettingsModel'
], function () { ], function () {
NzbDrone.Settings.Quality.QualityView = Backbone.Marionette.ItemView.extend({ NzbDrone.Settings.Quality.QualityView = Backbone.Marionette.ItemView.extend({
template : 'Settings/Quality/QualityTemplate', template : 'Settings/Quality/QualityTemplate',
className: 'form-horizontal', className: 'form-horizontal',
@ -12,6 +9,6 @@ define([
initialize: function (options) { initialize: function (options) {
this.qualityProfileCollection = options.qualityProfiles; this.qualityProfileCollection = options.qualityProfiles;
this.model.set({ qualityProfiles: this.qualityProfileCollection }); this.model.set({ qualityProfiles: this.qualityProfileCollection });
}, }
}); });
}); });

@ -1,4 +1,5 @@
define([ "use strict";
define([
'app', 'app',
'Settings/Naming/NamingView', 'Settings/Naming/NamingView',
'Settings/Quality/QualityLayout', 'Settings/Quality/QualityLayout',
@ -8,7 +9,7 @@
'Settings/System/SystemView', 'Settings/System/SystemView',
'Settings/Misc/MiscView' 'Settings/Misc/MiscView'
], ],
function (app) { function () {
NzbDrone.Settings.SettingsLayout = Backbone.Marionette.Layout.extend({ NzbDrone.Settings.SettingsLayout = Backbone.Marionette.Layout.extend({
template: 'Settings/SettingsLayoutTemplate', template: 'Settings/SettingsLayoutTemplate',
@ -106,15 +107,10 @@
NzbDrone.Router.navigate('settings/misc'); NzbDrone.Router.navigate('settings/misc');
}, },
initialize: function (context, action, query, settings) { initialize: function (options) {
this.settings = settings; this.settings = options.settings;
if (options.action) {
if (action) { this.action = options.action.toLowerCase();
this.action = action.toLowerCase();
}
if (query) {
this.query = query.toLowerCase();
} }
}, },

Loading…
Cancel
Save