mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-06 07:57:02 +03:00
Cleanup KnockoutJS
This commit is contained in:
parent
4c7ce61bc0
commit
4142526ba6
9 changed files with 121 additions and 279 deletions
|
|
@ -8,8 +8,6 @@
|
|||
// (0, eval)('this') is a robust way of getting a reference to the global object
|
||||
// For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023
|
||||
var document = window['document'],
|
||||
navigator = window['navigator'],
|
||||
JSON = window["JSON"],
|
||||
koExports = {};
|
||||
// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
|
||||
// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
|
||||
|
|
@ -55,7 +53,7 @@ ko.utils = {
|
|||
var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;
|
||||
|
||||
var container = templateDocument.createElement('div');
|
||||
nodes.forEach(node => container.append(ko.cleanNode(node)));
|
||||
nodesArray.forEach(node => container.append(ko.cleanNode(node)));
|
||||
return container;
|
||||
},
|
||||
|
||||
|
|
@ -97,7 +95,7 @@ ko.utils = {
|
|||
// Rule [B]
|
||||
while (continuousNodeArray.length > 1
|
||||
&& continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
|
||||
continuousNodeArray.length--;
|
||||
--continuousNodeArray.length;
|
||||
|
||||
// Rule [C]
|
||||
if (continuousNodeArray.length > 1) {
|
||||
|
|
@ -119,39 +117,11 @@ ko.utils = {
|
|||
string.trim() :
|
||||
string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''),
|
||||
|
||||
stringStartsWith: (string, startsWith) => {
|
||||
string = string || "";
|
||||
if (startsWith.length > string.length)
|
||||
return false;
|
||||
return string.substring(0, startsWith.length) === startsWith;
|
||||
},
|
||||
|
||||
domNodeIsContainedBy: (node, containedByNode) =>
|
||||
containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node),
|
||||
|
||||
domNodeIsAttachedToDocument: node => ko.utils.domNodeIsContainedBy(node, node.ownerDocument.documentElement),
|
||||
|
||||
catchFunctionErrors: delegate => {
|
||||
return ko['onError'] ? function () {
|
||||
try {
|
||||
return delegate.apply(this, arguments);
|
||||
} catch (e) {
|
||||
ko['onError'] && ko['onError'](e);
|
||||
throw e;
|
||||
}
|
||||
} : delegate;
|
||||
},
|
||||
|
||||
setTimeout: (handler, timeout) => setTimeout(ko.utils.catchFunctionErrors(handler), timeout),
|
||||
|
||||
deferError: error => setTimeout(() => {
|
||||
ko['onError'] && ko['onError'](error);
|
||||
throw error;
|
||||
}, 0),
|
||||
|
||||
registerEventHandler: (element, eventType, handler) =>
|
||||
element.addEventListener(eventType, ko.utils.catchFunctionErrors(handler), false),
|
||||
|
||||
triggerEvent: (element, eventType) => {
|
||||
if (!(element && element.nodeType))
|
||||
throw new Error("element must be a DOM node when calling triggerEvent");
|
||||
|
|
@ -306,7 +276,9 @@ ko.tasks = (() => {
|
|||
if (nextIndexToProcess > mark) {
|
||||
if (++countMarks >= 5000) {
|
||||
nextIndexToProcess = taskQueueLength; // skip all tasks remaining in the queue since any of them could be causing the recursion
|
||||
ko.utils.deferError(Error("'Too much recursion' after processing " + countMarks + " task groups."));
|
||||
setTimeout(() => {
|
||||
throw Error(`'Too much recursion' after processing ${countMarks} task groups.`)
|
||||
}, 0)
|
||||
break;
|
||||
}
|
||||
mark = taskQueueLength;
|
||||
|
|
@ -314,7 +286,7 @@ ko.tasks = (() => {
|
|||
try {
|
||||
task();
|
||||
} catch (ex) {
|
||||
ko.utils.deferError(ex);
|
||||
setTimeout(() => { throw ex }, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -381,7 +353,7 @@ function throttle(callback, timeout) {
|
|||
var timeoutInstance;
|
||||
return () => {
|
||||
if (!timeoutInstance) {
|
||||
timeoutInstance = ko.utils.setTimeout(() => {
|
||||
timeoutInstance = setTimeout(() => {
|
||||
timeoutInstance = 0;
|
||||
callback();
|
||||
}, timeout);
|
||||
|
|
@ -393,7 +365,7 @@ function debounce(callback, timeout) {
|
|||
var timeoutInstance;
|
||||
return () => {
|
||||
clearTimeout(timeoutInstance);
|
||||
timeoutInstance = ko.utils.setTimeout(callback, timeout);
|
||||
timeoutInstance = setTimeout(callback, timeout);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -915,7 +887,7 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
|
|||
case 'push':
|
||||
offset = arrayLength;
|
||||
case 'unshift':
|
||||
for (var index = 0; index < argsLength; index++) {
|
||||
for (let index = 0; index < argsLength; index++) {
|
||||
pushDiff('added', args[index], offset + index);
|
||||
}
|
||||
break;
|
||||
|
|
@ -936,7 +908,7 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
|
|||
endAddIndex = startIndex + argsLength - 2,
|
||||
endIndex = Math.max(endDeleteIndex, endAddIndex),
|
||||
additions = [], deletions = [];
|
||||
for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
|
||||
for (let index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
|
||||
if (index < endDeleteIndex)
|
||||
deletions.push(pushDiff('deleted', rawArray[index], index));
|
||||
if (index < endAddIndex)
|
||||
|
|
@ -1169,7 +1141,7 @@ var computedFn = {
|
|||
throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
|
||||
if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
|
||||
clearTimeout(this[computedState].evaluationTimeoutInstance);
|
||||
this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(() =>
|
||||
this[computedState].evaluationTimeoutInstance = setTimeout(() =>
|
||||
computedObservable.evaluateImmediate(true /*notifyChange*/)
|
||||
, throttleEvaluationTimeout);
|
||||
} else if (computedObservable._evalDelayed) {
|
||||
|
|
@ -2098,24 +2070,6 @@ ko.expressionRewriting = (() => {
|
|||
}
|
||||
};
|
||||
|
||||
// Given a function that returns bindings, create and return a new object that contains
|
||||
// binding value-accessors functions. Each accessor function calls the original function
|
||||
// so that it always gets the latest value and all dependencies are captured. This is used
|
||||
// by ko.applyBindingsToNode and getBindingsAndMakeAccessors.
|
||||
function makeAccessorsFromFunction(callback) {
|
||||
return ko.utils.objectMap(ko.dependencyDetection.ignore(callback), (value, key) =>
|
||||
() => callback()[key]
|
||||
);
|
||||
}
|
||||
|
||||
// Given a bindings function or object, create and return a new object that contains
|
||||
// binding value-accessors functions. This is used by ko.applyBindingsToNode.
|
||||
function makeBindingAccessors(bindings, context, node) {
|
||||
return (typeof bindings === 'function')
|
||||
? makeAccessorsFromFunction(bindings.bind(null, context, node))
|
||||
: ko.utils.objectMap(bindings, value => () => value);
|
||||
}
|
||||
|
||||
function validateThatBindingIsAllowedForVirtualElements(bindingName) {
|
||||
var validator = ko.virtualElements.allowedBindings[bindingName];
|
||||
if (!validator)
|
||||
|
|
@ -2773,9 +2727,8 @@ ko.expressionRewriting = (() => {
|
|||
}
|
||||
|
||||
})();
|
||||
var attrHtmlToJavaScriptMap = { 'class': 'className', 'for': 'htmlFor' };
|
||||
ko.bindingHandlers['attr'] = {
|
||||
'update': (element, valueAccessor, allBindings) => {
|
||||
'update': (element, valueAccessor) => {
|
||||
var value = ko.utils.unwrapObservable(valueAccessor()) || {};
|
||||
ko.utils.objectForEach(value, function(attrName, attrValue) {
|
||||
attrValue = ko.utils.unwrapObservable(attrValue);
|
||||
|
|
@ -2787,7 +2740,7 @@ ko.bindingHandlers['attr'] = {
|
|||
// To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
|
||||
// when someProp is a "no value"-like value (strictly null, false, or undefined)
|
||||
// (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
|
||||
var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
|
||||
var toRemove = (attrValue === false) || (attrValue == null);
|
||||
if (toRemove) {
|
||||
namespace ? element.removeAttributeNS(namespace, attrName) : element.removeAttribute(attrName);
|
||||
} else {
|
||||
|
|
@ -2867,7 +2820,7 @@ ko.bindingHandlers['event'] = {
|
|||
var eventsToHandle = valueAccessor() || {};
|
||||
ko.utils.objectForEach(eventsToHandle, eventName => {
|
||||
if (typeof eventName == "string") {
|
||||
ko.utils.registerEventHandler(element, eventName, function (event) {
|
||||
element.addEventListener(eventName, function (event) {
|
||||
var handlerReturnValue;
|
||||
var handlerFunction = valueAccessor()[eventName];
|
||||
if (!handlerFunction)
|
||||
|
|
@ -2942,10 +2895,13 @@ ko.bindingHandlers['hasfocus'] = {
|
|||
var handleElementFocusIn = handleElementFocusChange.bind(null, true);
|
||||
var handleElementFocusOut = handleElementFocusChange.bind(null, false);
|
||||
|
||||
ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
|
||||
ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn);
|
||||
ko.utils.registerEventHandler(element, "blur", handleElementFocusOut);
|
||||
ko.utils.registerEventHandler(element, "focusout", handleElementFocusOut);
|
||||
var registerEventHandler = (event, handler) =>
|
||||
element.addEventListener(event, handler);
|
||||
|
||||
registerEventHandler("focus", handleElementFocusIn);
|
||||
registerEventHandler("focusin", handleElementFocusIn);
|
||||
registerEventHandler("blur", handleElementFocusOut);
|
||||
registerEventHandler("focusout", handleElementFocusOut);
|
||||
|
||||
// Assume element is not focused (prevents "blur" being called initially)
|
||||
element[hasfocusLastValue] = false;
|
||||
|
|
@ -2984,10 +2940,10 @@ ko.bindingHandlers['html'] = {
|
|||
function makeWithIfBinding(bindingKey, isWith, isNot) {
|
||||
ko.bindingHandlers[bindingKey] = {
|
||||
'init': (element, valueAccessor, allBindings, viewModel, bindingContext) => {
|
||||
var didDisplayOnLastUpdate, savedNodes, contextOptions = {}, needAsyncContext;
|
||||
var savedNodes, contextOptions = {}, needAsyncContext;
|
||||
|
||||
if (isWith) {
|
||||
var as = allBindings.get('as'), noChildContext = false;
|
||||
var as = allBindings.get('as');
|
||||
contextOptions = { 'as': as, 'exportDependencies': true };
|
||||
}
|
||||
|
||||
|
|
@ -3032,8 +2988,6 @@ function makeWithIfBinding(bindingKey, isWith, isNot) {
|
|||
ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
|
||||
}
|
||||
|
||||
didDisplayOnLastUpdate = shouldDisplay;
|
||||
|
||||
}, { disposeWhenNodeIsRemoved: element });
|
||||
|
||||
return { 'controlsDescendantBindings': true };
|
||||
|
|
@ -3210,7 +3164,7 @@ ko.bindingHandlers['style'] = {
|
|||
ko.utils.objectForEach(value, (styleName, styleValue) => {
|
||||
styleValue = ko.utils.unwrapObservable(styleValue);
|
||||
|
||||
if (styleValue === null || styleValue === undefined || styleValue === false) {
|
||||
if (styleValue == null || styleValue === false) {
|
||||
// Empty string removes the value, whereas null/undefined have no effect
|
||||
styleValue = "";
|
||||
}
|
||||
|
|
@ -3235,7 +3189,7 @@ ko.bindingHandlers['submit'] = {
|
|||
'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", event => {
|
||||
element.addEventListener("submit", event => {
|
||||
var handlerReturnValue;
|
||||
var value = valueAccessor();
|
||||
try { handlerReturnValue = value.call(bindingContext['$data'], element); }
|
||||
|
|
@ -3284,26 +3238,15 @@ ko.bindingHandlers['textInput'] = {
|
|||
}
|
||||
};
|
||||
|
||||
var deferUpdateModel = event => {
|
||||
if (!timeoutHandle) {
|
||||
// The elementValueBeforeEvent variable is set *only* during the brief gap between an
|
||||
// event firing and the updateModel function running. This allows us to ignore model
|
||||
// updates that are from the previous state of the element, usually due to techniques
|
||||
// such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
|
||||
elementValueBeforeEvent = element.value;
|
||||
timeoutHandle = ko.utils.setTimeout(updateModel, 4);
|
||||
}
|
||||
};
|
||||
|
||||
var updateView = () => {
|
||||
var modelValue = ko.utils.unwrapObservable(valueAccessor());
|
||||
|
||||
if (modelValue === null || modelValue === undefined) {
|
||||
if (modelValue == null) {
|
||||
modelValue = '';
|
||||
}
|
||||
|
||||
if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {
|
||||
ko.utils.setTimeout(updateView, 4);
|
||||
setTimeout(updateView, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3316,7 +3259,7 @@ ko.bindingHandlers['textInput'] = {
|
|||
};
|
||||
|
||||
var onEvent = (event, handler) =>
|
||||
ko.utils.registerEventHandler(element, event, handler);
|
||||
element.addEventListener(event, handler);
|
||||
|
||||
onEvent('input', updateModel);
|
||||
|
||||
|
|
@ -3350,6 +3293,8 @@ ko.bindingHandlers['value'] = {
|
|||
var eventsToCatch = new Set;
|
||||
var requestedEventsToCatch = allBindings.get("valueUpdate");
|
||||
var elementValueBeforeEvent = null;
|
||||
var registerEventHandler = (event, handler) =>
|
||||
element.addEventListener(event, handler);
|
||||
|
||||
if (requestedEventsToCatch) {
|
||||
// Allow both individual event names, and arrays of event names
|
||||
|
|
@ -3373,7 +3318,7 @@ ko.bindingHandlers['value'] = {
|
|||
// 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")) {
|
||||
if ((eventName||'').startsWith("after")) {
|
||||
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
|
||||
|
|
@ -3383,11 +3328,11 @@ ko.bindingHandlers['value'] = {
|
|||
// techniques like rateLimit can trigger model changes at critical moments that will
|
||||
// override the user's inputs, causing keystrokes to be lost.
|
||||
elementValueBeforeEvent = ko.selectExtensions.readValue(element);
|
||||
ko.utils.setTimeout(valueUpdateHandler, 0);
|
||||
setTimeout(valueUpdateHandler, 0);
|
||||
};
|
||||
eventName = eventName.substring("after".length);
|
||||
eventName = eventName.substring(5);
|
||||
}
|
||||
ko.utils.registerEventHandler(element, eventName, handler);
|
||||
registerEventHandler(eventName, handler);
|
||||
});
|
||||
|
||||
var updateFromModel;
|
||||
|
|
@ -3396,7 +3341,7 @@ ko.bindingHandlers['value'] = {
|
|||
// For file input elements, can only write the empty string
|
||||
updateFromModel = () => {
|
||||
var newValue = ko.utils.unwrapObservable(valueAccessor());
|
||||
if (newValue === null || newValue === undefined || newValue === "") {
|
||||
if (newValue == null || newValue === "") {
|
||||
element.value = "";
|
||||
} else {
|
||||
ko.dependencyDetection.ignore(valueUpdateHandler); // reset the model to match the element
|
||||
|
|
@ -3408,7 +3353,7 @@ ko.bindingHandlers['value'] = {
|
|||
var elementValue = ko.selectExtensions.readValue(element);
|
||||
|
||||
if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {
|
||||
ko.utils.setTimeout(updateFromModel, 0);
|
||||
setTimeout(updateFromModel, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3434,7 +3379,7 @@ ko.bindingHandlers['value'] = {
|
|||
var updateFromModelComputed;
|
||||
ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, () => {
|
||||
if (!updateFromModelComputed) {
|
||||
ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
|
||||
registerEventHandler("change", valueUpdateHandler);
|
||||
updateFromModelComputed = ko.computed(updateFromModel, { disposeWhenNodeIsRemoved: element });
|
||||
} else if (allBindings.get('valueAllowUnset')) {
|
||||
updateFromModel();
|
||||
|
|
@ -3443,7 +3388,7 @@ ko.bindingHandlers['value'] = {
|
|||
}
|
||||
}, null, { 'notifyImmediately': true });
|
||||
} else {
|
||||
ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
|
||||
registerEventHandler("change", valueUpdateHandler);
|
||||
ko.computed(updateFromModel, { disposeWhenNodeIsRemoved: element });
|
||||
}
|
||||
},
|
||||
|
|
@ -3587,8 +3532,7 @@ makeEventHandlerShortcut('click');
|
|||
: null;
|
||||
}
|
||||
|
||||
function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
|
||||
options = options || {};
|
||||
function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext) {
|
||||
var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
|
||||
var templateDocument = (firstTargetNode || template || {}).ownerDocument;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue