forked from knockout/knockout
/
bindingAttributeSyntax.js
executable file
·74 lines (63 loc) · 3.7 KB
/
bindingAttributeSyntax.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
(function () {
var bindingAttributeName = "data-bind";
ko.bindingHandlers = {};
function parseBindingAttribute(attributeText, viewModel) {
try {
var json = " { " + ko.jsonExpressionRewriting.insertPropertyAccessorsIntoJson(attributeText) + " } ";
return ko.utils.evalWithinScope(json, viewModel === null ? window : viewModel);
} catch (ex) {
throw new Error("Unable to parse binding attribute.\nMessage: " + ex + ";\nAttribute value: " + attributeText);
}
}
function invokeBindingHandler(handler, element, dataValue, allBindings, viewModel) {
handler(element, dataValue, allBindings, viewModel);
}
ko.applyBindingsToNode = function (node, bindings, viewModel) {
var isFirstEvaluation = true;
// Each time the dependentObservable is evaluated (after data changes),
// the binding attribute is reparsed so that it can pick out the correct
// model properties in the context of the changed data.
// DOM event callbacks need to be able to access this changed data,
// so we need a single parsedBindings variable (shared by all callbacks
// associated with this node's bindings) that all the closures can access.
var parsedBindings;
function makeValueAccessor(bindingKey) {
return function () { return parsedBindings[bindingKey] }
}
function parsedBindingsAccessor() {
return parsedBindings;
}
new ko.dependentObservable(
function () {
var evaluatedBindings = (typeof bindings == "function") ? bindings() : bindings;
parsedBindings = evaluatedBindings || parseBindingAttribute(node.getAttribute(bindingAttributeName), viewModel);
// First run all the inits, so bindings can register for notification on changes
if (isFirstEvaluation) {
for (var bindingKey in parsedBindings) {
if (ko.bindingHandlers[bindingKey] && typeof ko.bindingHandlers[bindingKey]["init"] == "function")
invokeBindingHandler(ko.bindingHandlers[bindingKey]["init"], node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel);
}
}
// ... then run all the updates, which might trigger changes even on the first evaluation
for (var bindingKey in parsedBindings) {
if (ko.bindingHandlers[bindingKey] && typeof ko.bindingHandlers[bindingKey]["update"] == "function")
invokeBindingHandler(ko.bindingHandlers[bindingKey]["update"], node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel);
}
},
null,
{ 'disposeWhenNodeIsRemoved' : node }
);
isFirstEvaluation = false;
};
ko.applyBindings = function (viewModel, rootNode) {
if (rootNode && (rootNode.nodeType == undefined))
throw new Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node (note: this is a breaking change since KO version 1.05)");
rootNode = rootNode || window.document.body; // Make "rootNode" parameter optional
var elemsWithBindingAttribute = ko.utils.getElementsHavingAttribute(rootNode, bindingAttributeName);
ko.utils.arrayForEach(elemsWithBindingAttribute, function (element) {
ko.applyBindingsToNode(element, null, viewModel);
});
};
ko.exportSymbol('ko.bindingHandlers', ko.bindingHandlers);
ko.exportSymbol('ko.applyBindings', ko.applyBindings);
})();