Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

backbone.stickit

Package Overview
Dependencies
Maintainers
2
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

backbone.stickit - npm Package Compare versions

Comparing version 0.8.0 to 0.9.0

.travis.yml

404

backbone.stickit.js

@@ -1,3 +0,3 @@

// Backbone.Stickit v0.8.0, MIT Licensed
// Copyright (c) 2012 The New York Times, CMS Group, Matthew DeLambo <delambo@gmail.com>
// Backbone.Stickit v0.9.0, MIT Licensed
// Copyright (c) 2012-2015 The New York Times, CMS Group, Matthew DeLambo <delambo@gmail.com>

@@ -7,15 +7,12 @@ (function (factory) {

// Set up Stickit appropriately for the environment. Start with AMD.
if (typeof define === 'function' && define.amd) {
if (typeof define === 'function' && define.amd)
define(['underscore', 'backbone', 'exports'], factory);
}
// Next for Node.js or CommonJS.
else if (typeof exports === 'object') {
else if (typeof exports === 'object')
factory(require('underscore'), require('backbone'), exports);
}
// Finally, as a browser global.
else {
else
factory(_, Backbone, {});
}

@@ -27,2 +24,5 @@ }(function (_, Backbone, Stickit) {

// Export onto Backbone object
Backbone.Stickit = Stickit;
Stickit._handlers = [];

@@ -33,7 +33,7 @@

handlers = _.map(_.flatten([handlers]), function(handler) {
return _.extend({
return _.defaults({}, handler, {
updateModel: true,
updateView: true,
updateMethod: 'text'
}, handler);
});
});

@@ -56,5 +56,6 @@ this._handlers = this._handlers.concat(handlers);

unstickit: function(model, bindingSelector) {
// Support bindings hash in place of selector.
// Support passing a bindings hash in place of bindingSelector.
if (_.isObject(bindingSelector)) {
_.each(_.keys(bindingSelector), function(selector) {
_.each(bindingSelector, function(v, selector) {
this.unstickit(model, selector);

@@ -66,17 +67,17 @@ }, this);

var models = [], destroyFns = [];
_.each(this._modelBindings, function(binding, i) {
if (model && binding.model !== model) { return; }
this._modelBindings = _.reject(this._modelBindings, function(binding) {
if (model && binding.model !== model) return;
if (bindingSelector && binding.config.selector != bindingSelector) return;
binding.model.off(binding.event, binding.fn);
destroyFns.push(binding.config._destroy);
binding.model.off(binding.event, binding.fn);
models.push(binding.model);
delete this._modelBindings[i];
}, this);
return true;
});
// Trigger an event for each model that was unbound.
_.invoke(_.uniq(models), 'trigger', 'stickit:unstuck', this.cid);
// Call `_destroy` on a unique list of the binding callbacks.
_.each(_.uniq(destroyFns), function(fn) { fn.call(this); }, this);
// Cleanup the null values.
this._modelBindings = _.compact(this._modelBindings);

@@ -86,4 +87,5 @@ this.$el.off('.stickit' + (model ? '.' + model.cid : ''), bindingSelector);

// Using `this.bindings` configuration or the `optionalBindingsConfig`, binds `this.model`
// or the `optionalModel` to elements in the view.
// Initilize Stickit bindings for the view. Subsequent binding additions
// can either call `stickit` with the new bindings, or add them directly
// with `addBinding`. Both arguments to `stickit` are optional.
stickit: function(optionalModel, optionalBindingsConfig) {

@@ -95,4 +97,3 @@ var model = optionalModel || this.model,

// Iterate through the selectors in the bindings configuration and configure
// the various options for each field.
// Add bindings in bulk using `addBinding`.
this.addBinding(model, bindings);

@@ -111,19 +112,18 @@

this.remove.stickitWrapped = true;
return this;
},
// Add a single model binding to the view
addBinding: function(optionalModel, second, _binding) {
var $el, options, modelAttr, config, selector,
model = optionalModel || this.model,
namespace = '.stickit.' + model.cid,
binding = _binding || {},
bindId = _.uniqueId();
// Add a single Stickit binding or a hash of bindings to the model. If
// `optionalModel` is ommitted, will default to the view's `model` property.
addBinding: function(optionalModel, selector, binding) {
var model = optionalModel || this.model,
namespace = '.stickit.' + model.cid;
// Allow jQuery-style {key: val} event maps
if (_.isString(second)) {
selector = second;
} else {
var bindings = second;
_.each(bindings, function(v, selector) {
this.addBinding(model, selector, bindings[selector]);
binding = binding || {};
// Support jQuery-style {key: val} event maps.
if (_.isObject(selector)) {
var bindings = selector;
_.each(bindings, function(val, key) {
this.addBinding(model, key, val);
}, this);

@@ -133,5 +133,6 @@ return;

// Support ':el' selector - special case selector for the view managed delegate.
$el = selector === ':el' ? this.$el : this.$(selector);
// Special case the ':el' selector to use the view's this.$el.
var $el = selector === ':el' ? this.$el : this.$(selector);
// Clear any previous matching bindings.
this.unstickit(model, selector);

@@ -143,3 +144,3 @@

// Allow shorthand setting of model attributes - `'selector':'observe'`.
if (_.isString(binding)) binding = {observe:binding};
if (_.isString(binding)) binding = {observe: binding};

@@ -149,13 +150,19 @@ // Handle case where `observe` is in the form of a function.

config = getConfiguration($el, binding);
// Find all matching Stickit handlers that could apply to this element
// and store in a config object.
var config = getConfiguration($el, binding);
// The attribute we're observing in our config.
var modelAttr = config.observe;
// Store needed properties for later.
config.selector = selector;
modelAttr = config.observe;
config.view = this;
// Create the model set options with a unique `bindId` so that we
// can avoid double-binding in the `change:attribute` event handler.
config.bindId = bindId;
var bindId = config.bindId = _.uniqueId();
// Add a reference to the view for handlers of stickitChange events
config.view = this;
options = _.extend({stickitChange:config}, config.setOptions);
var options = _.extend({stickitChange: config}, config.setOptions);

@@ -165,22 +172,22 @@ // Add a `_destroy` callback to the configuration, in case `destroy`

config._destroy = function() {
applyViewFn(this, config.destroy, $el, model, config);
applyViewFn.call(this, config.destroy, $el, model, config);
};
initializeAttributes(this, $el, config, model, modelAttr);
initializeAttributes($el, config, model, modelAttr);
initializeVisible($el, config, model, modelAttr);
initializeClasses($el, config, model, modelAttr);
initializeVisible(this, $el, config, model, modelAttr);
if (modelAttr) {
// Setup one-way, form element to model, bindings.
// Setup one-way (input element -> model) bindings.
_.each(config.events, function(type) {
var event = type + namespace;
var method = function(event) {
var val = config.getVal.call(this, $el, event, config, _.rest(arguments));
var eventName = type + namespace;
var listener = function(event) {
var val = applyViewFn.call(this, config.getVal, $el, event, config, slice.call(arguments, 1));
// Don't update the model if false is returned from the `updateModel` configuration.
if (evaluateBoolean(this, config.updateModel, val, event, config))
setAttr(model, modelAttr, val, options, this, config);
var currentVal = evaluateBoolean(config.updateModel, val, event, config);
if (currentVal) setAttr(model, modelAttr, val, options, config);
};
method = _.bind(method, this);
if (selector === ':el') this.$el.on(event, method);
else this.$el.on(event, selector, method);
var sel = selector === ':el'? '' : selector;
this.$el.on(eventName, sel, _.bind(listener, this));
}, this);

@@ -191,14 +198,17 @@

_.each(_.flatten([modelAttr]), function(attr) {
observeModelEvent(model, this, 'change:'+attr, config, function(model, val, options) {
var changeId = options && options.stickitChange && options.stickitChange.bindId || null;
if (changeId !== bindId)
updateViewBindEl(this, $el, config, getAttr(model, modelAttr, config, this), model);
observeModelEvent(model, 'change:' + attr, config, function(m, val, options) {
var changeId = options && options.stickitChange && options.stickitChange.bindId;
if (changeId !== bindId) {
var currentVal = getAttr(model, modelAttr, config);
updateViewBindEl($el, config, currentVal, model);
}
});
}, this);
});
updateViewBindEl(this, $el, config, getAttr(model, modelAttr, config, this), model, true);
var currentVal = getAttr(model, modelAttr, config);
updateViewBindEl($el, config, currentVal, model, true);
}
// After each binding is setup, call the `initialize` callback.
applyViewFn(this, config.initialize, $el, model, config);
applyViewFn.call(this, config.initialize, $el, model, config);
}

@@ -212,2 +222,4 @@ };

var slice = [].slice;
// Evaluates the given `path` (in object/dot-notation) relative to the given

@@ -223,14 +235,18 @@ // `obj`. If the path is null/undefined, then the given `obj` is returned.

// a function that should be executed.
var applyViewFn = function(view, fn) {
if (fn) return (_.isString(fn) ? evaluatePath(view,fn) : fn).apply(view, _.rest(arguments, 2));
var applyViewFn = function(fn) {
fn = _.isString(fn) ? evaluatePath(this, fn) : fn;
if (fn) return (fn).apply(this, slice.call(arguments, 1));
};
var getSelectedOption = function($select) { return $select.find('option').not(function(){ return !this.selected; }); };
// Given a function, string (view function reference), or a boolean
// value, returns the truthy result. Any other types evaluate as false.
var evaluateBoolean = function(view, reference) {
if (_.isBoolean(reference)) return reference;
else if (_.isFunction(reference) || _.isString(reference))
return applyViewFn.apply(this, arguments);
// The first argument must be `reference` and the last must be `config`, but
// middle arguments can be variadic.
var evaluateBoolean = function(reference, val, config) {
if (_.isBoolean(reference)) {
return reference;
} else if (_.isFunction(reference) || _.isString(reference)) {
var view = _.last(arguments).view;
return applyViewFn.apply(view, arguments);
}
return false;

@@ -241,3 +257,4 @@ };

// in the view's _modelBindings.
var observeModelEvent = function(model, view, event, config, fn) {
var observeModelEvent = function(model, event, config, fn) {
var view = config.view;
model.on(event, fn, view);

@@ -248,10 +265,11 @@ view._modelBindings.push({model:model, event:event, fn:fn, config:config});

// Prepares the given `val`ue and sets it into the `model`.
var setAttr = function(model, attr, val, options, context, config) {
var value = {};
if (config.onSet)
val = applyViewFn(context, config.onSet, val, config);
var setAttr = function(model, attr, val, options, config) {
var value = {}, view = config.view;
if (config.onSet) {
val = applyViewFn.call(view, config.onSet, val, config);
}
if (config.set)
applyViewFn(context, config.set, attr, val, options, config);
else {
if (config.set) {
applyViewFn.call(view, config.set, attr, val, options, config);
} else {
value[attr] = val;

@@ -273,12 +291,12 @@ // If `observe` is defined as an array and `onSet` returned

// respective values will be returned.
var getAttr = function(model, attr, config, context) {
var val,
retrieveVal = function(field) {
return model[config.escape ? 'escape' : 'get'](field);
},
sanitizeVal = function(val) {
return val == null ? '' : val;
};
val = _.isArray(attr) ? _.map(attr, retrieveVal) : retrieveVal(attr);
if (config.onGet) val = applyViewFn(context, config.onGet, val, config);
var getAttr = function(model, attr, config) {
var view = config.view;
var retrieveVal = function(field) {
return model[config.escape ? 'escape' : 'get'](field);
};
var sanitizeVal = function(val) {
return val == null ? '' : val;
};
var val = _.isArray(attr) ? _.map(attr, retrieveVal) : retrieveVal(attr);
if (config.onGet) val = applyViewFn.call(view, config.onGet, val, config);
return _.isArray(val) ? _.map(val, sanitizeVal) : sanitizeVal(val);

@@ -301,7 +319,9 @@ };

handlers.push(binding);
// Merge handlers into a single config object. Last props in wins.
var config = _.extend.apply(_, handlers);
// `updateView` is defaulted to false for configutrations with
// `visible`; otherwise, `updateView` is defaulted to true.
if (config.visible && !_.has(config, 'updateView')) config.updateView = false;
else if (!_.has(config, 'updateView')) config.updateView = true;
if (!_.has(config, 'updateView')) config.updateView = !config.visible;
return config;

@@ -320,12 +340,19 @@ };

//
var initializeAttributes = function(view, $el, config, model, modelAttr) {
var props = ['autofocus', 'autoplay', 'async', 'checked', 'controls', 'defer', 'disabled', 'hidden', 'indeterminate', 'loop', 'multiple', 'open', 'readonly', 'required', 'scoped', 'selected'];
var initializeAttributes = function($el, config, model, modelAttr) {
var props = ['autofocus', 'autoplay', 'async', 'checked', 'controls',
'defer', 'disabled', 'hidden', 'indeterminate', 'loop', 'multiple',
'open', 'readonly', 'required', 'scoped', 'selected'];
var view = config.view;
_.each(config.attributes || [], function(attrConfig) {
var lastClass = '', observed, updateAttr;
attrConfig = _.clone(attrConfig);
observed = attrConfig.observe || (attrConfig.observe = modelAttr),
updateAttr = function() {
var updateType = _.indexOf(props, attrConfig.name, true) > -1 ? 'prop' : 'attr',
val = getAttr(model, observed, attrConfig, view);
attrConfig.view = view;
var lastClass = '';
var observed = attrConfig.observe || (attrConfig.observe = modelAttr);
var updateAttr = function() {
var updateType = _.contains(props, attrConfig.name) ? 'prop' : 'attr',
val = getAttr(model, observed, attrConfig);
// If it is a class then we need to remove the last value and add the new.

@@ -335,8 +362,12 @@ if (attrConfig.name === 'class') {

lastClass = val;
} else {
$el[updateType](attrConfig.name, val);
}
else $el[updateType](attrConfig.name, val);
};
_.each(_.flatten([observed]), function(attr) {
observeModelEvent(model, view, 'change:' + attr, config, updateAttr);
observeModelEvent(model, 'change:' + attr, config, updateAttr);
});
// Initialize the matched element's state.
updateAttr();

@@ -346,2 +377,20 @@ });

var initializeClasses = function($el, config, model, modelAttr) {
_.each(config.classes || [], function(classConfig, name) {
if (_.isString(classConfig)) classConfig = {observe: classConfig};
classConfig.view = config.view;
var observed = classConfig.observe;
var updateClass = function() {
var val = getAttr(model, observed, classConfig);
$el.toggleClass(name, !!val);
};
_.each(_.flatten([observed]), function(attr) {
observeModelEvent(model, 'change:' + attr, config, updateClass);
});
updateClass();
});
};
// If `visible` is configured, then the view element will be shown/hidden

@@ -356,20 +405,29 @@ // based on the truthiness of the modelattr's value or the result of the

//
var initializeVisible = function(view, $el, config, model, modelAttr) {
var initializeVisible = function($el, config, model, modelAttr) {
if (config.visible == null) return;
var view = config.view;
var visibleCb = function() {
var visible = config.visible,
visibleFn = config.visibleFn,
val = getAttr(model, modelAttr, config, view),
val = getAttr(model, modelAttr, config),
isVisible = !!val;
// If `visible` is a function then it should return a boolean result to show/hide.
if (_.isFunction(visible) || _.isString(visible)) isVisible = !!applyViewFn(view, visible, val, config);
if (_.isFunction(visible) || _.isString(visible)) {
isVisible = !!applyViewFn.call(view, visible, val, config);
}
// Either use the custom `visibleFn`, if provided, or execute the standard show/hide.
if (visibleFn) applyViewFn(view, visibleFn, $el, isVisible, config);
else {
if (visibleFn) {
applyViewFn.call(view, visibleFn, $el, isVisible, config);
} else {
$el.toggle(isVisible);
}
};
_.each(_.flatten([modelAttr]), function(attr) {
observeModelEvent(model, view, 'change:' + attr, config, visibleCb);
observeModelEvent(model, 'change:' + attr, config, visibleCb);
});
visibleCb();

@@ -385,6 +443,7 @@ };

//
var updateViewBindEl = function(view, $el, config, val, model, isInitializing) {
if (!evaluateBoolean(view, config.updateView, val, config)) return;
applyViewFn(view, config.update, $el, val, model, config);
if (!isInitializing) applyViewFn(view, config.afterUpdate, $el, val, config);
var updateViewBindEl = function($el, config, val, model, isInitializing) {
var view = config.view;
if (!evaluateBoolean(config.updateView, val, config)) return;
applyViewFn.call(view, config.update, $el, val, model, config);
if (!isInitializing) applyViewFn.call(view, config.afterUpdate, $el, val, config);
};

@@ -396,3 +455,3 @@

Stickit.addHandler([{
selector: '[contenteditable="true"]',
selector: '[contenteditable]',
updateMethod: 'html',

@@ -472,4 +531,10 @@ events: ['input', 'change']

var getList = function($el) {
return $el.map(function() {
return {value:this.value, label:this.text};
return $el.map(function(index, option) {
// Retrieve the text and value of the option, preferring "stickit-bind-val"
// data attribute over value property.
var dataVal = Backbone.$(option).data('stickit-bind-val');
return {
value: dataVal !== undefined ? dataVal : option.value,
label: option.text
};
}).get();

@@ -499,2 +564,3 @@ };

selectConfig.labelPath = selectConfig.labelPath || 'label';
selectConfig.disabledPath = selectConfig.disabledPath || 'disabled';

@@ -505,19 +571,37 @@ var addSelectOptions = function(optList, $el, fieldVal) {

var fillOption = function(text, val) {
var fillOption = function(text, val, disabled) {
option.text(text);
optionVal = val;
// Save the option value as data so that we can reference it later.
option.data('stickit_bind_val', optionVal);
option.data('stickit-bind-val', optionVal);
if (!_.isArray(optionVal) && !_.isObject(optionVal)) option.val(optionVal);
if (disabled === true) option.prop('disabled', 'disabled');
};
if (obj === '__default__')
fillOption(selectConfig.defaultOption.label, selectConfig.defaultOption.value);
else
fillOption(evaluatePath(obj, selectConfig.labelPath), evaluatePath(obj, selectConfig.valuePath));
var text, val, disabled;
if (obj === '__default__') {
text = fieldVal.label,
val = fieldVal.value,
disabled = fieldVal.disabled;
} else {
text = evaluatePath(obj, selectConfig.labelPath),
val = evaluatePath(obj, selectConfig.valuePath),
disabled = evaluatePath(obj, selectConfig.disabledPath);
}
fillOption(text, val, disabled);
// Determine if this option is selected.
if (!isMultiple && optionVal != null && fieldVal != null && optionVal === fieldVal || (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)))
var isSelected = function() {
if (!isMultiple && optionVal != null && fieldVal != null && optionVal === fieldVal) {
return true;
} else if (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)) {
return true;
}
return false;
};
if (isSelected()) {
option.prop('selected', true);
else if (isMultiple && _.isArray(fieldVal)) {
} else if (isMultiple && _.isArray(fieldVal)) {
_.each(fieldVal, function(val) {

@@ -538,17 +622,52 @@ if (_.isObject(val)) val = evaluatePath(val, selectConfig.valuePath);

// which represents the path to the list relative to `window` or the view/`this`.
var evaluate = function(view, list) {
if (_.isString(list)) {
var context = window;
if (list.indexOf('this.') === 0) context = view;
if (list.indexOf('this.') === 0) context = this;
list = list.replace(/^[a-z]*\.(.+)$/, '$1');
return evaluatePath(context, list);
};
if (_.isString(list)) optList = evaluate(this, list);
else if (_.isFunction(list)) optList = applyViewFn(this, list, $el, options);
else optList = list;
optList = evaluatePath(context, list);
} else if (_.isFunction(list)) {
optList = applyViewFn.call(this, list, $el, options);
} else {
optList = list;
}
// Support Backbone.Collection and deserialize.
if (optList instanceof Backbone.Collection) optList = optList.toJSON();
if (optList instanceof Backbone.Collection) {
var collection = optList;
var refreshSelectOptions = function() {
var currentVal = getAttr(model, options.observe, options);
applyViewFn.call(this, options.update, $el, currentVal, model, options);
};
// We need to call this function after unstickit and after an update so we don't end up
// with multiple listeners doing the same thing
var removeCollectionListeners = function() {
collection.off('add remove reset sort', refreshSelectOptions);
};
var removeAllListeners = function() {
removeCollectionListeners();
collection.off('stickit:selectRefresh');
model.off('stickit:selectRefresh');
};
// Remove previously set event listeners by triggering a custom event
collection.trigger('stickit:selectRefresh');
collection.once('stickit:selectRefresh', removeCollectionListeners, this);
// Listen to the collection and trigger an update of the select options
collection.on('add remove reset sort', refreshSelectOptions, this);
// Remove the previous model event listener
model.trigger('stickit:selectRefresh');
model.once('stickit:selectRefresh', function() {
model.off('stickit:unstuck', removeAllListeners);
});
// Remove collection event listeners once this binding is unstuck
model.once('stickit:unstuck', removeAllListeners, this);
optList = optList.toJSON();
}
if (selectConfig.defaultOption) {
addSelectOptions(["__default__"], $el);
var option = _.isFunction(selectConfig.defaultOption) ?
selectConfig.defaultOption.call(this, $el, options) :
selectConfig.defaultOption;
addSelectOptions(["__default__"], $el, option);
}

@@ -583,24 +702,21 @@

}
addSelectOptions(_.sortBy(opts, selectConfig.comparator || selectConfig.labelPath), $el, val);
opts = _.sortBy(opts, selectConfig.comparator || selectConfig.labelPath);
addSelectOptions(opts, $el, val);
}
},
getVal: function($el) {
var val;
var selected = $el.find('option:selected');
if ($el.prop('multiple')) {
val = Backbone.$(getSelectedOption($el).map(function() {
return Backbone.$(this).data('stickit_bind_val');
})).get();
return _.map(selected, function(el) {
return Backbone.$(el).data('stickit-bind-val');
});
} else {
val = getSelectedOption($el).data('stickit_bind_val');
return selected.data('stickit-bind-val');
}
return val;
}
}]);
return Stickit;
// Export onto Backbone object
Backbone.Stickit = Stickit;
return Backbone.Stickit;
}));
{
"name": "backbone.stickit",
"description": "Model binding in Backbone style.",
"version": "0.8.0",
"version": "0.9.0",
"author": "Matthew DeLambo <delambo@gmail.com>",

@@ -11,7 +11,8 @@ "repository": {

"main": "backbone.stickit.js",
"peerDependencies": {
"dependencies": {
"underscore": ">=1.4.2",
"backbone": ">=0.9.0"
"backbone": ">=1.0.0"
},
"devDependencies": {
"phantomjs": "^1.9.7",
"grunt-contrib": "~0.4.0",

@@ -27,2 +28,5 @@ "grunt-docco": "~0.3.0",

},
"scripts": {
"test": "phantomjs test/vendor/runner.js test/index.html?noglobals=true"
},
"bugs": {

@@ -29,0 +33,0 @@ "url": "http://github.com/nytimes/backbone.stickit/issues"

@@ -1,2 +0,2 @@

[-> **Documentation for current/stable release: 0.8.0**](http://nytimes.github.com/backbone.stickit/)
[-> **Documentation for current/stable release: 0.9.0**](http://nytimes.github.com/backbone.stickit/)

@@ -13,3 +13,3 @@ **The following is documentation for the code in master/edge version...**

[download v0.8.0](http://nytimes.github.com/backbone.stickit/downloads/backbone.stickit_0.8.0.zip)
[download v0.9.0](http://nytimes.github.com/backbone.stickit/downloads/backbone.stickit_0.9.0.zip)

@@ -33,3 +33,3 @@ [download master/edge](https://raw.github.com/NYTimes/backbone.stickit/master/backbone.stickit.js)

```javascript
```javascript
render: function() {

@@ -50,3 +50,3 @@ this.$el.html('<div id="title"/> <input id="author" type="text">');

```javascript
```javascript
render: function() {

@@ -64,3 +64,3 @@ this.$el.html(/* ... */);

Adds a single binding to the view, using the given model, or `view.model`, and the given `selector` and `configuration`. It's also possible to pass in a bindings hash as the second parameter. If you use a selector that was already used for a binding, then the old binding will be destroyed before initializing the new binding.
Adds a single binding to the view, using the given model, or `view.model`, and the given `selector` and `configuration`. It's also possible to pass in a bindings hash as the second parameter. If you use a selector that was already used for a binding, then the old binding will be destroyed before initializing the new binding.

@@ -73,3 +73,3 @@ ```javascript

// Or, with a bindings hash.
this.addBindings(null, {
this.addBinding(null, {
'#author': {

@@ -98,5 +98,5 @@ observe: 'author',

Notes on binding to an array of attributes: when binding from model->view, this configuration should be paired with an `onGet` callback that can unpack/format the values. When binding from view->model, then `onSet` or `getVal` should be defined and should return an array of values that stickit will set into the model.
Notes on binding to an array of attributes: when binding from model->view, this configuration should be paired with an `onGet` callback that can unpack/format the values. When binding from view->model, then `onSet` or `getVal` should be defined and should return an array of values that stickit will set into the model.
```javascript
```javascript
bindings: {

@@ -138,3 +138,3 @@ // Short form binding

```javascript
```javascript
tagName: 'form',

@@ -153,3 +153,3 @@ bindings: {

```javascript
```javascript
bindings: {

@@ -170,3 +170,3 @@ '#header': {

```javascript
```javascript
bindings: {

@@ -187,3 +187,3 @@ '#author': {

```javascript
```javascript
bindings: {

@@ -201,3 +201,3 @@ '#author': {

```javascript
```javascript
bindings: {

@@ -215,3 +215,3 @@ '#author': {

```javascript
```javascript
bindings: {

@@ -233,3 +233,3 @@ '#title': {

```javascript
```javascript
bindings: {

@@ -248,3 +248,3 @@ '#title': {

```javascript
```javascript
bindings: {

@@ -265,3 +265,3 @@ '#warning': {

```javascript
```javascript
bindings: {

@@ -280,3 +280,3 @@ '#header': {

```javascript
```javascript
bindings: {

@@ -295,3 +295,3 @@ '#header': {

```javascript
```javascript
bindings: {

@@ -311,3 +311,3 @@ '#album': {

```javascript
```javascript
bindings: {

@@ -328,5 +328,5 @@ '#album': {

If more than the standard jQuery show/hide is required, then you can manually take control by defining `visibleFn` with a callback.
If more than the standard jQuery show/hide is required, then you can manually take control by defining `visibleFn` with a callback.
```javascript
```javascript
bindings: {

@@ -340,3 +340,3 @@ '#author': {

```javascript
```javascript
bindings: {

@@ -351,3 +351,3 @@ '#title': {

```javascript
```javascript
bindings: {

@@ -370,3 +370,3 @@ '#body': {

The following is a list of the supported form elements, their binding details, and the default events used for binding:
The following is a list of the supported form elements, their binding details, and the default events used for binding:

@@ -383,3 +383,6 @@ - input, textarea, and contenteditable

- select
- if you choose to pre-render your select-options (unrecommended) then the binding will be configured with the "option[value]" attributes in the DOM; otherwise, see the `selectOptions` configuration.
- (recommended) specify `selectOptions` to have Stickit handle the rendering and option bindings of your select (see `selectOptions`)
- if you choose to pre-render your select-options (not recommended) then there are two ways of configuring the bindings:
- "data-stickit-bind-val" attributes in the DOM. This allows for binding non-string values from a prerendered &lt;select&gt;, assuming that you are using jQuery or a build of Zepto that includes the "data" module.
- "option[value]" attributes in the DOM (used if no data-stickit-bind-val is present)
- `change` event is used for handling

@@ -391,3 +394,3 @@

```javascript
```javascript
bindings: {

@@ -397,3 +400,3 @@ 'input#title': {

// Normally, stickit would bind `keyup`, `change`, `cut`, and `paste` events
// to an input:text element. The following will override these events and only
// to an input:text element. The following will override these events and only
// update/set the model after the input#title element is blur'ed.

@@ -412,3 +415,3 @@ events: ['blur']

- `valuePath`: the path to the values for select options within the collection of objects. When an options is selected, the value that is defined for the given option is set in the model. Leave this undefined if the whole object is the value or to use the default `value`.
- `defaultOption`: an object with `label` and `value` keys, used to define a default option value. A common use case would be something like the following: `{label: "Choose one...", value: null}`.
- `defaultOption`: an object or method that returns an object with `label` and `value` keys, used to define a default option value. A common use case would be something like the following: `{label: "Choose one...", value: null}`.

@@ -419,9 +422,9 @@ When bindings are initialized, Stickit will build the `<select>` element with the `<option>`s and bindings configured. `selectOptions` are not required - if left undefined, then Stickit will expect that the `<option>`s are pre-rendered and build the collection from the DOM.

The following example references a collection of stooges at `window.app.stooges` and uses the `age` attribute for labels and the `name` attribute for option values:
The following example references a collection of stooges at `window.app.stooges` and uses the `age` attribute for labels and the `name` attribute for option values:
```javascript
```javascript
window.app.stooges = [{name:'moe', age:40}, {name:'larry', age:50}, {name:'curly', age:60}];
```
```javascript
```javascript
bindings: {

@@ -469,4 +472,4 @@ 'select#stooges': {

labelPath: 'data.name'
// Leaving `valuePath` undefined so that the collection objects are used
// as option values. For example, if the "OH" option was selected, then the
// Leaving `valuePath` undefined so that the collection objects are used
// as option values. For example, if the "OH" option was selected, then the
// following value would be set into the model: `model.set('state', {id:1, data:{name:'OH'}});`

@@ -545,3 +548,3 @@ }

```javascript
```javascript
bindings: {

@@ -580,3 +583,3 @@ 'input#name': {

```javascript
```javascript
bindings: {

@@ -604,3 +607,3 @@ '#header': {

Adds the given handler or array of handlers to Stickit. A handler is a binding configuration, with an additional `selector` key, that is used to customize or override any of Stickit's default binding handling. To derive a binding configuration, the `selector`s are used to match against a bound element, and any matching handlers are mixed/extended in the order that they were added.
Adds the given handler or array of handlers to Stickit. A handler is a binding configuration, with an additional `selector` key, that is used to customize or override any of Stickit's default binding handling. To derive a binding configuration, the `selector`s are used to match against a bound element, and any matching handlers are mixed/extended in the order that they were added.

@@ -690,3 +693,3 @@ Internally, Stickit uses `addHandler` to add configuration for its default handling. For example, the following is the internal handler that matches against `textarea` elements:

JavaScript frameworks seem to be headed in the wrong direction - controller callbacks/directives, configuration, and special tags are being forced into the template/presentation layer. Who wants to program and debug templates?
JavaScript frameworks seem to be headed in the wrong direction - controller callbacks/directives, configuration, and special tags are being forced into the template/presentation layer. Who wants to program and debug templates?

@@ -705,2 +708,11 @@ If you are writing a custom frontend, then you're going to need to write custom JavaScript. Backbone helps you organize with a strong focus on the model, but stays the hell out of your presentation. Configuration and callbacks should only be in one place - the View/JavaScript.

#### Master
#### 0.9.0
- **Breaking Change**: Classes are now treated separately from other attribute bindings. Use the new `classes` hash to bind element classes to your attributes.
- `defaultOption` can be defined as a function.
- Passing a Backbone Collection to `selectOptions` will keep the select dropdown in sync with add, remove, and sort events on the collection.
- `view#stickit` returns `this` for easier chaining.
- Non-string values can be bound to a pre-rendered `<select>` dropdown by setting `data-stickit-bind-val` on `<option>` elements.
#### 0.8.0

@@ -734,3 +746,3 @@

- Added `Backbone.Stickit.addHandler()`, useful for defining a custom configuration for any bindings that match the `handler.selector`.
- Added `Backbone.Stickit.addHandler()`, useful for defining a custom configuration for any bindings that match the `handler.selector`.
- **Breaking Change**: `eventsOverride` was changed to `events`.

@@ -746,3 +758,3 @@ - **Breaking Change**: removed the third param (original value) from the `afterUpdate` parameters.

- Added `update` to the bindings api which is an override for handling how the View element gets updated with Model changes.
- Added `getVal` to the bindings api which is an override for retrieving the value of the View element.
- Added `getVal` to the bindings api which is an override for retrieving the value of the View element.
- Added support for passing in Backbone.Collection's into `selectOptions.collection`.

@@ -753,10 +765,10 @@ - Added support for referencing the view's scope with a String `selectOptions.collection` reference. For example: `collection:'this.viewCollection'`.

- **Breaking Change**: Changed the last parameter from the model attribute name to the bindings hash in most of the binding callbacks. Note the model attribute name can still be gleaned from the bindings hash - `options.observe`. The following are the callbacks that were affected and their parameters (`options` are the bindings hash):
`onGet(value, options)`
`onSet(value, options)`
`updateModel(value, options)`
`updateView(value, options)`
`afterUpdate($el, value, originalVal, options)`
`visible(value, options)`
`visibleFn($el, isVisible, options)`
- **Breaking Change**: Changed the last parameter from the model attribute name to the bindings hash in most of the binding callbacks. Note the model attribute name can still be gleaned from the bindings hash - `options.observe`. The following are the callbacks that were affected and their parameters (`options` are the bindings hash):
`onGet(value, options)`
`onSet(value, options)`
`updateModel(value, options)`
`updateView(value, options)`
`afterUpdate($el, value, originalVal, options)`
`visible(value, options)`
`visibleFn($el, isVisible, options)`
- Added support for handling multiple checkboxes with one binding/selector and using the `value` attribute, if present, for checkboxes.

@@ -768,3 +780,3 @@ - Added default values for `labelPath` and `valuePath` in selectOptions: `label` and `value` respectively.

- Fixed some bugs and added support requirements for zepto.js; [#58](https://github.com/NYTimes/backbone.stickit/pull/58).
- Bug Fixes: [#38](https://github.com/NYTimes/backbone.stickit/pull/38), [#42](https://github.com/NYTimes/backbone.stickit/pull/42),
- Bug Fixes: [#38](https://github.com/NYTimes/backbone.stickit/pull/38), [#42](https://github.com/NYTimes/backbone.stickit/pull/42),

@@ -771,0 +783,0 @@ #### 0.6.1

@@ -47,21 +47,24 @@ $(document).ready(function() {

test('contenteditable', function() {
_([1,2]).each(function(subtest) {
var sel = '#test17-' + subtest;
model.set({'water':'<span>fountain</span>'});
view.model = model;
view.templateId = 'jst17';
view.bindings = {
'#test17': {
test('contenteditable-' + subtest, function() {
model.set({'water':'<span>fountain</span>'});
view.model = model;
view.templateId = 'jst17';
view.bindings = {};
view.bindings[sel] = {
observe: 'water'
}
};
$('#qunit-fixture').html(view.render().el);
};
$('#qunit-fixture').html(view.render().el);
equal(view.$('#test17').html(), '<span>fountain</span>');
equal(view.$(sel).html(), '<span>fountain</span>');
model.set('water', '<span>evian</span>');
equal(view.$('#test17').html(), '<span>evian</span>');
model.set('water', '<span>evian</span>');
equal(view.$(sel).html(), '<span>evian</span>');
view.$('#test17').html('<span>dasina</span>').trigger('change');
equal(model.get('water'), '<span>dasina</span>');
view.$(sel).html('<span>dasina</span>').trigger('change');
equal(model.get('water'), '<span>dasina</span>');
});
});

@@ -318,6 +321,4 @@

events: function() {
var self = this;
return {
click: self.clickHandled
click: this.clickHandled
};

@@ -606,6 +607,6 @@ },

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'fountain');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'fountain');
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');

@@ -643,6 +644,6 @@ view.$('#test8 option').eq(2).prop('selected', true).trigger('change');

equal(view.$('#test8 option').eq(0).text(), 'Choose one...');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), null);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), null);
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');

@@ -653,3 +654,3 @@ view.$('#test8 option').eq(3).prop('selected', true).trigger('change');

test('bindings:selectOptions:defaultOption:OptGroups', 8, function() {
test('bindings:selectOptions:defaultOption (options is a method)', 8, function() {

@@ -666,2 +667,78 @@ model.set({'water':null});

equal(options.observe, 'water');
return [{id:1,type:{name:'fountain'}}, {id:2,type:{name:'evian'}}, {id:3,type:{name:'dasina'}}];
},
defaultOption: function(){
return {label: 'Choose dynamic...',
value: null};
},
labelPath: 'type.name',
valuePath: 'type.name'
}
}
};
$('#qunit-fixture').html(view.render().el);
equal(view.$('#test8 option').eq(0).text(), 'Choose dynamic...');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), null);
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');
view.$('#test8 option').eq(3).prop('selected', true).trigger('change');
equal(model.get('water'), 'dasina');
});
test('bindings:selectOptions:defaultOption (options is disabled)', 9, function() {
model.set({'water':null});
view.model = model;
view.templateId = 'jst8';
view.bindings = {
'#test8': {
observe: 'water',
selectOptions: {
collection: function($el, options) {
ok($el.is('select'));
equal(options.observe, 'water');
return [{id:1,type:{name:'fountain'}}, {id:2,type:{name:'evian'}}, {id:3,type:{name:'dasina'}}];
},
defaultOption: {
label: 'Choose one...',
value: null,
disabled: true
},
labelPath: 'type.name',
valuePath: 'type.name'
}
}
};
$('#qunit-fixture').html(view.render().el);
equal(view.$('#test8 option').eq(0).text(), 'Choose one...');
ok(view.$('#test8 option').eq(0).prop('disabled'));
equal(model.get('water'), null);
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');
// We can force the selection of disabled options
view.$('#test8 option').eq(0).prop('selected', true).trigger('change');
equal(model.get('water'), null);
});
test('bindings:selectOptions:defaultOption:OptGroups', 8, function() {
model.set({'water':null});
view.model = model;
view.templateId = 'jst8';
view.bindings = {
'#test8': {
observe: 'water',
selectOptions: {
collection: function($el, options) {
ok($el.is('select'));
equal(options.observe, 'water');
return {

@@ -685,6 +762,6 @@ 'opt_labels': ['Types'],

equal(view.$('#test8 > option').eq(0).text(), 'Choose one...');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), null);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), null);
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');

@@ -695,4 +772,30 @@ view.$('#test8 option').eq(3).prop('selected', true).trigger('change');

test('bindings:selectOptions (disabled options)' , function() {
model.set({'water':'fountain'});
view.model = model;
view.templateId = 'jst8';
view.bindings = {
'#test8': {
observe: 'water',
selectOptions: {
collection: [{id:1,name:'fountain'}, {id:2,name:'evian',disabled:true}, {id:3,name:'dasina'}],
labelPath: 'name',
valuePath: 'id'
}
}
};
$('#qunit-fixture').html(view.render().el);
equal(view.$('#test8 option').eq(0).prop('disabled'), false);
equal(view.$('#test8 option').eq(1).prop('disabled'), true);
equal(view.$('#test8 option').eq(2).prop('disabled'), false);
});
test('bindings:selectOptions (pre-rendered)', 3, function() {
// Note that we're working with strings and not numeric values here - the pre-rendered <select>
// handling is limited to strings unless data-stickit-bind-val is specified for each <option>
model.set({'water':'1'});

@@ -709,11 +812,38 @@ view.model = model;

equal(getSelectedOption(view.$('#test21')).data('stickit_bind_val'), '1');
strictEqual(getSelectedOption(view.$('#test21')).data('stickit-bind-val'), '1');
model.set('water', '2');
equal(getSelectedOption(view.$('#test21')).data('stickit_bind_val'), '2');
strictEqual(getSelectedOption(view.$('#test21')).data('stickit-bind-val'), '2');
view.$('#test21 option').eq(2).prop('selected', true).trigger('change');
equal(model.get('water'), '3');
strictEqual(model.get('water'), '3');
});
test('bindings:selectOptions (pre-rendered, with data-stickit-bind-val)', function() {
// Here we're testing that numeric values can be bound via data-stickit-bind-val
model.set({'water': 1});
view.model = model;
view.templateId = 'jst26';
view.bindings = {
'#test26': {
observe: 'water'
}
};
$('#qunit-fixture').html(view.render().el);
strictEqual(getSelectedOption(view.$('#test26')).data('stickit-bind-val'), 1);
model.set('water', 2);
strictEqual(getSelectedOption(view.$('#test26')).data('stickit-bind-val'), 2);
view.$('#test26 option:contains("dasina")').prop('selected', true).trigger('change');
strictEqual(model.get('water'), 3);
view.$('#test26 option:contains("foutain")').prop('selected', true).trigger('change');
strictEqual(model.get('water'), 0);
});
test('bindings:selectOptions (Backbone.Collection)', function() {

@@ -738,6 +868,6 @@

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'fountain');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'fountain');
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');

@@ -748,2 +878,71 @@ view.$('#test8 option').eq(2).prop('selected', true).trigger('change');

test('bindings:selectOptions (Backbone.Collection that changes)', function() {
var collection = new Backbone.Collection([{id:1,name:'fountain'}, {id:2,name:'evian'}, {id:3,name:'dasina'}]);
model.set({'water':'fountain'});
view.model = model;
view.templateId = 'jst8';
view.bindings = {
'#test8': {
observe: 'water',
selectOptions: {
collection: function() { return collection; },
labelPath: 'name',
valuePath: 'name'
}
}
};
$('#qunit-fixture').html(view.render().el);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'fountain');
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');
view.$('#test8 option').eq(2).prop('selected', true).trigger('change');
equal(model.get('water'), 'dasina');
// Test that the select options are auto-updated
collection.add({id:4,name:'buxton'});
equal(view.$('#test8 option').eq(3).data('stickit-bind-val'), 'buxton');
var modelEvents = ['stickit:unstuck', 'stickit:selectRefresh'];
var collectionEvents = ['stickit:selectRefresh', 'add', 'remove', 'reset', 'sort'];
// Test the number of event listeners set against the model and collection
equal(_.filter(model._events, function(event, key) {
return (event.length === 1 && _.contains(modelEvents, key));
}).length, modelEvents.length);
equal(_.filter(collection._events, function(event, key) {
return (event.length === 1 && _.contains(collectionEvents, key));
}).length, collectionEvents.length);
collection.remove(2);
equal(view.$('#test8 option').length, collection.length);
collection.reset();
equal(view.$('#test8 option').length, collection.length);
// Test the number of event listeners set against the model and collection after changes to the collection
equal(_.filter(model._events, function(event, key) {
return (event.length === 1 && _.contains(modelEvents, key));
}).length, modelEvents.length);
equal(_.filter(collection._events, function(event, key) {
return (event.length === 1 && _.contains(collectionEvents, key));
}).length, collectionEvents.length);
view.unstickit();
// Test that the select options are no longer updated
collection.add([{id:1,name:'fountain'}, {id:2,name:'evian'}, {id:3,name:'dasina'}]);
notEqual(view.$('#test8 option').length, collection.length);
// Test that all event listeners have been removed after unstickit
ok(_.isEmpty(model._events));
ok(_.isEmpty(collection._events));
});
test('bindings:selectOptions (collection path relative to `this`)', function() {

@@ -768,6 +967,6 @@

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'fountain');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'fountain');
model.set('water', 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');

@@ -797,6 +996,6 @@ view.$('#test8 option').eq(2).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val').id, 1);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val').id, 1);
model.set('water', {id:2, name:'evian'});
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val').id, 2);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val').id, 2);

@@ -827,7 +1026,7 @@ view.$('#test8 option').eq(2).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'session');
equal(view.$('#test8 option').eq(0).data('stickit_bind_val'), '');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'session');
equal(view.$('#test8 option').eq(0).data('stickit-bind-val'), '');
model.set('water', '');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), '');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), '');
});

@@ -853,6 +1052,6 @@

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'evian');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'evian');
model.set('water', 'fountain');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'fountain');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'fountain');
});

@@ -880,8 +1079,8 @@

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'moo');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'moo');
// Options are sorted alphabetically by label
equal(view.$('#test8 option:eq(0)').data('stickit_bind_val'), 'moo');
equal(view.$('#test8 option:eq(1)').data('stickit_bind_val'), 'oink');
equal(view.$('#test8 option:eq(2)').data('stickit_bind_val'), 'baa');
equal(view.$('#test8 option:eq(0)').data('stickit-bind-val'), 'moo');
equal(view.$('#test8 option:eq(1)').data('stickit-bind-val'), 'oink');
equal(view.$('#test8 option:eq(2)').data('stickit-bind-val'), 'baa');
});

@@ -910,8 +1109,8 @@

equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 'moo');
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 'moo');
// Options are sorted alphabetically by value
equal(view.$('#test8 option:eq(0)').data('stickit_bind_val'), 'baa');
equal(view.$('#test8 option:eq(1)').data('stickit_bind_val'), 'moo');
equal(view.$('#test8 option:eq(2)').data('stickit_bind_val'), 'oink');
equal(view.$('#test8 option:eq(0)').data('stickit-bind-val'), 'baa');
equal(view.$('#test8 option:eq(1)').data('stickit-bind-val'), 'moo');
equal(view.$('#test8 option:eq(2)').data('stickit-bind-val'), 'oink');
});

@@ -938,4 +1137,4 @@

equal(getSelectedOption(view.$('#test16')).eq(0).data('stickit_bind_val').name, 'fountain');
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit_bind_val').name, 'dasina');
equal(getSelectedOption(view.$('#test16')).eq(0).data('stickit-bind-val').name, 'fountain');
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit-bind-val').name, 'dasina');

@@ -946,3 +1145,3 @@ var field = _.clone(model.get('water'));

model.set({'water':field});
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit_bind_val').name, 'evian');
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit-bind-val').name, 'evian');

@@ -975,4 +1174,4 @@ view.$('#test16 option').eq(3).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test16')).eq(0).data('stickit_bind_val'), 1);
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit_bind_val'), 3);
equal(getSelectedOption(view.$('#test16')).eq(0).data('stickit-bind-val'), 1);
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit-bind-val'), 3);

@@ -983,3 +1182,3 @@ var field = _.clone(model.get('water'));

model.set({'water':field});
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit_bind_val'), 2);
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit-bind-val'), 2);

@@ -1005,4 +1204,4 @@ view.$('#test16 option').eq(3).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test23')).eq(0).data('stickit_bind_val'), '1');
equal(getSelectedOption(view.$('#test23')).eq(1).data('stickit_bind_val'), '3');
equal(getSelectedOption(view.$('#test23')).eq(0).data('stickit-bind-val'), '1');
equal(getSelectedOption(view.$('#test23')).eq(1).data('stickit-bind-val'), '3');

@@ -1013,3 +1212,3 @@ var field = _.clone(model.get('water'));

model.set({'water':field});
equal(getSelectedOption(view.$('#test23')).eq(1).data('stickit_bind_val'), '2');
equal(getSelectedOption(view.$('#test23')).eq(1).data('stickit-bind-val'), '2');

@@ -1048,4 +1247,4 @@ view.$('#test23 option').eq(3).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test16')).eq(0).data('stickit_bind_val'), 1);
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit_bind_val'), 3);
equal(getSelectedOption(view.$('#test16')).eq(0).data('stickit-bind-val'), 1);
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit-bind-val'), 3);

@@ -1056,3 +1255,3 @@ var field = _.clone(model.get('water'));

model.set({'water':field});
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit_bind_val'), 2);
equal(getSelectedOption(view.$('#test16')).eq(1).data('stickit-bind-val'), 2);

@@ -1091,6 +1290,6 @@ view.$('#test16 option').eq(3).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test8')).parent().attr('label'), 'Three Stooges');
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 3);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 3);
model.set({'character':2});
equal(getSelectedOption(view.$('#test8')).data('stickit_bind_val'), 2);
equal(getSelectedOption(view.$('#test8')).data('stickit-bind-val'), 2);

@@ -1116,6 +1315,6 @@ view.$('#test8 option').eq(3).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test22')).parent().attr('label'), 'Three Stooges');
equal(getSelectedOption(view.$('#test22')).data('stickit_bind_val'), '3');
equal(getSelectedOption(view.$('#test22')).data('stickit-bind-val'), '3');
model.set({'character':'2'});
equal(getSelectedOption(view.$('#test22')).data('stickit_bind_val'), '2');
equal(getSelectedOption(view.$('#test22')).data('stickit-bind-val'), '2');

@@ -1141,6 +1340,6 @@ view.$('#test22 option').eq(3).prop('selected', true).trigger('change');

equal(getSelectedOption(view.$('#test24')).parent().attr('label'), 'Three Stooges');
equal(getSelectedOption(view.$('#test24')).data('stickit_bind_val'), '3');
equal(getSelectedOption(view.$('#test24')).data('stickit-bind-val'), '3');
model.set({'character':'0'});
equal(getSelectedOption(view.$('#test24')).data('stickit_bind_val'), '0');
equal(getSelectedOption(view.$('#test24')).data('stickit-bind-val'), '0');

@@ -1219,2 +1418,26 @@ view.$('#test24 option').eq(4).prop('selected', true).trigger('change');

test('bindings:attributes:onGet (string)', function() {
model.set({'water':'fountain'});
view.model = model;
view.templateId = 'jst5';
view.onGetByString = function(val, options) { return '_' + val + '_' + options.observe; };
view.bindings = {
'#test5': {
observe: 'water',
attributes: [{
name: 'data-name',
onGet: 'onGetByString'
}]
}
};
$('#qunit-fixture').html(view.render().el);
equal(view.$('#test5').attr('data-name'), '_fountain_water');
model.set('water', 'evian');
equal(view.$('#test5').attr('data-name'), '_evian_water');
});
test('bindings:attributes:observe', function() {

@@ -1297,2 +1520,52 @@

test('bindings:classes:name', function() {
model.set({'water':'fountain'});
view.model = model;
view.templateId = 'jst5';
view.bindings = {
'#test5': {
classes: {
testClass: 'water'
}
}
};
$('#qunit-fixture').html(view.render().el);
ok(view.$('#test5').hasClass('testClass'));
model.set('water', false);
ok(!view.$('#test5').hasClass('testClass'));
});
test('bindings:classes:observe', function() {
model.set({'truthy':false});
view.model = model;
view.templateId = 'jst5';
view.bindings = {
'#test5': {
classes: {
'col-md-2': {
observe:'truthy'
},
'col-md-3': {
observe: 'truthy',
onGet: function(val) {
return !val;
}
}
}
}
};
$('#qunit-fixture').html(view.render().el);
ok(!view.$('#test5').hasClass('col-md-2'));
ok(view.$('#test5').hasClass('col-md-3'));
model.set('truthy', 'string is truthy');
ok(view.$('#test5').hasClass('col-md-2'));
ok(!view.$('#test5').hasClass('col-md-3'));
});
test('input:number', function() {

@@ -1490,6 +1763,6 @@

observe: 'water',
updateModel: function(val, event, options) {
updateModel: function(val, event, config) {
equal(this.cid, view.cid);
equal(val, view.$('#test1').val());
equal(options.observe, 'water');
equal(config.observe, 'water');
equal(event.type, 'change');

@@ -1496,0 +1769,0 @@ return val == 'evian';

@@ -47,3 +47,3 @@ $(document).ready(function() {

equal(_.keys(view.model._events).length, 4);
view.unstickit(null, '#test14-1');

@@ -56,3 +56,3 @@ equal(_.keys(view.model._events).length, 3);

view.unstickit(null, view.bindings);
equal(_.keys(view.model._events).length, 0);
equal(_.keys(view.model._events).length, 0);
});

@@ -187,2 +187,20 @@

});
test('change with different model', 2, function() {
model.set({'water':'fountain'});
view.model = model;
view.templateId = 'jst10';
view.bindings = {
'.test10': {
observe: 'water',
updateView: function(value, config) {
ok(value == 'fountain');
}
}
};
$('#qunit-fixture').html(view.render().el);
// intentionally pass a wrong model here (this can happen via event bubbling of Backbone.Associations i.e.)
model.trigger('change:water', new Backbone.Model({ 'otherProp': 'value' }));
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc