todomvc
Version:
> Helping you select an MV\* framework
219 lines (197 loc) • 5.71 kB
JavaScript
/*!
* CanJS - 2.0.3
* http://canjs.us/
* Copyright (c) 2013 Bitovi
* Tue, 26 Nov 2013 18:21:22 GMT
* Licensed MIT
* Includes: CanJS default build
* Download from: http://canjs.us/
*/
define(["can/util/library", "can/view/mustache", "can/control"], function(can){
/**
* @function can.view.bindings.can-value can-value
* @parent can.view.bindings
*
* Sets up two way bindings in a template.
*
* @signature `can-value='KEY'`
*
* Binds the element's value or checked property to the value specified by
* key. Example:
*
* <input type='text' can-value='first.name'/>
*
* @param {can.Mustache.key} key A named value in the current scope.
*
* @body
*
* ## Use
*
* Add a `can-value="KEY"` attribute to an input or select element and
* the element's value will be cross-bound to an observable value specified by `KEY`.
*
* Depending on the element and the element's type, `can-value` takes on
* different behaviors. If an input element has a type
* not listed here, the behavior is the same as the `text` type.
*
* ## input type=text
*
* Cross binds the input's string text value with the observable value.
*
* @demo can/view/bindings/hyperloop.html
*
* ## input type=checkbox
*
* Cross binds the checked property to a true or false value. An alternative
* true and false value can be specified by setting `can-true-value` and
* `can-false-value` attributes.
*
* @demo can/view/bindings/input-checkbox.html
*
* ## input type='radio'
*
* If the radio element is checked, sets the observable specified by `can-value` to match the value of
* `value` attribute.
*
* @demo can/view/bindings/input-radio.html
*
* ## select
*
* Cross binds the selected option value with an observable value.
*
* @demo can/view/bindings/select.html
*
*/
can.view.Scanner.attribute("can-value", function(data, el){
var attr = el.getAttribute("can-value"),
value = data.scope.computeData(attr,{args:[]}).compute;
if(el.nodeName.toLowerCase() === "input"){
if(el.type === "checkbox") {
if( el.hasAttribute("can-true-value") ) {
var trueValue = data.scope.compute( el.getAttribute("can-true-value") )
} else {
var trueValue = can.compute(true)
}
if( el.hasAttribute("can-false-value") ) {
var falseValue = data.scope.compute( el.getAttribute("can-false-value") )
} else {
var falseValue = can.compute(false)
}
}
if(el.type === "checkbox" || el.type === "radio") {
new Checked(el,{
value: value,
trueValue: trueValue,
falseValue: falseValue
});
return;
}
}
new Value(el,{value: value})
});
var special = {
enter: function(data, el, original){
return {
event: "keyup",
handler: function(ev){
if(ev.keyCode == 13) {
return original.call(this,ev)
}
}
}
}
}
/**
* @function can.view.bindings.can-EVENT can-EVENT
* @parent can.view.bindings
*
* @signature `can-EVENT='KEY'`
*
* Specify a callback function to be called on a particular event.
*
* @param {String} EVENT A event name like `click` or `keyup`. If you are
* using jQuery, you can listen to jQuery special events too.
*
* @param {can.Mustache.key} key A named value in the current scope. The value
* should be a function.
*
* @body
*
* ## Use
*
* By adding `can-EVENT='KEY'` to an element, the function pointed to
* by `KEY` is bound to the element's `EVENT` event. The function
* is called back with:
*
* - `context` - the context of the element
* - `element` - the element that was bound
* - `event` - the event that was triggered
*
* @demo can/view/bindings/can-event.html
*
*/
can.view.Scanner.attribute(/can-[\w\.]+/,function(data, el){
var attributeName = data.attr,
event = data.attr.substr("can-".length),
handler = function(ev){
var attr = el.getAttribute(attributeName),
scopeData = data.scope.read(attr,{returnObserveMethods: true, isArgument: true});
return scopeData.value.call(scopeData.parent,data.scope._context, can.$(this), ev )
};
if(special[event]){
var specialData = special[event](data, el, handler);
handler = specialData.handler;
event = specialData.event;
}
can.bind.call( el, event, handler);
});
var Value = can.Control.extend({
init: function(){
if(this.element[0].nodeName.toUpperCase() === "SELECT"){
// need to wait until end of turn ...
setTimeout($.proxy(this.set,this),1)
} else {
this.set()
}
},
"{value} change": "set",
set: function(){
this.element[0].value = this.options.value()
},
"change": function(){
this.options.value(this.element[0].value)
}
})
var Checked = can.Control.extend({
init: function(){
this.isCheckebox = (this.element[0].type.toLowerCase() == "checkbox");
this.check()
},
"{value} change": "check",
"{trueValue} change": "check",
"{falseValue} change": "check",
check: function(){
if(this.isCheckebox){
var value = this.options.value(),
trueValue = this.options.trueValue() || true,
falseValue = this.options.falseValue() || false;
this.element[0].checked = ( value == trueValue )
} else {
if(this.options.value() === this.element[0].value){
this.element[0].checked = true //.prop("checked", true)
} else {
this.element[0].checked = false //.prop("checked", false)
}
}
},
"change": function(){
if(this.isCheckebox){
this.options.value( this.element[0].checked? this.options.trueValue() : this.options.falseValue() );
} else {
if(this.element[0].checked){
this.options.value( this.element[0].value );
}
}
}
});
});