Skip to content

Commit

Permalink
knockout#54 - use lower case for all tags to support xhtml
Browse files Browse the repository at this point in the history
  • Loading branch information
mbest committed Feb 22, 2012
1 parent e9b12ad commit 7c62b80
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 29 deletions.
25 changes: 13 additions & 12 deletions src/binding/defaultBindings.js
Expand Up @@ -156,6 +156,7 @@ ko.bindingHandlers['value'] = {
});
},
'update': function (element, valueAccessor) {
var valueIsSelectOption = element.tagName.toLowerCase() == "select";
var newValue = ko.utils.unwrapObservable(valueAccessor());
var elementValue = ko.selectExtensions.readValue(element);
var valueHasChanged = (newValue != elementValue);
Expand All @@ -172,26 +173,26 @@ ko.bindingHandlers['value'] = {
// Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
// right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
// to apply the value as well.
var alsoApplyAsynchronously = element.tagName == "SELECT";
var alsoApplyAsynchronously = valueIsSelectOption;
if (alsoApplyAsynchronously)
setTimeout(applyValueAction, 0);
}

// If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
// because you're not allowed to have a model value that disagrees with a visible UI selection.
if ((element.tagName == "SELECT") && (element.length > 0))
if (valueIsSelectOption && (element.length > 0))
ensureDropdownSelectionIsConsistentWithModelValue(element, newValue, /* preferModelValue */ false);
}
};

