Drop support for old browsers and some jQuery

This commit is contained in:
djmaze 2020-09-17 16:40:43 +02:00
parent 091c4ec811
commit d06fed09d6
38 changed files with 6137 additions and 872 deletions

View file

@ -1,8 +1,8 @@
(function () {
// Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294
var contextSubscribable = ko.utils.createSymbolOrString('_subscribable');
var contextAncestorBindingInfo = ko.utils.createSymbolOrString('_ancestorBindingInfo');
var contextDataDependency = ko.utils.createSymbolOrString('_dataDependency');
var contextSubscribable = Symbol('_subscribable');
var contextAncestorBindingInfo = Symbol('_ancestorBindingInfo');
var contextDataDependency = Symbol('_dataDependency');
ko.bindingHandlers = {};
@ -565,11 +565,6 @@
};
ko.applyBindings = function (viewModelOrBindingContext, rootNode, extendContextCallback) {
// If jQuery is loaded after Knockout, we won't initially have access to it. So save it here.
if (!jQueryInstance && window['jQuery']) {
jQueryInstance = window['jQuery'];
}
if (arguments.length < 2) {
rootNode = document.body;
if (!rootNode) {

14
vendors/knockout/src/binding/defaultBindings/attr.js vendored Executable file → Normal file
View file

@ -19,17 +19,7 @@ ko.bindingHandlers['attr'] = {
attrValue = attrValue.toString();
}
// In IE <= 7 and IE8 Quirks Mode, you have to use the JavaScript property name instead of the
// HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
// but instead of figuring out the mode, we'll just set the attribute through the JavaScript
// property for IE <= 8.
if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavaScriptMap) {
attrName = attrHtmlToJavaScriptMap[attrName];
if (toRemove)
element.removeAttribute(attrName);
else
element[attrName] = attrValue;
} else if (!toRemove) {
if (!toRemove) {
namespace ? element.setAttributeNS(namespace, attrName, attrValue) : element.setAttribute(attrName, attrValue);
}
@ -38,7 +28,7 @@ ko.bindingHandlers['attr'] = {
// Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
// entirely, and there's no strong reason to allow for such casing in HTML.
if (attrName === "name") {
ko.utils.setElementName(element, toRemove ? "" : attrValue);
element.name = toRemove ? "" : attrValue;
}
});
}

19
vendors/knockout/src/binding/defaultBindings/checked.js vendored Executable file → Normal file
View file

@ -1,5 +1,16 @@
(function() {
function addOrRemoveItem(array, value, included) {
var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.peekObservable(array), value);
if (existingEntryIndex < 0) {
if (included)
array.push(value);
} else {
if (!included)
array.splice(existingEntryIndex, 1);
}
}
ko.bindingHandlers['checked'] = {
'after': ['value', 'attr'],
'init': function (element, valueAccessor, allBindings) {
@ -45,13 +56,13 @@ ko.bindingHandlers['checked'] = {
// 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);
addOrRemoveItem(writableValue, elemValue, true);
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);
addOrRemoveItem(writableValue, elemValue, isChecked);
}
if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
@ -127,4 +138,4 @@ ko.bindingHandlers['checkedValue'] = {
}
};
})();
})();

0
vendors/knockout/src/binding/defaultBindings/click.js vendored Executable file → Normal file
View file

18
vendors/knockout/src/binding/defaultBindings/css.js vendored Executable file → Normal file
View file

@ -1,10 +1,22 @@
var classesWrittenByBindingKey = '__ko__cssValue';
// For details on the pattern for changing node classes
// see: https://github.com/knockout/knockout/issues/1597
function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
if (classNames) {
var addOrRemoveFn = shouldHaveClass ? 'add' : 'remove';
classNames.split(/\s+/).forEach(function(className) {
node.classList[addOrRemoveFn](className);
});
}
}
ko.bindingHandlers['class'] = {
'update': function (element, valueAccessor) {
var value = ko.utils.stringTrim(ko.utils.unwrapObservable(valueAccessor()));
ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
element[classesWrittenByBindingKey] = value;
ko.utils.toggleDomNodeCssClass(element, value, true);
toggleDomNodeCssClass(element, value, true);
}
};
@ -14,7 +26,7 @@ ko.bindingHandlers['css'] = {
if (value !== null && typeof value == "object") {
ko.utils.objectForEach(value, function(className, shouldHaveClass) {
shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);
ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
toggleDomNodeCssClass(element, className, shouldHaveClass);
});
} else {
ko.bindingHandlers['class']['update'](element, valueAccessor);

0
vendors/knockout/src/binding/defaultBindings/enableDisable.js vendored Executable file → Normal file
View file

0
vendors/knockout/src/binding/defaultBindings/event.js vendored Executable file → Normal file
View file

0
vendors/knockout/src/binding/defaultBindings/foreach.js vendored Executable file → Normal file
View file

16
vendors/knockout/src/binding/defaultBindings/hasfocus.js vendored Executable file → Normal file
View file

@ -10,17 +10,7 @@ ko.bindingHandlers['hasfocus'] = {
// from calling 'blur()' on the element when it loses focus.
// Discussion at https://github.com/SteveSanderson/knockout/pull/352
element[hasfocusUpdatingProperty] = true;
var ownerDoc = element.ownerDocument;
if ("activeElement" in ownerDoc) {
var active;
try {
active = ownerDoc.activeElement;
} catch(e) {
// IE9 throws if you access activeElement during page load (see issue #703)
active = ownerDoc.body;
}
isFocused = (active === element);
}
isFocused = (element.ownerDocument.activeElement === element);
var modelValue = valueAccessor();
ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'hasfocus', isFocused, true);
@ -32,9 +22,9 @@ ko.bindingHandlers['hasfocus'] = {
var handleElementFocusOut = handleElementFocusChange.bind(null, false);
ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn);
ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut); // For IE
ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut);
// Assume element is not focused (prevents "blur" being called initially)
element[hasfocusLastValue] = false;

