-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
checked.js
executable file
·130 lines (111 loc) · 5.72 KB
/
checked.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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(function() {
ko.bindingHandlers['checked'] = {
'after': ['value', 'attr'],
'init': function (element, valueAccessor, allBindings) {
var checkedValue = ko.pureComputed(function() {
// Treat "value" like "checkedValue" when it is included with "checked" binding
if (allBindings['has']('checkedValue')) {
return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
} else if (useElementValue) {
if (allBindings['has']('value')) {
return ko.utils.unwrapObservable(allBindings.get('value'));
} else {
return element.value;
}
}
});
function updateModel() {
// This updates the model value from the view value.
// It runs in response to DOM events (click) and changes in checkedValue.
var isChecked = element.checked,
elemValue = checkedValue();
// When we're first setting up this computed, don't change any model state.
if (ko.computedContext.isInitial()) {
return;
}
// We can ignore unchecked radio buttons, because some other radio
// button will be checked, and that one can take care of updating state.
// Also ignore value changes to an already unchecked checkbox.
if (!isChecked && (isRadio || ko.computedContext.getDependenciesCount())) {
return;
}
var modelValue = ko.dependencyDetection.ignore(valueAccessor);
if (valueIsArray) {
var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue,
saveOldValue = oldElemValue;
oldElemValue = elemValue;
if (saveOldValue !== elemValue) {
// When we're responding to the checkedValue changing, and the element is
// currently checked, replace the old elem value with the new elem value
// in the model array.
if (isChecked) {
ko.utils.addOrRemoveItem(writableValue, elemValue, true);
ko.utils.addOrRemoveItem(writableValue, saveOldValue, false);
}
} else {
// When we're responding to the user having checked/unchecked a checkbox,
// add/remove the element value to the model array.
ko.utils.addOrRemoveItem(writableValue, elemValue, isChecked);
}
if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
modelValue(writableValue);
}
} else {
if (isCheckbox) {
if (elemValue === undefined) {
elemValue = isChecked;
} else if (!isChecked) {
elemValue = undefined;
}
}
ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
}
};
function updateView() {
// This updates the view value from the model value.
// It runs in response to changes in the bound (checked) value.
var modelValue = ko.utils.unwrapObservable(valueAccessor()),
elemValue = checkedValue();
if (valueIsArray) {
// When a checkbox is bound to an array, being checked represents its value being present in that array
element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
oldElemValue = elemValue;
} else if (isCheckbox && elemValue === undefined) {
// When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
// being checked represents the value being trueish
element.checked = !!modelValue;
} else {
// Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
element.checked = (checkedValue() === modelValue);
}
};
var isCheckbox = element.type == "checkbox",
isRadio = element.type == "radio";
// Only bind to check boxes and radio buttons
if (!isCheckbox && !isRadio) {
return;
}
var rawValue = valueAccessor(),
valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),
rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),
useElementValue = isRadio || valueIsArray,
oldElemValue = valueIsArray ? checkedValue() : undefined;
// IE 6 won't allow radio buttons to be selected unless they have a name
if (isRadio && !element.name)
ko.bindingHandlers['uniqueName']['init'](element, function() { return true });
// Set up two computeds to update the binding:
// The first responds to changes in the checkedValue value and to element clicks
ko.computed(updateModel, null, { disposeWhenNodeIsRemoved: element });
ko.utils.registerEventHandler(element, "click", updateModel);
// The second responds to changes in the model value (the one associated with the checked binding)
ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
rawValue = undefined;
}
};
ko.expressionRewriting.twoWayBindings['checked'] = true;
ko.bindingHandlers['checkedValue'] = {
'update': function (element, valueAccessor) {
element.value = ko.utils.unwrapObservable(valueAccessor());
}
};
})();