KnockoutJS to ES2015/ES6

TODO: use the new Proxy for ko.observable
This commit is contained in:
djmaze 2020-10-04 21:58:13 +02:00
parent a0f8ac0dad
commit 72ed2b08b2
56 changed files with 941 additions and 1873 deletions

67
vendors/knockout/src/binding/bindingAttributeSyntax.js vendored Executable file → Normal file
View file

@ -1,4 +1,4 @@
(function () {
(() => {
// Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294
var contextSubscribable = Symbol('_subscribable');
var contextAncestorBindingInfo = Symbol('_ancestorBindingInfo');
@ -19,9 +19,7 @@
};
// Use an overridable method for retrieving binding handlers so that plugins may support dynamically created handlers
ko['getBindingHandler'] = function(bindingKey) {
return ko.bindingHandlers[bindingKey];
};
ko['getBindingHandler'] = bindingKey => ko.bindingHandlers[bindingKey];
var inheritParentVm = {};
@ -93,7 +91,6 @@
shouldInheritData = dataItemOrAccessor === inheritParentVm,
realDataItemOrAccessor = shouldInheritData ? undefined : dataItemOrAccessor,
isFunc = typeof(realDataItemOrAccessor) == "function" && !ko.isObservable(realDataItemOrAccessor),
nodes,
subscribable,
dataDependency = options && options['dataDependency'];
@ -132,14 +129,14 @@
if (dataItemAlias && options && options['noChildContext']) {
var isFunc = typeof(dataItemOrAccessor) == "function" && !ko.isObservable(dataItemOrAccessor);
return new ko.bindingContext(inheritParentVm, this, null, function (self) {
return new ko.bindingContext(inheritParentVm, this, null, self => {
if (extendCallback)
extendCallback(self);
self[dataItemAlias] = isFunc ? dataItemOrAccessor() : dataItemOrAccessor;
}, options);
}
return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, function (self, parentContext) {
return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, (self, parentContext) => {
// Extend the context hierarchy by setting the appropriate pointers
self['$parentContext'] = parentContext;
self['$parent'] = parentContext['$data'];
@ -154,9 +151,9 @@
// Similarly to "child" contexts, provide a function here to make sure that the correct values are set
// when an observable view model is updated.
ko.bindingContext.prototype['extend'] = function(properties, options) {
return new ko.bindingContext(inheritParentVm, this, null, function(self, parentContext) {
ko.utils.extend(self, typeof(properties) == "function" ? properties(self) : properties);
}, options);
return new ko.bindingContext(inheritParentVm, this, null, self =>
ko.utils.extend(self, typeof(properties) == "function" ? properties(self) : properties)
, options);
};
var boundElementDomDataKey = ko.utils.domData.nextKey();
@ -209,7 +206,7 @@
childrenComplete: "childrenComplete",
descendantsComplete : "descendantsComplete",
subscribe: function (node, event, callback, context, options) {
subscribe: (node, event, callback, context, options) => {
var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
if (!bindingInfo.eventSubscribable) {
bindingInfo.eventSubscribable = new ko.subscribable;
@ -220,7 +217,7 @@
return bindingInfo.eventSubscribable.subscribe(callback, context, event);
},
notify: function (node, event) {
notify: (node, event) => {
var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
if (bindingInfo) {
bindingInfo.notifiedEvents[event] = true;
@ -239,7 +236,7 @@
}
},
startPossiblyAsyncContentBinding: function (node, bindingContext) {
startPossiblyAsyncContentBinding: (node, bindingContext) => {
var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
if (!bindingInfo.asyncContext) {
@ -251,7 +248,7 @@
return bindingContext;
}
return bindingContext['extend'](function (ctx) {
return bindingContext['extend'](ctx => {
ctx[contextAncestorBindingInfo] = bindingInfo;
});
}
@ -439,28 +436,19 @@
// context update), just return the value accessor from the binding. Otherwise, return a function that always gets
// the latest binding value and registers a dependency on the binding updater.
var getValueAccessor = bindingsUpdater
? function(bindingKey) {
return function() {
return evaluateValueAccessor(bindingsUpdater()[bindingKey]);
};
} : function(bindingKey) {
return bindings[bindingKey];
};
? bindingKey => () => evaluateValueAccessor(bindingsUpdater()[bindingKey])
: bindingKey => bindings[bindingKey];
// Use of allBindings as a function is maintained for backwards compatibility, but its use is deprecated
function allBindings() {
return ko.utils.objectMap(bindingsUpdater ? bindingsUpdater() : bindings, evaluateValueAccessor);
}
// The following is the 3.x allBindings API
allBindings['get'] = function(key) {
return bindings[key] && evaluateValueAccessor(getValueAccessor(key));
};
allBindings['has'] = function(key) {
return key in bindings;
};
allBindings['get'] = key => bindings[key] && evaluateValueAccessor(getValueAccessor(key));
allBindings['has'] = key => key in bindings;
if (ko.bindingEvent.childrenComplete in bindings) {
ko.bindingEvent.subscribe(node, ko.bindingEvent.childrenComplete, function () {
ko.bindingEvent.subscribe(node, ko.bindingEvent.childrenComplete, () => {
var callback = evaluateValueAccessor(bindings[ko.bindingEvent.childrenComplete]);
if (callback) {
var nodes = ko.virtualElements.childNodes(node);
@ -499,7 +487,7 @@
try {
// Run init, ignoring any dependencies
if (typeof handlerInitFn == "function") {
ko.dependencyDetection.ignore(function() {
ko.dependencyDetection.ignore(() => {
var initResult = handlerInitFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);
// If this binding handler claims to control descendant bindings, make a note of this
@ -514,9 +502,7 @@
// Run update in its own computed wrapper
if (typeof handlerUpdateFn == "function") {
ko.dependentObservable(
function() {
handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);
},
() => handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend),
null,
{ disposeWhenNodeIsRemoved: node }
);
@ -533,9 +519,9 @@
'shouldBindDescendants': shouldBindDescendants,
'bindingContextForDescendants': shouldBindDescendants && contextToExtend
};
};
}
ko.storedBindingContextForNode = function (node) {
ko.storedBindingContextForNode = node => {
var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
return bindingInfo && bindingInfo.context;
}
@ -546,16 +532,15 @@
: new ko.bindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);
}
ko.applyBindingAccessorsToNode = function (node, bindings, viewModelOrBindingContext) {
return applyBindingsToNodeInternal(node, bindings, getBindingContext(viewModelOrBindingContext));
};
ko.applyBindingAccessorsToNode = (node, bindings, viewModelOrBindingContext) =>
applyBindingsToNodeInternal(node, bindings, getBindingContext(viewModelOrBindingContext));
ko.applyBindingsToNode = function (node, bindings, viewModelOrBindingContext) {
ko.applyBindingsToNode = (node, bindings, viewModelOrBindingContext) => {
var context = getBindingContext(viewModelOrBindingContext);
return ko.applyBindingAccessorsToNode(node, makeBindingAccessors(bindings, context, node), context);
};
ko.applyBindingsToDescendants = function(viewModelOrBindingContext, rootNode) {
ko.applyBindingsToDescendants = (viewModelOrBindingContext, rootNode) => {
if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
applyBindingsToDescendantsInternal(getBindingContext(viewModelOrBindingContext), rootNode);
};
@ -574,13 +559,13 @@
};
// Retrieving binding context from arbitrary nodes
ko.contextFor = function(node) {
ko.contextFor = node => {
// We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
if (node && (node.nodeType === 1 || node.nodeType === 8)) {
return ko.storedBindingContextForNode(node);
}
};
ko.dataFor = function(node) {
ko.dataFor = node => {
var context = ko.contextFor(node);
return context ? context['$data'] : undefined;
};

View file

@ -6,7 +6,7 @@
};
ko.utils.extend(ko.bindingProvider.prototype, {
'nodeHasBindings': function(node) {
'nodeHasBindings': node => {
switch (node.nodeType) {
case 1: // Element
return node.getAttribute(defaultBindingAttributeName) != null
@ -31,12 +31,12 @@
// The following function is only used internally by this default provider.
// It's not part of the interface definition for a general binding provider.
'getBindingsString': function(node, bindingContext) {
'getBindingsString': (node, bindingContext) => {
switch (node.nodeType) {
case 1: return node.getAttribute(defaultBindingAttributeName); // Element
case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
default: return null;
}
return null;
},
// The following function is only used internally by this default provider.

View file

@ -1,6 +1,6 @@
var attrHtmlToJavaScriptMap = { 'class': 'className', 'for': 'htmlFor' };
ko.bindingHandlers['attr'] = {
'update': function(element, valueAccessor, allBindings) {
'update': (element, valueAccessor, allBindings) => {
var value = ko.utils.unwrapObservable(valueAccessor()) || {};
ko.utils.objectForEach(value, function(attrName, attrValue) {
attrValue = ko.utils.unwrapObservable(attrValue);

View file

@ -1,137 +0,0 @@
(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) {
var checkedValue = ko.pureComputed(function() {
// Treat "value" like "checkedValue" when it is included with "checked" binding
if (allBindings['has']('checkedValue')) {
return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
} else if (useElementValue) {
if (allBindings['has']('value')) {
return ko.utils.unwrapObservable(allBindings.get('value'));
} else {
return element.value;
}
}
});
function updateModel() {
// This updates the model value from the view value.
// It runs in response to DOM events (click) and changes in checkedValue.
var isChecked = element.checked,
elemValue = checkedValue();
// When we're first setting up this computed, don't change any model state.
if (ko.computedContext.isInitial()) {
return;
}
// We can ignore unchecked radio buttons, because some other radio
// button will be checked, and that one can take care of updating state.
// Also ignore value changes to an already unchecked checkbox.
if (!isChecked && (isRadio || ko.computedContext.getDependenciesCount())) {
return;
}
var modelValue = ko.dependencyDetection.ignore(valueAccessor);
if (valueIsArray) {
var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue,
saveOldValue = oldElemValue;
oldElemValue = elemValue;
if (saveOldValue !== elemValue) {
// When we're responding to the checkedValue changing, and the element is
// currently checked, replace the old elem value with the new elem value
// in the model array.
if (isChecked) {
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.
addOrRemoveItem(writableValue, elemValue, isChecked);
}
if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
modelValue(writableValue);
}
} else {
if (isCheckbox) {
if (elemValue === undefined) {
elemValue = isChecked;
} else if (!isChecked) {
elemValue = undefined;
}
}
ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
}
};
function updateView() {
// This updates the view value from the model value.
// It runs in response to changes in the bound (checked) value.
var modelValue = ko.utils.unwrapObservable(valueAccessor()),
elemValue = checkedValue();
if (valueIsArray) {
// When a checkbox is bound to an array, being checked represents its value being present in that array
element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
oldElemValue = elemValue;
} else if (isCheckbox && elemValue === undefined) {
// When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
// being checked represents the value being trueish
element.checked = !!modelValue;
} else {
// Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
element.checked = (checkedValue() === modelValue);
}
};
var isCheckbox = element.type == "checkbox",
isRadio = element.type == "radio";
// Only bind to check boxes and radio buttons
if (!isCheckbox && !isRadio) {
return;
}
var rawValue = valueAccessor(),
valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),
rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),
useElementValue = isRadio || valueIsArray,
oldElemValue = valueIsArray ? checkedValue() : undefined;
// Set up two computeds to update the binding:
// The first responds to changes in the checkedValue value and to element clicks
ko.computed(updateModel, null, { disposeWhenNodeIsRemoved: element });
ko.utils.registerEventHandler(element, "click", updateModel);
// The second responds to changes in the model value (the one associated with the checked binding)
ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
rawValue = undefined;
}
};
ko.expressionRewriting.twoWayBindings['checked'] = true;
ko.bindingHandlers['checkedValue'] = {
'update': function (element, valueAccessor) {
element.value = ko.utils.unwrapObservable(valueAccessor());
}
};
})();

View file

@ -1,7 +1,7 @@
var classesWrittenByBindingKey = '__ko__cssValue';
ko.bindingHandlers['class'] = {
'update': function (element, valueAccessor) {
'update': (element, valueAccessor) => {
var value = ko.utils.stringTrim(ko.utils.unwrapObservable(valueAccessor()));
ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
element[classesWrittenByBindingKey] = value;
@ -10,10 +10,10 @@ ko.bindingHandlers['class'] = {
};
ko.bindingHandlers['css'] = {
'update': function (element, valueAccessor) {
'update': (element, valueAccessor) => {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value !== null && typeof value == "object") {
ko.utils.objectForEach(value, function(className, shouldHaveClass) {
ko.utils.objectForEach(value, (className, shouldHaveClass) => {
shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);
ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
});

View file

@ -1,5 +1,5 @@
ko.bindingHandlers['enable'] = {
'update': function (element, valueAccessor) {
'update': (element, valueAccessor) => {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value && element.disabled)
element.removeAttribute("disabled");
@ -9,7 +9,6 @@ ko.bindingHandlers['enable'] = {
};
ko.bindingHandlers['disable'] = {
'update': function (element, valueAccessor) {
ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
}
'update': (element, valueAccessor) =>
ko.bindingHandlers['enable']['update'](element, () => !ko.utils.unwrapObservable(valueAccessor()))
};

View file

@ -3,7 +3,7 @@
function makeEventHandlerShortcut(eventName) {
ko.bindingHandlers[eventName] = {
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var newValueAccessor = function () {
var newValueAccessor = () => {
var result = {};
result[eventName] = valueAccessor();
return result;
@ -14,9 +14,9 @@ function makeEventHandlerShortcut(eventName) {
}
ko.bindingHandlers['event'] = {
'init' : function (element, valueAccessor, allBindings, viewModel, bindingContext) {
'init' : (element, valueAccessor, allBindings, viewModel, bindingContext) => {
var eventsToHandle = valueAccessor() || {};
ko.utils.objectForEach(eventsToHandle, function(eventName) {
ko.utils.objectForEach(eventsToHandle, eventName => {
if (typeof eventName == "string") {
ko.utils.registerEventHandler(element, eventName, function (event) {
var handlerReturnValue;

View file

@ -1,8 +1,8 @@
// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
ko.bindingHandlers['foreach'] = {
makeTemplateValueAccessor: function(valueAccessor) {
return function() {
makeTemplateValueAccessor: valueAccessor => {
return () => {
var modelValue = valueAccessor(),
unwrappedValue = ko.utils.peekObservable(modelValue); // Unwrap without setting a dependency here
@ -28,12 +28,11 @@ ko.bindingHandlers['foreach'] = {
};
};
},
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
},
'update': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext);
}
'init': (element, valueAccessor) =>
ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor))
,
'update': (element, valueAccessor, allBindings, viewModel, bindingContext) =>
ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext)
};
ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
ko.virtualElements.allowedBindings['foreach'] = true;

View file

@ -1,8 +1,8 @@
var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
var hasfocusLastValue = '__ko_hasfocusLastValue';
ko.bindingHandlers['hasfocus'] = {
'init': function(element, valueAccessor, allBindings) {
var handleElementFocusChange = function(isFocused) {
'init': (element, valueAccessor, allBindings) => {
var handleElementFocusChange = isFocused => {
// Where possible, ignore which event was raised and determine focus state using activeElement,
// as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
// However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
@ -29,7 +29,7 @@ ko.bindingHandlers['hasfocus'] = {
// Assume element is not focused (prevents "blur" being called initially)
element[hasfocusLastValue] = false;
},
'update': function(element, valueAccessor) {
'update': (element, valueAccessor) => {
var value = !!ko.utils.unwrapObservable(valueAccessor());
if (!element[hasfocusUpdatingProperty] && element[hasfocusLastValue] !== value) {

View file

@ -1,10 +1,9 @@
ko.bindingHandlers['html'] = {
'init': function() {
'init': () => {
// Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
return { 'controlsDescendantBindings': true };
},
'update': function (element, valueAccessor) {
'update': (element, valueAccessor) =>
// setHtml will unwrap the value if needed
ko.utils.setHtml(element, valueAccessor());
}
ko.utils.setHtml(element, valueAccessor())
};

View file

@ -1,81 +0,0 @@
(function () {
// Makes a binding like with or if
function makeWithIfBinding(bindingKey, isWith, isNot) {
ko.bindingHandlers[bindingKey] = {
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var didDisplayOnLastUpdate, savedNodes, contextOptions = {}, completeOnRender, needAsyncContext, renderOnEveryChange;
if (isWith) {
var as = allBindings.get('as'), noChildContext = allBindings.get('noChildContext');
renderOnEveryChange = !(as && noChildContext);
contextOptions = { 'as': as, 'noChildContext': noChildContext, 'exportDependencies': renderOnEveryChange };
}
completeOnRender = allBindings.get("completeOn") == "render";
needAsyncContext = completeOnRender || allBindings['has'](ko.bindingEvent.descendantsComplete);
ko.computed(function() {
var value = ko.utils.unwrapObservable(valueAccessor()),
shouldDisplay = !isNot !== !value, // equivalent to isNot ? !value : !!value,
isInitial = !savedNodes,
childContext;
if (!renderOnEveryChange && shouldDisplay === didDisplayOnLastUpdate) {
return;
}
if (needAsyncContext) {
bindingContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);
}
if (shouldDisplay) {
if (!isWith || renderOnEveryChange) {
contextOptions['dataDependency'] = ko.computedContext.computed();
}
if (isWith) {
childContext = bindingContext['createChildContext'](typeof value == "function" ? value : valueAccessor, contextOptions);
} else if (ko.computedContext.getDependenciesCount()) {
childContext = bindingContext['extend'](null, contextOptions);
} else {
childContext = bindingContext;
}
}
// Save a copy of the inner nodes on the initial update, but only if we have dependencies.
if (isInitial && ko.computedContext.getDependenciesCount()) {
savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
}
if (shouldDisplay) {
if (!isInitial) {
ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(savedNodes));
}
ko.applyBindingsToDescendants(childContext, element);
} else {
ko.virtualElements.emptyNode(element);
if (!completeOnRender) {
ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
}
}
didDisplayOnLastUpdate = shouldDisplay;
}, null, { disposeWhenNodeIsRemoved: element });
return { 'controlsDescendantBindings': true };
}
};
ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
ko.virtualElements.allowedBindings[bindingKey] = true;
}
// Construct the actual binding handlers
makeWithIfBinding('if');
makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
makeWithIfBinding('with', true /* isWith */);
})();

View file

@ -1,10 +0,0 @@
ko.bindingHandlers['let'] = {
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// Make a modified binding context, with extra properties, and apply it to descendant elements
var innerContext = bindingContext['extend'](valueAccessor);
ko.applyBindingsToDescendants(innerContext, element);
return { 'controlsDescendantBindings': true };
}
};
ko.virtualElements.allowedBindings['let'] = true;

View file

@ -1,6 +1,6 @@
var captionPlaceholder = {};
ko.bindingHandlers['options'] = {
'init': function(element) {
'init': element => {
if (ko.utils.tagNameLower(element) !== "select")
throw new Error("options binding applies only to SELECT elements");
@ -12,9 +12,9 @@ ko.bindingHandlers['options'] = {
// Ensures that the binding processor doesn't try to bind the options
return { 'controlsDescendantBindings': true };
},
'update': function (element, valueAccessor, allBindings) {
'update': (element, valueAccessor, allBindings) => {
function selectedOptions() {
return ko.utils.arrayFilter(element.options, function (node) { return node.selected; });
return ko.utils.arrayFilter(element.options, node => node.selected);
}
var selectWasPreviouslyEmpty = element.length == 0,
@ -41,9 +41,9 @@ ko.bindingHandlers['options'] = {
unwrappedArray = [unwrappedArray];
// Filter out any entries marked as destroyed
filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
return includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
});
filteredArray = ko.utils.arrayFilter(unwrappedArray, item =>
includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy'])
);
// If caption is included, add it to the array
if (allBindings['has']('optionsCaption')) {
@ -95,10 +95,7 @@ ko.bindingHandlers['options'] = {
// By using a beforeRemove callback, we delay the removal until after new items are added. This fixes a selection
// problem in IE<=8 and Firefox. See https://github.com/knockout/knockout/issues/1208
arrayToDomNodeChildrenOptions['beforeRemove'] =
function (option) {
element.removeChild(option);
};
arrayToDomNodeChildrenOptions['beforeRemove'] = option => element.removeChild(option);
function setSelectionCallback(arrayEntry, newOptions) {
if (itemUpdate && valueAllowUnset) {
@ -119,7 +116,7 @@ ko.bindingHandlers['options'] = {
var callback = setSelectionCallback;
if (allBindings['has']('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') == "function") {
callback = function(arrayEntry, newOptions) {
callback = (arrayEntry, newOptions) => {
setSelectionCallback(arrayEntry, newOptions);
ko.dependencyDetection.ignore(allBindings.get('optionsAfterRender'), null, [newOptions[0], arrayEntry !== captionPlaceholder ? arrayEntry : undefined]);
}

View file

@ -1,41 +0,0 @@
ko.bindingHandlers['selectedOptions'] = {
'init': function (element, valueAccessor, allBindings) {
function updateFromView() {
var value = valueAccessor(), valueToWrite = [];
ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
if (node.selected)
valueToWrite.push(ko.selectExtensions.readValue(node));
});
ko.expressionRewriting.writeValueToProperty(value, allBindings, 'selectedOptions', valueToWrite);
}
function updateFromModel() {
var newValue = ko.utils.unwrapObservable(valueAccessor()),
previousScrollTop = element.scrollTop;
if (newValue && typeof newValue.length == "number") {
ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
node.selected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
});
}
element.scrollTop = previousScrollTop;
}
if (ko.utils.tagNameLower(element) != "select") {
throw new Error("selectedOptions binding applies only to SELECT elements");
}
var updateFromModelComputed;
ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
if (!updateFromModelComputed) {
ko.utils.registerEventHandler(element, "change", updateFromView);
updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
} else {
updateFromView();
}
}, null, { 'notifyImmediately': true });
},
'update': function() {} // Keep for backwards compatibility with code that may have wrapped binding
};
ko.expressionRewriting.twoWayBindings['selectedOptions'] = true;

View file

@ -1,7 +1,7 @@
ko.bindingHandlers['style'] = {
'update': function (element, valueAccessor) {
'update': (element, valueAccessor) => {
var value = ko.utils.unwrapObservable(valueAccessor() || {});
ko.utils.objectForEach(value, function(styleName, styleValue) {
ko.utils.objectForEach(value, (styleName, styleValue) => {
styleValue = ko.utils.unwrapObservable(styleValue);
if (styleValue === null || styleValue === undefined || styleValue === false) {
@ -13,9 +13,7 @@ ko.bindingHandlers['style'] = {
// Is styleName a custom CSS property?
element.style.setProperty(styleName, styleValue);
} else {
styleName = styleName.replace(/-(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
styleName = styleName.replace(/-(\w)/g, (all, letter) => letter.toUpperCase());
var previousStyle = element.style[styleName];
element.style[styleName] = styleValue;

View file

@ -1,8 +1,8 @@
ko.bindingHandlers['submit'] = {
'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
'init': (element, valueAccessor, allBindings, viewModel, bindingContext) => {
if (typeof valueAccessor() != "function")
throw new Error("The value for a submit binding must be a function");
ko.utils.registerEventHandler(element, "submit", function (event) {
ko.utils.registerEventHandler(element, "submit", event => {
var handlerReturnValue;
var value = valueAccessor();
try { handlerReturnValue = value.call(bindingContext['$data'], element); }

View file

@ -1,11 +1,10 @@
ko.bindingHandlers['text'] = {
'init': function() {
'init': () => {
// Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).
// It should also make things faster, as we no longer have to consider whether the text node might be bindable.
return { 'controlsDescendantBindings': true };
},
'update': function (element, valueAccessor) {
ko.utils.setTextContent(element, valueAccessor());
}
'update': (element, valueAccessor) =>
ko.utils.setTextContent(element, valueAccessor())
};
ko.virtualElements.allowedBindings['text'] = true;

View file

@ -1,13 +1,11 @@
(function () {
ko.bindingHandlers['textInput'] = {
'init': function (element, valueAccessor, allBindings) {
'init': (element, valueAccessor, allBindings) => {
var previousElementValue = element.value,
timeoutHandle,
elementValueBeforeEvent;
var updateModel = function (event) {
var updateModel = event => {
clearTimeout(timeoutHandle);
elementValueBeforeEvent = timeoutHandle = undefined;
@ -20,7 +18,7 @@ ko.bindingHandlers['textInput'] = {
}
};
var deferUpdateModel = function (event) {
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
@ -32,11 +30,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 ourUpdate = false;
var updateView = function () {
var updateView = () => {
var modelValue = ko.utils.unwrapObservable(valueAccessor());
if (modelValue === null || modelValue === undefined) {
@ -51,20 +45,17 @@ ko.bindingHandlers['textInput'] = {
// 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) {
ourUpdate = true; // Make sure we ignore events (propertychange) that result from updating the value
element.value = modelValue;
ourUpdate = false;
previousElementValue = element.value; // In case the browser changes the value (see #2281)
}
};
var onEvent = function (event, handler) {
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.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], function(eventName) {
ko.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], eventName => {
if (eventName.slice(0,5) == 'after') {
onEvent(eventName.slice(5), deferUpdateModel);
} else {
@ -89,9 +80,5 @@ 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': function (value, name, addBinding) {
addBinding('textInput', value);
}
'preprocess': (value, name, addBinding) => addBinding('textInput', value)
};
})();

View file

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

View file

@ -1,15 +0,0 @@
ko.bindingHandlers['using'] = {
'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var options;
if (allBindings['has']('as')) {
options = { 'as': allBindings.get('as'), 'noChildContext': allBindings.get('noChildContext') };
}
var innerContext = bindingContext['createChildContext'](valueAccessor, options);
ko.applyBindingsToDescendants(innerContext, element);
return { 'controlsDescendantBindings': true };
}
};
ko.virtualElements.allowedBindings['using'] = true;

View file

@ -1,5 +1,5 @@
ko.bindingHandlers['value'] = {
'init': function (element, valueAccessor, allBindings) {
'init': (element, valueAccessor, allBindings) => {
var tagName = ko.utils.tagNameLower(element),
isInputElement = tagName == "input";
@ -18,27 +18,27 @@ ko.bindingHandlers['value'] = {
if (typeof requestedEventsToCatch == "string") {
eventsToCatch = [requestedEventsToCatch];
} else {
eventsToCatch = requestedEventsToCatch ? requestedEventsToCatch.filter(function(item, index) {
return requestedEventsToCatch.indexOf(item) === index;
}) : [];
eventsToCatch = requestedEventsToCatch ? requestedEventsToCatch.filter((item, index) =>
requestedEventsToCatch.indexOf(item) === index
) : [];
}
ko.utils.arrayRemoveItem(eventsToCatch, "change"); // We'll subscribe to "change" events later
}
var valueUpdateHandler = function() {
var valueUpdateHandler = () => {
elementValueBeforeEvent = null;
var modelValue = valueAccessor();
var elementValue = ko.selectExtensions.readValue(element);
ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'value', elementValue);
}
ko.utils.arrayForEach(eventsToCatch, function(eventName) {
ko.utils.arrayForEach(eventsToCatch, 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
// (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
var handler = valueUpdateHandler;
if (ko.utils.stringStartsWith(eventName, "after")) {
handler = function() {
handler = () => {
// The elementValueBeforeEvent variable is non-null *only* during the brief gap between
// a keyX event firing and the valueUpdateHandler running, which is scheduled to happen
// at the earliest asynchronous opportunity. We store this temporary information so that
@ -58,7 +58,7 @@ ko.bindingHandlers['value'] = {
if (isInputElement && element.type == "file") {
// For file input elements, can only write the empty string
updateFromModel = function () {
updateFromModel = () => {
var newValue = ko.utils.unwrapObservable(valueAccessor());
if (newValue === null || newValue === undefined || newValue === "") {
element.value = "";
@ -67,7 +67,7 @@ ko.bindingHandlers['value'] = {
}
}
} else {
updateFromModel = function () {
updateFromModel = () => {
var newValue = ko.utils.unwrapObservable(valueAccessor());
var elementValue = ko.selectExtensions.readValue(element);
@ -96,7 +96,7 @@ ko.bindingHandlers['value'] = {
if (tagName === "select") {
var updateFromModelComputed;
ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, () => {
if (!updateFromModelComputed) {
ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
@ -111,6 +111,6 @@ ko.bindingHandlers['value'] = {
ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
}
},
'update': function() {} // Keep for backwards compatibility with code that may have wrapped value binding
'update': () => {} // Keep for backwards compatibility with code that may have wrapped value binding
};
ko.expressionRewriting.twoWayBindings['value'] = true;

View file

@ -1,5 +1,5 @@
ko.bindingHandlers['visible'] = {
'update': function (element, valueAccessor) {
'update': (element, valueAccessor) => {
var value = ko.utils.unwrapObservable(valueAccessor());
var isCurrentlyVisible = !(element.style.display == "none");
if (value && !isCurrentlyVisible)
@ -10,7 +10,6 @@ ko.bindingHandlers['visible'] = {
};
ko.bindingHandlers['hidden'] = {
'update': function (element, valueAccessor) {
ko.bindingHandlers['visible']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
}
'update': (element, valueAccessor) =>
ko.bindingHandlers['visible']['update'](element, () => !ko.utils.unwrapObservable(valueAccessor()) )
};

View file

@ -1,4 +1,4 @@
(function () {
(() => {
// Objective:
// * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
// map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
@ -12,7 +12,7 @@
function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
// Map this array value inside a dependentObservable so we re-map when any dependency changes
var mappedNodes = [];
var dependentObservable = ko.dependentObservable(function() {
var dependentObservable = ko.dependentObservable(() => {
var newMappedNodes = mapping(valueToMap, index, ko.utils.fixUpContinuousNodeArray(mappedNodes, containerNode)) || [];
// On subsequent evaluations, just replace the previously-inserted DOM nodes
@ -33,7 +33,7 @@
var lastMappingResultDomDataKey = ko.utils.domData.nextKey(),
deletedItemDummyValue = ko.utils.domData.nextKey();
ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes, editScript) {
ko.utils.setDomNodeChildrenFromArrayMapping = (domNode, array, mapping, options, callbackAfterAddingNodes, editScript) => {
array = array || [];
if (typeof array.length == "undefined") // Coerce single value into array
array = [array];
@ -88,7 +88,7 @@
} else {
if (!editScript || (lastMappingResult && lastMappingResult['_countWaitingForRemove'])) {
// Compare the provided array against the previous one
var lastArray = Array.prototype.map.call(lastMappingResult, function (x) { return x.arrayEntry; }),
var lastArray = Array.prototype.map.call(lastMappingResult, x => x.arrayEntry),
compareOptions = {
'dontLimitMoves': options['dontLimitMoves'],
'sparse': true
@ -96,7 +96,7 @@
editScript = ko.utils.compareArrays(lastArray, array, compareOptions);
}
for (var i = 0, editScriptItem, movedIndex, itemIndex; editScriptItem = editScript[i]; i++) {
for (let i = 0, editScriptItem, movedIndex, itemIndex; editScriptItem = editScript[i]; i++) {
movedIndex = editScriptItem['moved'];
itemIndex = editScriptItem['index'];
switch (editScriptItem['status']) {

View file

@ -1,5 +1,5 @@
// Go through the items that have been added and deleted and try to find matches between them.
ko.utils.findMovesInArrayComparison = function (left, right, limitFailedCompares) {
ko.utils.findMovesInArrayComparison = (left, right, limitFailedCompares) => {
if (left.length && right.length) {
var failedCompares, l, r, leftItem, rightItem;
for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
@ -17,7 +17,7 @@ ko.utils.findMovesInArrayComparison = function (left, right, limitFailedCompares
}
};
ko.utils.compareArrays = (function () {
ko.utils.compareArrays = (() => {
var statusNotInOld = 'added', statusNotInNew = 'deleted';
// Simple calculation based on Levenshtein distance.

View file

@ -1,4 +1,4 @@
ko.expressionRewriting = (function () {
ko.expressionRewriting = (() => {
var javaScriptReservedWords = ["true", "false", "null", "undefined"];
// Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
@ -28,7 +28,7 @@ ko.expressionRewriting = (function () {
"//.*\n",
// Match a regular expression (text enclosed by slashes), but will also match sets of divisions
// as a regular expression (this is handled by the parsing loop below).
'/(?:\\\\.|[^/])+/\w*',
'/(?:\\\\.|[^/])+/w*',
// Match text (at least two characters) that does not contain any of the above special characters,
// although some of the special characters are allowed to start it (all but the colon and comma).
// The text can contain spaces, but leading or trailing spaces are skipped.
@ -143,9 +143,9 @@ ko.expressionRewriting = (function () {
keyValueArray = typeof bindingsStringOrKeyValueArray === "string" ?
parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray;
ko.utils.arrayForEach(keyValueArray, function(keyValue) {
processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value);
});
ko.utils.arrayForEach(keyValueArray, keyValue =>
processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value)
);
if (propertyAccessorResultStrings.length)
processKeyValue('_ko_property_writers', "{" + propertyAccessorResultStrings.join(",") + " }");
@ -162,7 +162,7 @@ ko.expressionRewriting = (function () {
preProcessBindings: preProcessBindings,
keyValueArrayContainsKey: function(keyValueArray, key) {
keyValueArrayContainsKey: (keyValueArray, key) => {
for (var i = 0; i < keyValueArray.length; i++)
if (keyValueArray[i]['key'] == key)
return true;
@ -178,7 +178,7 @@ ko.expressionRewriting = (function () {
// value: The value to be written
// checkIfDifferent: If true, and if the property being written is a writable observable, the value will only be written if
// it is !== existing value on that writable observable
writeValueToProperty: function(property, allBindings, key, value, checkIfDifferent) {
writeValueToProperty: (property, allBindings, key, value, checkIfDifferent) => {
if (!property || !ko.isObservable(property)) {
var propWriters = allBindings.get('_ko_property_writers');
if (propWriters && propWriters[key])
@ -194,17 +194,3 @@ ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);
// Making bindings explicitly declare themselves as "two way" isn't ideal in the long term (it would be better if
// all bindings could use an official 'property writer' API without needing to declare that they might). However,
// since this is not, and has never been, a public API (_ko_property_writers was never documented), it's acceptable
// as an internal implementation detail in the short term.
// For those developers who rely on _ko_property_writers in their custom bindings, we expose _twoWayBindings as an
// undocumented feature that makes it relatively easy to upgrade to KO 3.0. However, this is still not an official
// public API, and we reserve the right to remove it at any time if we create a real public property writers API.
ko.exportSymbol('expressionRewriting._twoWayBindings', ko.expressionRewriting.twoWayBindings);
// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);

View file

@ -1,11 +1,11 @@
(function () {
(() => {
var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';
// Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
// are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
// that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
ko.selectExtensions = {
readValue : function(element) {
readValue : element => {
switch (ko.utils.tagNameLower(element)) {
case 'option':
if (element[hasDomDataExpandoProperty] === true)
@ -18,7 +18,7 @@
}
},
writeValue: function(element, value, allowUnset) {
writeValue: (element, value, allowUnset) => {
switch (ko.utils.tagNameLower(element)) {
case 'option':
if (typeof value === "string") {