0
vendors/knockout/src/binding/defaultBindings/html.js vendored Executable file → Normal file
View file

0
vendors/knockout/src/binding/defaultBindings/ifIfnotWith.js vendored Executable file → Normal file
View file

7
vendors/knockout/src/binding/defaultBindings/options.js vendored Executable file → Normal file
View file

@ -30,7 +30,7 @@ ko.bindingHandlers['options'] = {
if (!valueAllowUnset) {
if (multiple) {
previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);
previousSelectedValues = selectedOptions().map(ko.selectExtensions.readValue);
} else if (element.selectedIndex >= 0) {
previousSelectedValues.push(ko.selectExtensions.readValue(element.options[element.selectedIndex]));
}
@ -108,7 +108,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 isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;
ko.utils.setOptionNodeSelectionState(newOptions[0], isSelected);
newOptions[0].selected = isSelected;
// If this option was changed from being selected during a single-item update, notify the change
if (itemUpdate && !isSelected) {
@ -154,9 +154,6 @@ ko.bindingHandlers['options'] = {
ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
}
// Workaround for IE bug
ko.utils.ensureSelectElementIsRenderedCorrectly(element);
if (previousScrollTop && Math.abs(previousScrollTop - element.scrollTop) > 20)
element.scrollTop = previousScrollTop;
}

2
vendors/knockout/src/binding/defaultBindings/selectedOptions.js vendored Executable file → Normal file
View file

@ -17,7 +17,7 @@ ko.bindingHandlers['selectedOptions'] = {
ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
if (node.selected != isSelected) { // This check prevents flashing of the select element in IE
ko.utils.setOptionNodeSelectionState(node, isSelected);
node.selected = isSelected;
}
});
}

4
vendors/knockout/src/binding/defaultBindings/style.js vendored Executable file → Normal file
View file

@ -9,9 +9,7 @@ ko.bindingHandlers['style'] = {
styleValue = "";
}
if (jQueryInstance) {
jQueryInstance(element)['css'](styleName, styleValue);
} else if (/^--/.test(styleName)) {
if (/^--/.test(styleName)) {
// Is styleName a custom CSS property?
element.style.setProperty(styleName, styleValue);
} else {

0
vendors/knockout/src/binding/defaultBindings/submit.js vendored Executable file → Normal file
View file

0
vendors/knockout/src/binding/defaultBindings/text.js vendored Executable file → Normal file
View file

View file

@ -1,51 +1,5 @@
(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 userAgent = window.navigator.userAgent,
operaVersion, chromeVersion, safariVersion, firefoxVersion, ieVersion, edgeVersion;
(operaVersion = window.opera && window.opera.version && parseInt(window.opera.version()))
|| (edgeVersion = parseVersion(userAgent.match(/Edge\/([^ ]+)$/)))
|| (chromeVersion = parseVersion(userAgent.match(/Chrome\/([^ ]+)/)))
|| (safariVersion = parseVersion(userAgent.match(/Version\/([^ ]+) Safari/)))
|| (firefoxVersion = parseVersion(userAgent.match(/Firefox\/([^ ]+)/)))
|| (ieVersion = ko.utils.ieVersion || parseVersion(userAgent.match(/MSIE ([^ ]+)/))) // Detects up to IE 10
|| (ieVersion = parseVersion(userAgent.match(/rv:([^ )]+)/))); // Detects IE 11
}
// 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 (ieVersion >= 8 && 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) {
@ -80,8 +34,7 @@ ko.bindingHandlers['textInput'] = {
// IE9 will mess up the DOM if you handle events synchronously which results in DOM changes (such as other bindings);
// so we'll make sure all updates are asynchronous
var ieUpdateModel = ko.utils.ieVersion == 9 ? deferUpdateModel : updateModel,
ourUpdate = false;
var ourUpdate = false;
var updateView = function () {
var modelValue = ko.utils.unwrapObservable(valueAccessor());
@ -119,66 +72,7 @@ ko.bindingHandlers['textInput'] = {
}
});
} else {
if (ieVersion) {
// All versions (including 11) of Internet Explorer have a bug that they don't generate an input or propertychange event when ESC is pressed
onEvent('keypress', updateModel);
}
if (ieVersion < 11) {
// 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 and 10 support 'input', but since they don't always
// fire it when using autocomplete, we'll use 'propertychange' for them also.
onEvent('propertychange', function(event) {
if (!ourUpdate && event.propertyName === 'value') {
ieUpdateModel(event);
}
});
}
if (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 (registerForSelectionChangeEvent) {
// 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, ieUpdateModel); // 'selectionchange' covers cut, paste, drop, delete, etc.
onEvent('dragend', deferUpdateModel);
}
if (!ieVersion || ieVersion >= 9) {
// All other supported browsers support the 'input' event, which fires whenever the content of the element is changed
// through the user interface.
onEvent('input', ieUpdateModel);
}
if (safariVersion < 5 && ko.utils.tagNameLower(element) === "textarea") {
// Safari <5 doesn't fire the 'input' event for <textarea> elements (it does fire 'textInput'
// but only when typing). So we'll just catch as much as we can with keydown, cut, and paste.
onEvent('keydown', deferUpdateModel);
onEvent('paste', deferUpdateModel);
onEvent('cut', deferUpdateModel);
} else if (operaVersion < 11) {
// Opera 10 doesn't always fire the 'input' event for cut, paste, undo & drop operations.
// We can try to catch some of those using 'keydown'.
onEvent('keydown', deferUpdateModel);
} else if (firefoxVersion < 4.0) {
// Firefox <= 3.6 doesn't fire the 'input' event when text is filled in through autocomplete
onEvent('DOMAutoComplete', updateModel);
// Firefox <=3.5 doesn't fire the 'input' event when text is dropped into the input.
onEvent('dragdrop', updateModel); // <3.5
onEvent('drop', updateModel); // 3.5
} else if (edgeVersion && element.type === "number") {
// Microsoft Edge doesn't fire 'input' or 'change' events for number inputs when
// the value is changed via the up / down arrow keys
onEvent('keydown', deferUpdateModel);
}
onEvent('input', updateModel);
}
// Bind to the change event so that we can catch programmatic updates of the value that fire this event.
@ -200,4 +94,4 @@ ko.bindingHandlers['textinput'] = {
}
};
})();
})();

