snappymail/dev/External/ko.js

200 lines
5.5 KiB
JavaScript
Raw Normal View History

2021-03-25 17:08:29 +08:00
import { i18nToNodes } from 'Common/Translator';
import { doc, createElement } from 'Common/Globals';
2021-02-11 17:08:27 +08:00
import { SaveSettingsStep } from 'Common/Enums';
2022-02-17 16:36:29 +08:00
import { arrayLength, isFunction, forEachObjectEntry } from 'Common/Utils';
2022-02-17 06:12:12 +08:00
export const
/**
* The value of the pureComputed observable shouldnt vary based on the
* number of evaluations or other hidden information. Its value should be
* based solely on the values of other observables in the application
*/
koComputable = fn => ko.computed(fn, {'pure':true}),
2022-02-17 16:36:29 +08:00
addObservablesTo = (target, observables) =>
forEachObjectEntry(observables, (key, value) =>
target[key] = /*isArray(value) ? ko.observableArray(value) :*/ ko.observable(value) ),
addComputablesTo = (target, computables) =>
forEachObjectEntry(computables, (key, fn) => target[key] = koComputable(fn)),
addSubscribablesTo = (target, subscribables) =>
forEachObjectEntry(subscribables, (key, fn) => target[key].subscribe(fn)),
2022-02-17 06:12:12 +08:00
dispose = disposable => disposable && isFunction(disposable.dispose) && disposable.dispose();
2016-06-30 08:02:45 +08:00
ko.bindingHandlers.tooltipErrorTip = {
init: (element, fValueAccessor) => {
doc.addEventListener('click', () => {
let value = fValueAccessor();
ko.isObservable(value) && !ko.isComputed(value) && value('');
element.removeAttribute('data-rainloopErrorTip');
});
2016-06-30 08:02:45 +08:00
},
update: (element, fValueAccessor) => {
let value = ko.unwrap(fValueAccessor());
value = isFunction(value) ? value() : value;
2020-08-13 19:16:08 +08:00
if (value) {
2020-08-14 04:58:41 +08:00
setTimeout(() => element.setAttribute('data-rainloopErrorTip', value), 100);
2020-08-13 19:16:08 +08:00
} else {
element.removeAttribute('data-rainloopErrorTip');
2014-08-20 23:03:12 +08:00
}
2016-06-30 08:02:45 +08:00
}
};
2014-08-20 23:03:12 +08:00
2016-06-30 08:02:45 +08:00
ko.bindingHandlers.onEnter = {
init: (element, fValueAccessor, fAllBindings, viewModel) => {
let fn = event => {
if ('Enter' == event.key) {
element.dispatchEvent(new Event('change'));
fValueAccessor().call(viewModel);
2014-08-20 23:03:12 +08:00
}
};
element.addEventListener('keydown', fn);
ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeEventListener('keydown', fn));
2016-06-30 08:02:45 +08:00
}
};
2014-08-20 23:03:12 +08:00
2016-06-30 08:02:45 +08:00
ko.bindingHandlers.onSpace = {
init: (element, fValueAccessor, fAllBindings, viewModel) => {
let fn = event => {
if (' ' == event.key) {
fValueAccessor().call(viewModel, event);
2016-06-30 08:02:45 +08:00
}
};
element.addEventListener('keyup', fn);
ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeEventListener('keyup', fn));
2016-06-30 08:02:45 +08:00
}
};
2015-02-15 08:30:21 +08:00
2016-06-30 08:02:45 +08:00
ko.bindingHandlers.i18nInit = {
init: element => i18nToNodes(element)
2016-06-30 08:02:45 +08:00
};
ko.bindingHandlers.i18nUpdate = {
update: (element, fValueAccessor) => {
2016-06-30 08:02:45 +08:00
ko.unwrap(fValueAccessor());
i18nToNodes(element);
2016-06-30 08:02:45 +08:00
}
};
ko.bindingHandlers.title = {
2020-08-27 21:45:47 +08:00
update: (element, fValueAccessor) => element.title = ko.unwrap(fValueAccessor())
2016-06-30 08:02:45 +08:00
};
ko.bindingHandlers.command = {
init: (element, fValueAccessor, fAllBindings, viewModel, bindingContext) => {
const command = fValueAccessor();
if (!command || !command.enabled || !command.canExecute) {
throw new Error('Value should be a command');
}
2020-09-15 15:29:25 +08:00
ko.bindingHandlers['FORM'==element.nodeName ? 'submit' : 'click'].init(
2019-07-05 03:19:24 +08:00
element,
fValueAccessor,
fAllBindings,
2019-07-05 03:19:24 +08:00
viewModel,
bindingContext
);
2016-06-30 08:02:45 +08:00
},
update: (element, fValueAccessor) => {
const cl = element.classList,
command = fValueAccessor();
2014-08-20 23:03:12 +08:00
2021-02-09 00:37:21 +08:00
let disabled = !command.enabled();
2014-08-20 23:03:12 +08:00
2021-02-09 00:37:21 +08:00
disabled = disabled || !command.canExecute();
cl.toggle('disabled', disabled);
2014-08-20 23:03:12 +08:00
2021-01-25 05:58:06 +08:00
if (element.matches('INPUT,TEXTAREA,BUTTON')) {
2021-02-09 00:37:21 +08:00
element.disabled = disabled;
2014-08-20 23:03:12 +08:00
}
2016-06-30 08:02:45 +08:00
}
};
2014-08-20 23:03:12 +08:00
2021-02-11 17:08:27 +08:00
ko.bindingHandlers.saveTrigger = {
init: (element) => {
let icon = element;
if (element.matches('input,select,textarea')) {
element.classList.add('settings-saved-trigger-input');
element.after(element.saveTriggerIcon = icon = createElement('span'));
}
icon.classList.add('settings-save-trigger');
},
update: (element, fValueAccessor) => {
const value = parseInt(ko.unwrap(fValueAccessor()),10);
let cl = (element.saveTriggerIcon || element).classList;
if (element.saveTriggerIcon) {
cl.toggle('saving', value === SaveSettingsStep.Animate);
cl.toggle('success', value === SaveSettingsStep.TrueResult);
cl.toggle('error', value === SaveSettingsStep.FalseResult);
}
cl = element.classList;
cl.toggle('success', value === SaveSettingsStep.TrueResult);
cl.toggle('error', value === SaveSettingsStep.FalseResult);
}
};
2016-06-30 08:02:45 +08:00
// extenders
2014-10-04 19:58:01 +08:00
ko.extenders.limitedList = (target, limitedList) => {
const result = ko
2019-07-05 03:19:24 +08:00
.computed({
read: target,
write: (newValue) => {
const currentValue = ko.unwrap(target),
list = ko.unwrap(limitedList);
2021-07-22 03:34:17 +08:00
if (arrayLength(list)) {
if (list.includes(newValue)) {
2019-07-05 03:19:24 +08:00
target(newValue);
} else if (list.includes(currentValue, list)) {
2019-07-05 03:19:24 +08:00
target(currentValue + ' ');
target(currentValue);
} else {
target(list[0] + ' ');
target(list[0]);
}
} else {
target('');
}
}
2019-07-05 03:19:24 +08:00
})
.extend({ notify: 'always' });
result(target());
2019-07-05 03:19:24 +08:00
if (!result.valueHasMutated) {
result.valueHasMutated = () => target.valueHasMutated();
2016-06-30 08:02:45 +08:00
}
2014-08-20 23:03:12 +08:00
return result;
2016-06-30 08:02:45 +08:00
};
2014-08-20 23:03:12 +08:00
ko.extenders.toggleSubscribeProperty = (target, options) => {
const prop = options[1];
2019-07-05 03:19:24 +08:00
if (prop) {
target.subscribe(
prev => prev && prev[prop] && prev[prop](false),
2019-07-05 03:19:24 +08:00
options[0],
'beforeChange'
);
2014-08-20 23:03:12 +08:00
target.subscribe(next => next && next[prop] && next[prop](true), options[0]);
2016-06-30 08:02:45 +08:00
}
2014-08-20 23:03:12 +08:00
return target;
2016-06-30 08:02:45 +08:00
};
ko.extenders.falseTimeout = (target, option) => {
2021-02-11 17:08:27 +08:00
target.subscribe((() => target(false)).debounce(parseInt(option, 10) || 0));
return target;
2016-06-30 08:02:45 +08:00
};
// functions
2014-10-04 19:58:01 +08:00
ko.observable.fn.askDeleteHelper = function() {
return this.extend({ falseTimeout: 3000, toggleSubscribeProperty: [this, 'askDelete'] });
2016-06-30 08:02:45 +08:00
};