diff --git a/code/html/spec/settings.spec.mjs b/code/html/spec/settings.spec.mjs
index a58345d0..7cf257c0 100644
--- a/code/html/spec/settings.spec.mjs
+++ b/code/html/spec/settings.spec.mjs
@@ -26,7 +26,7 @@ test('select unchanged with empty value when original is missing', () => {
.toEqual(getOriginalForElement(node));
node.selectedIndex = -1;
- expect(getDataForElement(node)).toBe(null);
+ expect(getDataForElement(node)).toBeNull();
assert(!isChangedElement(node));
node.selectedIndex = 1;
@@ -53,22 +53,21 @@ test('number input unchanged with empty value when original is missing', () => {
expect(getDataForElement(node))
.toEqual(getOriginalForElement(node));
- const data = 12345;
- expect(getDataForElement(node)).toBe(null);
+ expect(getDataForElement(node)).toBeNaN();
assert(!isChangedElement(node));
- node.valueAsNumber = data;
+ setInputValue(node, 12345);
assert(!isChangedElement(node));
assert(checkAndSetElementChanged(node));
- node.value = '';
+ setInputValue(node, '');
assert(isChangedElement(node));
assert(checkAndSetElementChanged(node));
assert(!isChangedElement(node));
setOriginalsFromValuesForNode(node);
- node.value = '';
+ setInputValue(node, '');
assert(!checkAndSetElementChanged(node));
assert(!isChangedElement(node));
@@ -255,16 +254,19 @@ test('input value update', () => {
input.value = '';
setInputValue(input, null);
- expect(getDataForElement(input)).toBeNull();
+ expect(getDataForElement(input)).toBeNaN();
setInputValue(input, 12345);
expect(getDataForElement(input)).toBe(12345);
+ setInputValue(input, '');
+ expect(getDataForElement(input)).toBeNaN();
+
setInputValue(input, '56789');
expect(getDataForElement(input)).toBe(56789);
setInputValue(input, 'text');
- expect(getDataForElement(input)).toBe(null);
+ expect(getDataForElement(input)).toBeNaN();
input.type = 'text';
input.value = '';
diff --git a/code/html/src/settings.mjs b/code/html/src/settings.mjs
index d31edbfb..f47c769a 100644
--- a/code/html/src/settings.mjs
+++ b/code/html/src/settings.mjs
@@ -558,8 +558,7 @@ export function getDataForElement(elem) {
case "number":
case "range":
- return !isNaN(elem.valueAsNumber)
- ? elem.valueAsNumber : null;
+ return elem.valueAsNumber;
}
} else if (elem instanceof HTMLSelectElement) {
@@ -602,7 +601,7 @@ export function getOriginalForElement(elem) {
case "range":
return (original !== undefined)
? parseInt(original)
- : null;
+ : NaN;
}
} else if (elem instanceof HTMLSelectElement) {
if (original === undefined) {
@@ -707,19 +706,25 @@ export function setInputValue(input, value) {
case "radio":
input.checked = (value === input.value);
break;
+
case "checkbox":
input.checked =
(typeof value === "boolean") ? value :
(typeof value === "string") ? stringToBoolean(value) :
(typeof value === "number") ? (value !== 0) : false;
break;
+
case "number":
- case "password":
case "range":
+ input.valueAsNumber =
+ (typeof value === "string") ? parseInt(value) :
+ (typeof value === "number") ? value : NaN;
+ break;
+
+ case "password":
case "text":
- if (value !== null) {
- input.value = value.toString();
- }
+ input.value =
+ (value ?? "").toString();
break;
}
}
@@ -796,26 +801,33 @@ export function setSelectValue(select, value) {
});
}
+/**
+ * @param {InputOrSelect} elem
+ */
+export function setOriginalFromValue(elem) {
+ if (elem instanceof HTMLInputElement) {
+ if (elem.readOnly) {
+ return;
+ }
+
+ if (elem.type === "checkbox") {
+ elem.dataset["original"] = elem.checked.toString();
+ } else {
+ elem.dataset["original"] = elem.value;
+ }
+ } else if (elem instanceof HTMLSelectElement) {
+ elem.dataset["original"] = selectedValues(elem).join(",");
+ }
+
+ resetChangedElement(elem);
+}
+
/**
* @param {InputOrSelect[]} elems
*/
export function setOriginalsFromValues(elems) {
for (let elem of elems) {
- if (elem instanceof HTMLInputElement) {
- if (elem.readOnly) {
- continue;
- }
-
- if (elem.type === "checkbox") {
- elem.dataset["original"] = elem.checked.toString();
- } else {
- elem.dataset["original"] = elem.value;
- }
- } else if (elem instanceof HTMLSelectElement) {
- elem.dataset["original"] = selectedValues(elem).join(",");
- }
-
- resetChangedElement(elem);
+ setOriginalFromValue(elem);
}
}
@@ -905,8 +917,14 @@ function onEnumerableUpdateSpan(span, enumerables) {
}
/**
+ * @callback EnumerableElemCallback
* @param {HTMLElement} elem
* @param {EnumerableEntry[]} enumerables
+ * @returns {void}
+ */
+
+/**
+ * @type {EnumerableElemCallback}
*/
function onEnumerableUpdateElem(elem, enumerables) {
if (elem instanceof HTMLSelectElement) {
@@ -1136,6 +1154,25 @@ export function initInputKeyValueElement(key, value) {
const Settings = new SettingsBase();
+/**
+ * @param {InputOrSelect} elem
+ * @returns {boolean}
+ */
+function checkElementChanged(elem) {
+ const lhs = getOriginalForElement(elem);
+ const rhs = getDataForElement(elem);
+
+ if (typeof lhs === "number"
+ && typeof rhs === "number"
+ && isNaN(lhs)
+ && isNaN(rhs))
+ {
+ return false;
+ }
+
+ return lhs !== rhs;
+}
+
/**
* @param {InputOrSelect} elem
* @returns {boolean}
@@ -1143,7 +1180,7 @@ const Settings = new SettingsBase();
export function checkAndSetElementChanged(elem) {
const changed = isChangedElement(elem);
- if (getOriginalForElement(elem) !== getDataForElement(elem)) {
+ if (checkElementChanged(elem)) {
setChangedElement(elem);
} else {
resetChangedElement(elem);