ko.bindingHandlers['options'] = {
'update': function (element, valueAccessor, allBindingsAccessor) {
if (element.tagName != "SELECT")
if (element.tagName.toLowerCase() != "select")
throw new Error("options binding applies only to SELECT elements");

var selectWasPreviouslyEmpty = element.length == 0;
var previousSelectedValues = ko.utils.arrayMap(ko.utils.arrayFilter(element.childNodes, function (node) {
return node.tagName && node.tagName == "OPTION" && node.selected;
return node.tagName && node.tagName.toLowerCase() == "option" && node.selected;
}), function (node) {
return ko.selectExtensions.readValue(node) || node.innerText || node.textContent;
});
Expand All @@ -213,13 +214,13 @@ ko.bindingHandlers['options'] = {
if (typeof value.length != "number")
value = [value];
if (allBindings['optionsCaption']) {
var option = document.createElement("OPTION");
var option = document.createElement("option");
ko.utils.setHtml(option, allBindings['optionsCaption']);
ko.selectExtensions.writeValue(option, undefined);
element.appendChild(option);
}
for (var i = 0, j = value.length; i < j; i++) {
var option = document.createElement("OPTION");
var option = document.createElement("option");

// Apply a value to the option element
var optionValue = typeof allBindings['optionsValue'] == "string" ? value[i][allBindings['optionsValue']] : value[i];
Expand All @@ -245,7 +246,7 @@ ko.bindingHandlers['options'] = {

// IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
// That's why we first added them without selection. Now it's time to set the selection.
var newOptions = element.getElementsByTagName("OPTION");
var newOptions = element.getElementsByTagName("option");
var countSelectionsRetained = 0;
for (var i = 0, j = newOptions.length; i < j; i++) {
if (ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[i])) >= 0) {
Expand Down Expand Up @@ -276,10 +277,10 @@ ko.bindingHandlers['selectedOptions'] = {
var result = [];
var nodes = selectNode.childNodes;
for (var i = 0, j = nodes.length; i < j; i++) {
var node = nodes[i];
if ((node.tagName == "OPTION") && node.selected)
var node = nodes[i], tagName = node.tagName.toLowerCase();
if (tagName == "option" && node.selected)
result.push(ko.selectExtensions.readValue(node));
else if (node.tagName == "OPTGROUP") {
else if (tagName == "optgroup") {
var selectedValuesFromOptGroup = ko.bindingHandlers['selectedOptions'].getSelectedValuesFromSelectNode(node);
Array.prototype.splice.apply(result, [result.length, 0].concat(selectedValuesFromOptGroup)); // Add new entries to existing 'result' instance
}
Expand All @@ -299,15 +300,15 @@ ko.bindingHandlers['selectedOptions'] = {
});
},
'update': function (element, valueAccessor) {
if (element.tagName != "SELECT")
if (element.tagName.toLowerCase() != "select")
throw new Error("values binding applies only to SELECT elements");

var newValue = ko.utils.unwrapObservable(valueAccessor());
if (newValue && typeof newValue.length == "number") {
var nodes = element.childNodes;
for (var i = 0, j = nodes.length; i < j; i++) {
var node = nodes[i];
if (node.tagName == "OPTION")
if (node.tagName.toLowerCase() == "option")
ko.utils.setOptionNodeSelectionState(node, ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0);
}
}
Expand Down
20 changes: 13 additions & 7 deletions src/binding/selectExtensions.js
Expand Up @@ -6,18 +6,21 @@
// that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
ko.selectExtensions = {
readValue : function(element) {
if (element.tagName == 'OPTION') {
switch (element.tagName.toLowerCase()) {
case 'option':
if (element[hasDomDataExpandoProperty] === true)
return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
return element.getAttribute("value");
} else if (element.tagName == 'SELECT')
case 'select':
return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
else
default:
return element.value;
}
},

writeValue: function(element, value) {
if (element.tagName == 'OPTION') {
switch (element.tagName.toLowerCase()) {
case 'option':
switch(typeof value) {
case "string":
ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
Expand All @@ -34,18 +37,21 @@
// Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
element.value = typeof value === "number" ? value : "";
break;
}
} else if (element.tagName == 'SELECT') {
}
break;
case 'select':
for (var i = element.options.length - 1; i >= 0; i--) {
if (ko.selectExtensions.readValue(element.options[i]) == value) {
element.selectedIndex = i;
break;
}
}
} else {
break;
default:
if ((value === null) || (value === undefined))
value = "";
element.value = value;
break;
}
}
};
Expand Down
18 changes: 8 additions & 10 deletions src/utils.js
Expand Up @@ -29,7 +29,7 @@ ko.utils = new (function () {
isIe7 = ieVersion === 7;

function isClickOnCheckableElement(element, eventType) {
if ((element.tagName != "INPUT") || !element.type) return false;
if ((element.tagName.toLowerCase() != "input") || !element.type) return false;
if (eventType.toLowerCase() != "click") return false;
var inputType = element.type.toLowerCase();
return (inputType == "checkbox") || (inputType == "radio");
Expand Down Expand Up @@ -254,10 +254,8 @@ ko.utils = new (function () {
} else if (typeof element.fireEvent != "undefined") {
// Unlike other browsers, IE doesn't change the checked state of checkboxes/radiobuttons when you trigger their "click" event
// so to make it consistent, we'll do it manually here
if (eventType == "click") {
if ((element.tagName == "INPUT") && ((element.type.toLowerCase() == "checkbox") || (element.type.toLowerCase() == "radio")))
element.checked = element.checked !== true;
}
if (isClickOnCheckableElement(element, eventType))
element.checked = element.checked !== true;
element.fireEvent("on" + eventType);
}
else
Expand Down Expand Up @@ -334,7 +332,7 @@ ko.utils = new (function () {
ieVersion : ieVersion,

getFormFields: function(form, fieldName) {
var fields = ko.utils.makeArray(form.getElementsByTagName("INPUT")).concat(ko.utils.makeArray(form.getElementsByTagName("TEXTAREA")));
var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
var isMatchingField = (typeof fieldName == 'string')
? function(field) { return field.name === fieldName }
: function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
Expand Down Expand Up @@ -371,7 +369,7 @@ ko.utils = new (function () {
var url = urlOrForm;

// If we were given a form, use its 'action' URL and pick out any requested field values
if((typeof urlOrForm == 'object') && (urlOrForm.tagName == "FORM")) {
if((typeof urlOrForm == 'object') && (urlOrForm.tagName.toLowerCase() == "form")) {
var originalForm = urlOrForm;
url = originalForm.action;
for (var i = includeFields.length - 1; i >= 0; i--) {
Expand All @@ -382,18 +380,18 @@ ko.utils = new (function () {
}

data = ko.utils.unwrapObservable(data);
var form = document.createElement("FORM");
var form = document.createElement("form");
form.style.display = "none";
form.action = url;
form.method = "post";
for (var key in data) {
var input = document.createElement("INPUT");
var input = document.createElement("input");
input.name = key;
input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
form.appendChild(input);
}
for (var key in params) {
var input = document.createElement("INPUT");
var input = document.createElement("input");
input.name = key;
input.value = params[key];
form.appendChild(input);
Expand Down

0 comments on commit 7c62b80

Please sign in to comment.