3
vendors/knockout/src/binding/defaultBindings/uniqueName.js vendored Executable file → Normal file
View file

@ -1,8 +1,7 @@
ko.bindingHandlers['uniqueName'] = {
'init': function (element, valueAccessor) {
if (valueAccessor()) {
var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
ko.utils.setElementName(element, name);
element.name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
}
}
};

20
vendors/knockout/src/binding/defaultBindings/value.js vendored Executable file → Normal file
View file

@ -11,7 +11,6 @@ ko.bindingHandlers['value'] = {
var eventsToCatch = [];
var requestedEventsToCatch = allBindings.get("valueUpdate");
var propertyChangedFired = false;
var elementValueBeforeEvent = null;
if (requestedEventsToCatch) {
@ -19,33 +18,20 @@ ko.bindingHandlers['value'] = {
if (typeof requestedEventsToCatch == "string") {
eventsToCatch = [requestedEventsToCatch];
} else {
eventsToCatch = ko.utils.arrayGetDistinctValues(requestedEventsToCatch);
eventsToCatch = requestedEventsToCatch ? requestedEventsToCatch.filter(function(item, index) {
return requestedEventsToCatch.indexOf(item) === index;
}) : [];
}
ko.utils.arrayRemoveItem(eventsToCatch, "change"); // We'll subscribe to "change" events later
}
var valueUpdateHandler = function() {
elementValueBeforeEvent = null;
propertyChangedFired = false;
var modelValue = valueAccessor();
var elementValue = ko.selectExtensions.readValue(element);
ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'value', elementValue);
}
// Workaround for https://github.com/SteveSanderson/knockout/issues/122
// IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
var ieAutoCompleteHackNeeded = ko.utils.ieVersion && isInputElement && element.type == "text"
&& element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
ko.utils.registerEventHandler(element, "focus", function () { propertyChangedFired = false });
ko.utils.registerEventHandler(element, "blur", function() {
if (propertyChangedFired) {
valueUpdateHandler();
}
});
}
ko.utils.arrayForEach(eventsToCatch, function(eventName) {
// The syntax "after<eventname>" means "run the handler asynchronously after the event"
// This is useful, for example, to catch "keydown" events after the browser has updated the control

0
vendors/knockout/src/binding/defaultBindings/visibleHidden.js vendored Executable file → Normal file
View file

View file

@ -88,7 +88,7 @@
} else {
if (!editScript || (lastMappingResult && lastMappingResult['_countWaitingForRemove'])) {
// Compare the provided array against the previous one
var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; }),
var lastArray = Array.prototype.map.call(lastMappingResult, function (x) { return x.arrayEntry; }),
compareOptions = {
'dontLimitMoves': options['dontLimitMoves'],
'sparse': true

View file

@ -10,9 +10,7 @@
case 'option':
if (element[hasDomDataExpandoProperty] === true)
return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
return ko.utils.ieVersion <= 7
? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)
: element.value;
return element.value;
case 'select':
return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
default:
@ -53,14 +51,6 @@
}
if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) {
element.selectedIndex = selection;
if (ko.utils.ieVersion === 6) {
// 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.
ko.utils.setTimeout(function () {
element.selectedIndex = selection;
}, 0);
}
}
break;
default:

View file

