ko.bindingHandlers['textInput'] = { 'init': (element, valueAccessor, allBindings) => { var previousElementValue = element.value, timeoutHandle, elementValueBeforeEvent; var updateModel = event => { clearTimeout(timeoutHandle); elementValueBeforeEvent = timeoutHandle = undefined; var elementValue = element.value; if (previousElementValue !== elementValue) { // Provide a way for tests to know exactly which event was processed if (DEBUG && event) element['_ko_textInputProcessedEvent'] = event.type; previousElementValue = elementValue; ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue); } }; var deferUpdateModel = event => { if (!timeoutHandle) { // The elementValueBeforeEvent variable is set *only* during the brief gap between an // event firing and the updateModel function running. This allows us to ignore model // updates that are from the previous state of the element, usually due to techniques // such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost. elementValueBeforeEvent = element.value; var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel; timeoutHandle = ko.utils.setTimeout(handler, 4); } }; var updateView = () => { var modelValue = ko.utils.unwrapObservable(valueAccessor()); if (modelValue === null || modelValue === undefined) { modelValue = ''; } if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) { ko.utils.setTimeout(updateView, 4); return; } // Update the element only if the element and model are different. On some browsers, updating the value // will move the cursor to the end of the input, which would be bad while the user is typing. if (element.value !== modelValue) { element.value = modelValue; previousElementValue = element.value; // In case the browser changes the value (see #2281) } }; var onEvent = (event, handler) => ko.utils.registerEventHandler(element, event, handler); if (DEBUG && ko.bindingHandlers['textInput']['_forceUpdateOn']) { // Provide a way for tests to specify exactly which events are bound ko.bindingHandlers['textInput']['_forceUpdateOn'].forEach(eventName => { if (eventName.slice(0,5) == 'after') { onEvent(eventName.slice(5), deferUpdateModel); } else { onEvent(eventName, updateModel); } }); } else { onEvent('input', updateModel); } // Bind to the change event so that we can catch programmatic updates of the value that fire this event. onEvent('change', updateModel); // To deal with browsers that don't notify any kind of event for some changes (IE, Safari, etc.) onEvent('blur', updateModel); ko.computed(updateView, { disposeWhenNodeIsRemoved: element }); } }; ko.expressionRewriting.twoWayBindings['textInput'] = true; // textinput is an alias for textInput ko.bindingHandlers['textinput'] = { // preprocess is the only way to set up a full alias 'preprocess': (value, name, addBinding) => addBinding('textInput', value) };