(function () { if (window && window.navigator) { var parseVersion = function (matches) { if (matches) { return parseFloat(matches[1]); } }; // Detect various browser versions because some old versions don't fully support the 'input' event var operaVersion = window.opera && window.opera.version && parseInt(window.opera.version()), userAgent = window.navigator.userAgent, safariVersion = parseVersion(userAgent.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)), firefoxVersion = parseVersion(userAgent.match(/Firefox\/([^ ]*)/)); } // IE 8 and 9 have bugs that prevent the normal events from firing when the value changes. // But it does fire the 'selectionchange' event on many of those, presumably because the // cursor is moving and that counts as the selection changing. The 'selectionchange' event is // fired at the document level only and doesn't directly indicate which element changed. We // set up just one event handler for the document and use 'activeElement' to determine which // element was changed. if (ko.utils.ieVersion < 10) { var selectionChangeRegisteredName = ko.utils.domData.nextKey(), selectionChangeHandlerName = ko.utils.domData.nextKey(); var selectionChangeHandler = function(event) { var target = this.activeElement, handler = target && ko.utils.domData.get(target, selectionChangeHandlerName); if (handler) { handler(event); } }; var registerForSelectionChangeEvent = function (element, handler) { var ownerDoc = element.ownerDocument; if (!ko.utils.domData.get(ownerDoc, selectionChangeRegisteredName)) { ko.utils.domData.set(ownerDoc, selectionChangeRegisteredName, true); ko.utils.registerEventHandler(ownerDoc, 'selectionchange', selectionChangeHandler); } ko.utils.domData.set(element, selectionChangeHandlerName, handler); }; } ko.bindingHandlers['textInput'] = { 'init': function (element, valueAccessor, allBindings) { var previousElementValue = element.value, timeoutHandle, elementValueBeforeEvent; var updateModel = function (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 = function (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 = setTimeout(handler, 4); } }; var updateView = function () { var modelValue = ko.utils.unwrapObservable(valueAccessor()); if (modelValue === null || modelValue === undefined) { modelValue = ''; } if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) { 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) { previousElementValue = modelValue; // Make sure we ignore events (propertychange) that result from updating the value element.value = modelValue; } }; var onEvent = function (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.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], function(eventName) { if (eventName.slice(0,5) == 'after') { onEvent(eventName.slice(5), deferUpdateModel); } else { onEvent(eventName, updateModel); } }); } else { if (ko.utils.ieVersion < 10) { // Internet Explorer <= 8 doesn't support the 'input' event, but does include 'propertychange' that fires whenever // any property of an element changes. Unlike 'input', it also fires if a property is changed from JavaScript code, // but that's an acceptable compromise for this binding. IE 9 does support 'input', but since it doesn't fire it // when using autocomplete, we'll use 'propertychange' for it also. onEvent('propertychange', function(event) { if (event.propertyName === 'value') { updateModel(event); } }); if (ko.utils.ieVersion == 8) { // IE 8 has a bug where it fails to fire 'propertychange' on the first update following a value change from // JavaScript code. It also doesn't fire if you clear the entire value. To fix this, we bind to the following // events too. onEvent('keyup', updateModel); // A single keystoke onEvent('keydown', updateModel); // The first character when a key is held down } if (ko.utils.ieVersion >= 8) { // Internet Explorer 9 doesn't fire the 'input' event when deleting text, including using // the backspace, delete, or ctrl-x keys, clicking the 'x' to clear the input, dragging text // out of the field, and cutting or deleting text using the context menu. 'selectionchange' // can detect all of those except dragging text out of the field, for which we use 'dragend'. // These are also needed in IE8 because of the bug described above. registerForSelectionChangeEvent(element, updateModel); // 'selectionchange' covers cut, paste, drop, delete, etc. onEvent('dragend', deferUpdateModel); } } else { // All other supported browsers support the 'input' event, which fires whenever the content of the element is changed // through the user interface. onEvent('input', updateModel); if (safariVersion < 5 && ko.utils.tagNameLower(element) === "textarea") { // Safari <5 doesn't fire the 'input' event for