@ -5,7 +5,7 @@
var tagNameLower = ko.utils.tagNameLower(node);
if (ko.components.isRegistered(tagNameLower)) {
// Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]" || (ko.utils.ieVersion <= 8 && node.tagName === tagNameLower)) {
if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]") {
return tagNameLower;
}
}
@ -84,32 +84,4 @@
return { '$raw': {} };
}
}
// --------------------------------------------------------------------------------
// Compatibility code for older (pre-HTML5) IE browsers
if (ko.utils.ieVersion < 9) {
// Whenever you preregister a component, enable it as a custom element in the current document
ko.components['register'] = (function(originalFunction) {
return function(componentName) {
document.createElement(componentName); // Allows IE<9 to parse markup containing the custom element
return originalFunction.apply(this, arguments);
}
})(ko.components['register']);
// Whenever you create a document fragment, enable all preregistered component names as custom elements
// This is needed to make innerShiv/jQuery HTML parsing correctly handle the custom elements
document.createDocumentFragment = (function(originalFunction) {
return function() {
var newDocFrag = originalFunction(),
allComponents = ko.components._allRegisteredComponents;
for (var componentName in allComponents) {
if (Object.prototype.hasOwnProperty.call(allComponents, componentName)) {
newDocFrag.createElement(componentName);
}
}
return newDocFrag;
};
})(document.createDocumentFragment);
}
})();
})();

View file

@ -1,4 +1,4 @@
var computedState = ko.utils.createSymbolOrString('_state');
var computedState = Symbol('_state');
ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
if (typeof evaluatorFunctionOrOptions === "object") {

View file

@ -1,96 +0,0 @@
(function() {
var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)
ko.toJS = function(rootObject) {
if (arguments.length == 0)
throw new Error("When calling ko.toJS, pass the object you want to convert.");
// We just unwrap everything at every level in the object graph
return mapJsObjectGraph(rootObject, function(valueToMap) {
// Loop because an observable's value might in turn be another observable wrapper
for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
valueToMap = valueToMap();
return valueToMap;
});
};
ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
var plainJavaScriptObject = ko.toJS(rootObject);
return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
};
function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
visitedObjects = visitedObjects || new objectLookup();
rootObject = mapInputCallback(rootObject);
var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
if (!canHaveProperties)
return rootObject;
var outputProperties = rootObject instanceof Array ? [] : {};
visitedObjects.save(rootObject, outputProperties);
visitPropertiesOrArrayEntries(rootObject, function(indexer) {
var propertyValue = mapInputCallback(rootObject[indexer]);
switch (typeof propertyValue) {
case "boolean":
case "number":
case "string":
case "function":
outputProperties[indexer] = propertyValue;
break;
case "object":
case "undefined":
var previouslyMappedValue = visitedObjects.get(propertyValue);
outputProperties[indexer] = (previouslyMappedValue !== undefined)
? previouslyMappedValue
: mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
break;
}
});
return outputProperties;
}
function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
if (rootObject instanceof Array) {
for (var i = 0; i < rootObject.length; i++)
visitorCallback(i);
// For arrays, also respect toJSON property for custom mappings (fixes #278)
if (typeof rootObject['toJSON'] == 'function')
visitorCallback('toJSON');
} else {
for (var propertyName in rootObject) {
visitorCallback(propertyName);
}
}
};
function objectLookup() {
this.keys = [];
this.values = [];
};
objectLookup.prototype = {
constructor: objectLookup,
save: function(key, value) {
var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
if (existingIndex >= 0)
this.values[existingIndex] = value;
else {
this.keys.push(key);
this.values.push(value);
}
},
get: function(key) {
var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
return (existingIndex >= 0) ? this.values[existingIndex] : undefined;
}
};
})();
ko.exportSymbol('toJS', ko.toJS);
ko.exportSymbol('toJSON', ko.toJSON);

View file

