|
// It's may not work (just becauze we are using jquery and no handling over the class) |
|
// We can't rely on backbone's previous value so we save state (user can use silte: true) |
|
// DOMAttr just used for bindAttr, and oldValue for bindClass, but for smaller |
|
// (and better!) code used all the same way! |
|
var bindClass = function ($el, DOMAttr, value, oldValue) { |
|
$el.removeClass(oldValue).addClass(value); |
|
}, |
|
bindText = function ($el, DOMAttr, value, oldValue) { |
|
$el.text(value); |
|
}, |
|
bindHtml = function ($el, DOMAttr, value, oldValue) { |
|
$el.html(value); |
|
}, |
|
bindAttr = function ($el, DOMAttr, value, oldValue) { |
|
$el.prop(DOMAttr, value) |
|
}; |
|
|
|
var callbackGenerator = function (config, action, that) { |
|
var oldValue, $el = that.$(config.el); |
|
if (!$el.length) return; |
|
return function (newValue) { |
|
var _ret, $el = that.$(config.el); |
|
if (config.truthy) { |
|
_ret = !! newValue ? config.truthy : config.falsy; |
|
} else { |
|
_ret = newValue; |
|
} |
|
// This lines make sure that opposite of true or false is available (in case |
|
// user forgot to add falsy or truthy value opposite of another one's boolean type. |
|
// And falsy = false, truthy = "disabled" won't work! |
|
if (typeof oldValue === 'boolean') { |
|
newValue = !!newValue; |
|
} |
|
action($el, config.to, _ret, oldValue); |
|
oldValue = _ret; |
|
}; |
|
}; |
|
|
|
|
|
var View = Marionette.ItemView.extend({ |
|
unbindModelFromDom: function () { |
|
if (!this._domCallbacks) { |
|
return this; |
|
} |
|
var callbacks = this._domCallbacks; |
|
for (var i = 0, l = callbacks.length; i < l; i++) { |
|
this.stopListening(this.model, 'all', callbacks[i]); |
|
} |
|
return this; |
|
}, |
|
|
|
bindModelToDom: function () { |
|
if (!this.model) { |
|
return; |
|
} |
|
|
|
var bindAttributes = Marionette.getOption(this, "bindAttributes"), |
|
context = this; |
|
|
|
if (!bindAttributes) { |
|
return; |
|
} |
|
|
|
var domCallbacks = this._domCallbacks || (this._domCallbacks = []); |
|
|
|
// Listen method is used for listening to models change (by attr) |
|
listen = function (attr, callback) { |
|
context.listenTo(context.model, 'change:' + attr, callback); |
|
}; |
|
|
|
// Instead of using huge switch case, use a hash and (||) operator! |
|
var type2action = { |
|
'class': bindClass, |
|
'html': bindText, |
|
'bareHtml': bindHtml |
|
}; |
|
|
|
_.each(bindAttributes, function (settings, attr) { |
|
if (!_.isArray(settings)) { |
|
settings = [settings]; |
|
} |
|
if (!settings.length) return; |
|
var callbacks = [], |
|
callback; |
|
for (var i = 0, l = settings.length; i < l; i++) { |
|
// Before this must check for valid config! |
|
// |
|
// Below code will add a function with own workspace to callbacks. |
|
// I think this is acceptable by browser's GC (Not sure about IE, I've linux) |
|
callback = callbackGenerator(settings[i], type2action[settings[i].to] || bindAttr, context); |
|
if (callback) callbacks.push(callback); |
|
} |
|
var changeFn = function (model, newValue) { |
|
for (var i = 0, l = callbacks.length; i < l; i++) { |
|
callbacks[i].call(context, newValue); |
|
} |
|
}; |
|
listen(attr, changeFn); |
|
domCallbacks.push(changeFn); |
|
changeFn(context.model, context.model.get(attr)); |
|
}); |
|
return this; |
|
}, |
|
onBeforeRender: function () { |
|
this.unbindModelFromDom(); |
|
}, |
|
onRender: function () { |
|
this.bindModelToDom(); |
|
}, |
|
}); |