@ -1,4 +1,4 @@
var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');
var observableLatestValue = Symbol('_latestValue');
ko.observable = function (initialValue) {
function observable() {

View file

@ -12,11 +12,10 @@ ko.when = function(predicate, callback, context) {
return subscription;
}
if (typeof Promise === "function" && !callback) {
return new Promise(kowhen);
} else {
if (callback) {
return kowhen(callback.bind(context));
}
return new Promise(kowhen);
};
ko.exportSymbol('when', ko.when);

View file

@ -1,84 +0,0 @@
(function() {
ko.jqueryTmplTemplateEngine = function () {
// Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
// doesn't expose a version number, so we have to infer it.
// Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
// which KO internally refers to as version "2", so older versions are no longer detected.
var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
if (!jQueryInstance || !(jQueryInstance['tmpl']))
return 0;
// Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
try {
if (jQueryInstance['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
// Since 1.0.0pre, custom tags should append markup to an array called "__"
return 2; // Final version of jquery.tmpl
}
} catch(ex) { /* Apparently not the version we were looking for */ }
return 1; // Any older version that we don't support
})();
function ensureHasReferencedJQueryTemplates() {
if (jQueryTmplVersion < 2)
throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
}
function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
return jQueryInstance['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
}
this['renderTemplateSource'] = function(templateSource, bindingContext, options, templateDocument) {
templateDocument = templateDocument || document;
options = options || {};
ensureHasReferencedJQueryTemplates();
// Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
var precompiled = templateSource['data']('precompiled');
if (!precompiled) {
var templateText = templateSource['text']() || "";
// Wrap in "with($whatever.koBindingContext) { ... }"
templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";
precompiled = jQueryInstance['template'](null, templateText);
templateSource['data']('precompiled', precompiled);
}
var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
var jQueryTemplateOptions = jQueryInstance['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);
var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
resultNodes['appendTo'](templateDocument.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work
jQueryInstance['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
return resultNodes;
};
this['createJavaScriptEvaluatorBlock'] = function(script) {
return "{{ko_code ((function() { return " + script + " })()) }}";
};
this['addTemplate'] = function(templateName, templateMarkup) {
document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
};
if (jQueryTmplVersion > 0) {
jQueryInstance['tmpl']['tag']['ko_code'] = {
open: "__.push($1 || '');"
};
jQueryInstance['tmpl']['tag']['ko_with'] = {
open: "with($1) {",
close: "} "
};
}
};
ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
ko.jqueryTmplTemplateEngine.prototype.constructor = ko.jqueryTmplTemplateEngine;
// Use this one by default *only if jquery.tmpl is referenced*
var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);
ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
})();

View file

@ -5,16 +5,13 @@ ko.nativeTemplateEngine = function () {
ko.nativeTemplateEngine.prototype = new ko.templateEngine();
ko.nativeTemplateEngine.prototype.constructor = ko.nativeTemplateEngine;
ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
templateNodes = templateNodesFunc ? templateSource['nodes']() : null;
var templateNodesFunc = templateSource.nodes,
templateNodes = templateNodesFunc ? templateSource.nodes() : null;
if (templateNodes) {
return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
} else {
var templateText = templateSource['text']();
return ko.utils.parseHtmlFragment(templateText, templateDocument);
}
var templateText = templateSource['text']();
return ko.utils.parseHtmlFragment(templateText, templateDocument);
};
ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();

View file

@ -1,52 +1,26 @@
ko.utils.domData = new (function () {
var uniqueId = 0;
var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
var dataStore = {};
var dataStoreKeyExpandoPropertyName = "__ko__" + (Date.now());
var getDataForNode, clear;
if (!ko.utils.ieVersion) {
// We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using
// it cross-window, so instead we just store the data directly on the node.
// See https://github.com/knockout/knockout/issues/2141
getDataForNode = function (node, createIfNotFound) {
var dataForNode = node[dataStoreKeyExpandoPropertyName];
if (!dataForNode && createIfNotFound) {
dataForNode = node[dataStoreKeyExpandoPropertyName] = {};
}
return dataForNode;
};
clear = function (node) {
if (node[dataStoreKeyExpandoPropertyName]) {
delete node[dataStoreKeyExpandoPropertyName];
return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
}
return false;
};
} else {
// Old IE versions have memory issues if you store objects on the node, so we use a
// separate data storage and link to it from the node using a string key.
getDataForNode = function (node, createIfNotFound) {
var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
if (!hasExistingDataStore) {
if (!createIfNotFound)
return undefined;
dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
dataStore[dataStoreKey] = {};
}
return dataStore[dataStoreKey];
};
clear = function (node) {
var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
if (dataStoreKey) {
delete dataStore[dataStoreKey];
node[dataStoreKeyExpandoPropertyName] = null;
return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
}
return false;
};
}
// We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using
// it cross-window, so instead we just store the data directly on the node.
// See https://github.com/knockout/knockout/issues/2141
getDataForNode = function (node, createIfNotFound) {
var dataForNode = node[dataStoreKeyExpandoPropertyName];
if (!dataForNode && createIfNotFound) {
dataForNode = node[dataStoreKeyExpandoPropertyName] = {};
}
return dataForNode;
};
clear = function (node) {
if (node[dataStoreKeyExpandoPropertyName]) {
delete node[dataStoreKeyExpandoPropertyName];
return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
}
return false;
};
return {
get: function (node, key) {

View file

@ -13,10 +13,7 @@
'th': tr,
'option': select,
'optgroup': select
},
// This is needed for old IE if you're *not* using either jQuery or innerShiv. Doesn't affect other cases.
mayRequireCreateElementHack = ko.utils.ieVersion <= 8;
};
function getWrap(tags) {
var m = tags.match(/^(?:<!--.*?-->\s*?)*?<([a-z]+)[\s>]/);
@ -49,17 +46,7 @@
// somehow shims the native APIs so it just works anyway)
div.appendChild(windowContext['innerShiv'](markup));
} else {
if (mayRequireCreateElementHack) {
// The document.createElement('my-element') trick to enable custom elements in IE6-8
// only works if we assign innerHTML on an element associated with that document.
documentContext.body.appendChild(div);
}
div.innerHTML = markup;
if (mayRequireCreateElementHack) {
div.parentNode.removeChild(div);
}
}
// Move to the right depth
@ -69,35 +56,8 @@
return ko.utils.makeArray(div.lastChild.childNodes);
}
function jQueryHtmlParse(html, documentContext) {
// jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
if (jQueryInstance['parseHTML']) {
return jQueryInstance['parseHTML'](html, documentContext) || []; // Ensure we always return an array and never null
} else {
// For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
var elems = jQueryInstance['clean']([html], documentContext);
// As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
// Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
// Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
if (elems && elems[0]) {
// Find the top-most parent element that's a direct child of a document fragment
var elem = elems[0];
while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
elem = elem.parentNode;
// ... then detach it
if (elem.parentNode)
elem.parentNode.removeChild(elem);
}
return elems;
}
}
ko.utils.parseHtmlFragment = function(html, documentContext) {
return jQueryInstance ?
jQueryHtmlParse(html, documentContext) : // As below, benefit from jQuery's optimisations where possible
simpleHtmlParse(html, documentContext); // ... otherwise, this simple logic will do in most common cases.
return simpleHtmlParse(html, documentContext); // ... otherwise, this simple logic will do in most common cases.
};
ko.utils.parseHtmlForTemplateNodes = function(html, documentContext) {
@ -118,14 +78,10 @@
// jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
// for example <tr> elements which are not normally allowed to exist on their own.
// If you've referenced jQuery we'll use that rather than duplicating its code.
if (jQueryInstance) {
jQueryInstance(node)['html'](html);
} else {
// ... otherwise, use KO's own parsing logic.
var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);
for (var i = 0; i < parsedNodes.length; i++)
node.appendChild(parsedNodes[i]);
}
// ... otherwise, use KO's own parsing logic.
var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);
for (var i = 0; i < parsedNodes.length; i++)
node.appendChild(parsedNodes[i]);
}
};
})();

View file

@ -88,12 +88,12 @@ ko.utils.domNodeDisposal = new (function () {
node.parentNode.removeChild(node);
},
"cleanExternalData" : function (node) {
'cleanExternalData' : function (node) {
// Special support for jQuery here because it's so commonly used.
// Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
// so notify it to tear down any resources associated with the node & descendants here.
if (jQueryInstance && (typeof jQueryInstance['cleanData'] == "function"))
jQueryInstance['cleanData']([node]);
if (jQuery && jQuery['cleanData'])
jQuery['cleanData']([node]);
}
};
})();

View file

@ -1,21 +1,15 @@
ko.utils = (function () {
var hasOwnProperty = Object.prototype.hasOwnProperty;
function objectForEach(obj, action) {
for (var prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
action(prop, obj[prop]);
}
}
obj && Object.entries(obj).forEach(function(prop) {
action(prop[0], prop[1]);
});
}
function extend(target, source) {
if (source) {
for(var prop in source) {
if(hasOwnProperty.call(source, prop)) {
target[prop] = source[prop];
}
}
Object.entries(source).forEach(function(prop) {
target[prop[0]] = prop[1];
});
}
return target;
}
@ -26,98 +20,32 @@ ko.utils = (function () {
}
var canSetPrototype = ({ __proto__: [] } instanceof Array);
var canUseSymbols = !DEBUG && typeof Symbol === 'function';
// Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
var knownEvents = {}, knownEventTypesByEventName = {};
var keyEventTypeName = (navigator && /Firefox\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
objectForEach(knownEvents, function(eventType, knownEventsForType) {
if (knownEventsForType.length) {
for (var i = 0, j = knownEventsForType.length; i < j; i++)
knownEventTypesByEventName[knownEventsForType[i]] = eventType;
}
});
var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406
// Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
// Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
// Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
// If there is a future need to detect specific versions of IE10+, we will amend this.
var ieVersion = document && (function() {
var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
// Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
while (
div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
iElems[0]
) {}
return version > 4 ? version : undefined;
}());
var isIe6 = ieVersion === 6,
isIe7 = ieVersion === 7;
var ke = 'KeyboardEvent', me = 'MouseEvent',
knownEventTypesByEventName = {
keyup: ke, keydown: ke, keypress: ke,
click: me, dblclick: me, mousedown: me, mouseup: me, mousemove: me,
mouseover: me, mouseout: me, mouseenter: me, mouseleave: me
};
function isClickOnCheckableElement(element, eventType) {
if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
if ((element.nodeName !== "INPUT") || !element.type) return false;
if (eventType.toLowerCase() != "click") return false;
var inputType = element.type;
return (inputType == "checkbox") || (inputType == "radio");
}
// For details on the pattern for changing node classes
// see: https://github.com/knockout/knockout/issues/1597
var cssClassNameRegex = /\S+/g;
var jQueryEventAttachName;
function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
var addOrRemoveFn;
if (classNames) {
if (typeof node.classList === 'object') {
addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];
ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
addOrRemoveFn.call(node.classList, className);
});
} else if (typeof node.className['baseVal'] === 'string') {
// SVG tag .classNames is an SVGAnimatedString instance
toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);
} else {
// node.className ought to be a string.
toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);
}
}
}
function toggleObjectClassPropertyString(obj, prop, classNames, shouldHaveClass) {
// obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.
var currentClassNames = obj[prop].match(cssClassNameRegex) || [];
ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);
});
obj[prop] = currentClassNames.join(" ");
}
return {
fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],
arrayForEach: function (array, action, actionOwner) {
for (var i = 0, j = array.length; i < j; i++) {
action.call(actionOwner, array[i], i, array);
}
},
arrayIndexOf: typeof Array.prototype.indexOf == "function"
? function (array, item) {
return Array.prototype.indexOf.call(array, item);
}
: function (array, item) {
for (var i = 0, j = array.length; i < j; i++) {
if (array[i] === item)
return i;
}
return -1;
},
arrayIndexOf: function (array, item) {
return Array.prototype.indexOf.call(array, item);
},
arrayFirst: function (array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
@ -137,34 +65,8 @@ ko.utils = (function () {
}
},
arrayGetDistinctValues: function (array) {
var result = [];
if (array) {
ko.utils.arrayForEach(array, function(item) {
if (ko.utils.arrayIndexOf(result, item) < 0)
result.push(item);
});
}
return result;
},
arrayMap: function (array, mapping, mappingOwner) {
var result = [];
if (array) {
for (var i = 0, j = array.length; i < j; i++)
result.push(mapping.call(mappingOwner, array[i], i));
}
return result;
},
arrayFilter: function (array, predicate, predicateOwner) {
var result = [];
if (array) {
for (var i = 0, j = array.length; i < j; i++)
if (predicate.call(predicateOwner, array[i], i))
result.push(array[i]);
}
return result;
return array ? Array.prototype.filter.call(array, predicate, predicateOwner) : [];
},
arrayPushAll: function (array, valuesToPush) {
@ -176,17 +78,6 @@ ko.utils = (function () {
return array;
},
addOrRemoveItem: function(array, value, included) {
var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.peekObservable(array), value);
if (existingEntryIndex < 0) {
if (included)
array.push(value);
} else {
if (!included)
array.splice(existingEntryIndex, 1);
}
},
canSetPrototype: canSetPrototype,
extend: extend,
@ -201,11 +92,9 @@ ko.utils = (function () {
if (!source)
return source;
var target = {};
for (var prop in source) {
if (hasOwnProperty.call(source, prop)) {
target[prop] = mapping.call(mappingOwner, source[prop], prop, source);
}
}
Object.entries(source).forEach(function(prop) {
target[prop[0]] = mapping.call(mappingOwner, prop[1], prop[0], source);
});
return target;
},
@ -245,13 +134,15 @@ ko.utils = (function () {
},
replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType
? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
if (nodesToReplaceArray.length > 0) {
var insertionPoint = nodesToReplaceArray[0];
var parent = insertionPoint.parentNode;
for (var i = 0, j = newNodesArray.length; i < j; i++)
var insertionPoint = nodesToReplaceArray[0],
parent = insertionPoint.parentNode,
i, j;
for (i = 0, j = newNodesArray.length; i < j; i++)
parent.insertBefore(newNodesArray[i], insertionPoint);
for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
for (i = 0, j = nodesToReplaceArray.length; i < j; i++) {
ko.removeNode(nodesToReplaceArray[i]);
}
}
@ -283,7 +174,8 @@ ko.utils = (function () {
continuousNodeArray.splice(0, 1);
// Rule [B]
while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
while (continuousNodeArray.length > 1
&& continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
continuousNodeArray.length--;
// Rule [C]
@ -301,14 +193,6 @@ ko.utils = (function () {
return continuousNodeArray;
},
setOptionNodeSelectionState: function (optionNode, isSelected) {
// IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
if (ieVersion < 7)
optionNode.setAttribute("selected", isSelected);
else
optionNode.selected = isSelected;
},
stringTrim: function (string) {
return string === null || string === undefined ? '' :
string.trim ?
@ -324,18 +208,7 @@ ko.utils = (function () {
},
domNodeIsContainedBy: function (node, containedByNode) {
if (node === containedByNode)
return true;
if (node.nodeType === 11)
return false; // Fixes issue #1162 - can't use node.contains for document fragments on IE8
if (containedByNode.contains)
return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node);
if (containedByNode.compareDocumentPosition)
return (containedByNode.compareDocumentPosition(node) & 16) == 16;
while (node && node != containedByNode) {
node = node.parentNode;
}
return !!node;
return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node);
},
domNodeIsAttachedToDocument: function (node) {
@ -378,26 +251,10 @@ ko.utils = (function () {
registerEventHandler: function (element, eventType, handler) {
var wrappedHandler = ko.utils.catchFunctionErrors(handler);
var mustUseAttachEvent = eventsThatMustBeRegisteredUsingAttachEvent[eventType];
if (!ko.options['useOnlyNativeEvents'] && !mustUseAttachEvent && jQueryInstance) {
if (!jQueryEventAttachName) {
jQueryEventAttachName = (typeof jQueryInstance(element)['on'] == 'function') ? 'on' : 'bind';
}
jQueryInstance(element)[jQueryEventAttachName](eventType, wrappedHandler);
} else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
element.addEventListener(eventType, wrappedHandler, false);
else if (typeof element.attachEvent != "undefined") {
var attachEventHandler = function (event) { wrappedHandler.call(element, event); },
attachEventName = "on" + eventType;
element.attachEvent(attachEventName, attachEventHandler);
// IE does not dispose attachEvent handlers automatically (unlike with addEventListener)
// so to avoid leaks, we have to remove them manually. See bug #856
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
element.detachEvent(attachEventName, attachEventHandler);
});
if (!ko.options['useOnlyNativeEvents'] && jQuery) {
jQuery(element)['on'](eventType, wrappedHandler);
} else
throw new Error("Browser doesn't support addEventListener or attachEvent");
element.addEventListener(eventType, wrappedHandler, false);
},
triggerEvent: function (element, eventType) {
@ -410,23 +267,13 @@ ko.utils = (function () {
// In both cases, we'll use the click method instead.
var useClickWorkaround = isClickOnCheckableElement(element, eventType);
if (!ko.options['useOnlyNativeEvents'] && jQueryInstance && !useClickWorkaround) {
jQueryInstance(element)['trigger'](eventType);
} else if (typeof document.createEvent == "function") {
if (typeof element.dispatchEvent == "function") {
var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
var event = document.createEvent(eventCategory);
event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
element.dispatchEvent(event);
}
else
throw new Error("The supplied element doesn't support dispatchEvent");
} else if (useClickWorkaround && element.click) {
element.click();
} else if (typeof element.fireEvent != "undefined") {
element.fireEvent("on" + eventType);
if (!ko.options['useOnlyNativeEvents'] && jQuery && !useClickWorkaround) {
jQuery(element)['trigger'](eventType);
} else {
throw new Error("Browser doesn't support triggering events");
var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
var event = document.createEvent(eventCategory);
event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
element.dispatchEvent(event);
}
},
@ -438,8 +285,6 @@ ko.utils = (function () {
return ko.isObservable(value) ? value.peek() : value;
},
toggleDomNodeCssClass: toggleDomNodeCssClass,
setTextContent: function(element, textContent) {
var value = ko.utils.unwrapObservable(textContent);
if ((value === null) || (value === undefined))
@ -454,142 +299,10 @@ ko.utils = (function () {
} else {
innerTextNode.data = value;
}
ko.utils.forceRefresh(element);
},
setElementName: function(element, name) {
element.name = name;
// Workaround IE 6/7 issue
// - https://github.com/SteveSanderson/knockout/issues/197
// - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
if (ieVersion <= 7) {
try {
var escapedName = element.name.replace(/[&<>'"]/g, function(r){ return "&#" + r.charCodeAt(0) + ";"; });
element.mergeAttributes(document.createElement("<input name='" + escapedName + "'/>"), false);
}
catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
}
},
forceRefresh: function(node) {
// Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
if (ieVersion >= 9) {
// For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
var elem = node.nodeType == 1 ? node : node.parentNode;
if (elem.style)
elem.style.zoom = elem.style.zoom;
}
},
ensureSelectElementIsRenderedCorrectly: function(selectElement) {
// Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
// (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
// Also fixes IE7 and IE8 bug that causes selects to be zero width if enclosed by 'if' or 'with'. (See issue #839)
if (ieVersion) {
var originalWidth = selectElement.style.width;
selectElement.style.width = 0;
selectElement.style.width = originalWidth;
}
},
range: function (min, max) {
min = ko.utils.unwrapObservable(min);
max = ko.utils.unwrapObservable(max);
var result = [];
for (var i = min; i <= max; i++)
result.push(i);
return result;
},
makeArray: function(arrayLikeObject) {
var result = [];
for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
result.push(arrayLikeObject[i]);
};
return result;
},
createSymbolOrString: function(identifier) {
return canUseSymbols ? Symbol(identifier) : identifier;
},
isIe6 : isIe6,
isIe7 : isIe7,
ieVersion : ieVersion,
getFormFields: function(form, fieldName) {
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
var matches = [];
for (var i = fields.length - 1; i >= 0; i--) {
if (isMatchingField(fields[i]))
matches.push(fields[i]);
};
return matches;
},
parseJson: function (jsonString) {
if (typeof jsonString == "string") {
jsonString = ko.utils.stringTrim(jsonString);
if (jsonString) {
if (JSON && JSON.parse) // Use native parsing where available
return JSON.parse(jsonString);
return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
}
}
return null;
},
stringifyJson: function (data, replacer, space) { // replacer and space are optional
if (!JSON || !JSON.stringify)
throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
},
postJson: function (urlOrForm, data, options) {
options = options || {};
var params = options['params'] || {};
var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
var url = urlOrForm;
// If we were given a form, use its 'action' URL and pick out any requested field values
if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
var originalForm = urlOrForm;
url = originalForm.action;
for (var i = includeFields.length - 1; i >= 0; i--) {
var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
for (var j = fields.length - 1; j >= 0; j--)
params[fields[j].name] = fields[j].value;
}
}
data = ko.utils.unwrapObservable(data);
var form = document.createElement("form");
form.style.display = "none";
form.action = url;
form.method = "post";
for (var key in data) {
// Since 'data' this is a model object, we include all properties including those inherited from its prototype
var input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
form.appendChild(input);
}
objectForEach(params, function(key, value) {
var input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = value;
form.appendChild(input);
});
document.body.appendChild(form);
options['submitter'] ? options['submitter'](form) : form.submit();
setTimeout(function () { form.parentNode.removeChild(form); }, 0);
return Array['from'](arrayLikeObject);
}
}
}());
@ -598,47 +311,17 @@ ko.exportSymbol('utils', ko.utils);
ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
ko.exportSymbol('utils.cloneNodes', ko.utils.cloneNodes);
ko.exportSymbol('utils.createSymbolOrString', ko.utils.createSymbolOrString);
ko.exportSymbol('utils.extend', ko.utils.extend);
ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
ko.exportSymbol('utils.objectMap', ko.utils.objectMap);
ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
ko.exportSymbol('utils.postJson', ko.utils.postJson);
ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
ko.exportSymbol('utils.range', ko.utils.range);
ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
ko.exportSymbol('utils.objectForEach', ko.utils.objectForEach);
ko.exportSymbol('utils.addOrRemoveItem', ko.utils.addOrRemoveItem);
ko.exportSymbol('utils.setTextContent', ko.utils.setTextContent);
ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly
if (!Function.prototype['bind']) {
// Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
// In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
Function.prototype['bind'] = function (object) {
var originalFunction = this;
if (arguments.length === 1) {
return function () {
return originalFunction.apply(object, arguments);
};
} else {
var partialArgs = Array.prototype.slice.call(arguments, 1);
return function () {
var args = partialArgs.slice(0);
args.push.apply(args, arguments);
return originalFunction.apply(object, args);
};
}
};
}