snappymail/dev/External/ko.js
S-A-L13 876ed221c3
Update Fork (#2)
* Cleanup OpenPgpImportPopupView code

* update polish translation

* small fix

* Added Import S/MIME certificate popup
And much better handling of the sign and encrypt options

* bugfix: store in Passphrases

* Resolve #1448

* pre-verify S/MIME opaque signed messages so we have a body to view

* Fix timestampToString() for future dates

* Move php8.php to /app/libraries/polyfill/

* Improved Settings handling to prevent bugs in outer code

* Changed AbstractProvider::IsActive() to be abstract

* Example for #1449

* bugfix: previous IsActive() commit

* OpenSSL required due to S/MIME

* Use get_debug_type() instead of gettype()

* update polish translation

* Make all Enumerations classes abstract

* Added search functionality in Admin -> Config
And removed the unused ['capa']['quota']

* Cleanup Quota handling

* OPEN_PGP should be OPENPGP as it is one word

* Improve Capa handling

* Resolve #1451

* Bugfix TypeError: b64Encode(...).match(...) is null

* Small StorageType change

* Bugfix: mailvelope editor failed

* Bugfix: undefined getMailvelopePrivateKeyFor()

* Bugfix: MIME parser RegExp didn't escape `boundary` which caused issues

* Return detailed info on PgpImportKey

* Show GnuPG verify error

* Sort PGP keys by email and id

* Sort S/MIME certificates on emailAddress else validTo

* S/MIME import from signature use `BEGIN PKCS7`

* Optionally use existing private key to generate S/MIME certificate

* Chaned some error_log() to MailSo Logger()

* Force reload of S/MIME certificates list on import

* Make better use of SnappyMail\SensitiveString

* Fix view PGP key button

* Mask all POST data that has a key which contains `pass`

* v2.35.1

* Resolve #1455

* Improved GnuPG error handling

* Update pt/pt-PT translation

* update Polish translation

* Drop support for gnupg pecl extension as it fails with "no passphrase" issues

* Resolve #1456

* Resolve #1458

* v2.35.2

* fix changelog

* Resolve #1461

* Update pt/pt-PT translation

* compact-composer plugin v1.0.0

* Resolve #1462

* Fix decrypt error message

* `new Error()` to `Error()`

* Resolve #1463

* Show url for #1466

* Simplify SignMe/Remember me code

* Simplify language Notifications

* Bugfix: SetPassword expects \SnappyMail\SensitiveString

* https://github.com/the-djmaze/snappymail/issues/1450#issuecomment-1972147950

* improve: fire the 'squire2-toolbar' event after more props are added

* improve: add dark theme support and use 'button' element as menu trigger for consistent styling

* fix: use compact template in non-destructive way (do not replace the PopupsCompose template if a different wysiwyg is used)

* Update admin.json

* Update user.json

* CSS rainloopErrorTip location

* Improved error handling on PGP and S/MIME decrypt

* KnockoutJS remove unused `beforeRemove`

* KnockoutJS drop unused `as`

* KnockoutJS simplify renderMode because only 1 option is used

* KnoutJS cleanup templating.js a bit

* KnockoutJS drop unused `bindingRewriteValidators`

* KnockoutJS drop the twoWayBindings code

* KnockoutJS simplify virtualElements binding check

* KnockoutJS simplify applyBindingsToNodeInternal

* KnockoutJS use Array.isArray

* KnockoutJS drop alias `textinput` for `textInput`

* KnockoutJS scramble `createChildContext`

* KnockoutJS scramble `controlsDescendantBindings`

* KnockoutJS scramble `exportDependencies`

* KnockoutJS drop unused `throttleEvaluation`

* KnockoutJS drop unused `valueAllowUnset`

* KnockoutJS drop unused `templateNodes`

* KnockoutJS drop unused `optionsCaption`

* KnockoutJS drop unused `dontLimitMoves`

* KnockoutJS drop unused `uniqueName`

* KnockoutJS drop IE leftovers

* KnockoutJS drop unused `preprocess`

* KnockoutJS drop unused "disposeWhenNodeIsRemoved" and "disposeWhen"

* KnockoutJS don't scramble exportDependencies. controlsDescendantBindings, createChildContext

* KnockoutJS drop unused `$parentContext` and `$parents`

* KnockoutJS drop unused `$rawData`

* Knockoutjs built latest

* KnockoutJS drop unused template options `nodes`, `if`, `ifnot`

* KnockoutJS use more Array.isArray

* KnockoutJS cleanup code a bit

* KnockoutJS primitiveTypes can just be checked with Object()

* KnockoutJS rebuilt

* Verify S/MIME signed automatically and log Exception

* Automatically verify PGP and S/MIME signed messages

* `new Error` to `Error`

* By default throw AccountNotAllowed as confused in #1478

* GPG use pinentries for decrypt, sign and export

* Better GPG error handling

* GPG show error on view/export

* OpenPGP fix handling of importing keys

* Make "verify signatures automatically" optional, as it requires more IMAP fetching

* S/MIME don't post identity key and certificate, just fetch from server

* Show error to old browsers, instead of crashing

* Automatically verify S/MIME decrypted signed message

---------

Co-authored-by: the-djmaze <>
Co-authored-by: tinola <tinola@poczta.onet.pl>
Co-authored-by: Maarten <3752035+the-djmaze@users.noreply.github.com>
Co-authored-by: lmperfis <joint.striker@gmail.com>
Co-authored-by: Sergey Mosin <sergey@srgdev.com>
Co-authored-by: hguilbert <51283484+hguilbert@users.noreply.github.com>
2024-03-04 17:00:27 +01:00

180 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ko from 'ko';
import { i18nToNodes } from 'Common/Translator';
import { doc, createElement } from 'Common/Globals';
import { SaveSettingStatus } from 'Common/Enums';
import { isFunction, forEachObjectEntry } from 'Common/Utils';
export const
errorTip = (element, value) => value
? setTimeout(() => element.setAttribute('data-rainloopErrorTip', value), 100)
: element.removeAttribute('data-rainloopErrorTip'),
/**
* 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}),
addObservablesTo = (target, observables) =>
forEachObjectEntry(observables, (key, value) =>
target[key] || (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)),
dispose = disposable => isFunction(disposable?.dispose) && disposable.dispose(),
onEvent = (element, event, fn) => {
element.addEventListener(event, fn);
ko.utils.domNodeDisposal.addDisposeCallback(element, () => element.removeEventListener(event, fn));
},
onKey = (key, element, fValueAccessor, fAllBindings, model) => {
let fn = event => {
if (key == event.key) {
// stopEvent(event);
// element.dispatchEvent(new Event('change'));
fValueAccessor().call(model);
}
};
onEvent(element, 'keydown', fn);
},
// With this we don't need delegateRunOnDestroy
koArrayWithDestroy = data => {
data = ko.observableArray(data);
data.subscribe(changes =>
changes.forEach(item =>
'deleted' === item.status && null == item.moved && item.value.onDestroy?.()
)
, data, 'arrayChange');
return data;
};
Object.assign(ko.bindingHandlers, {
tooltipErrorTip: {
init: (element, fValueAccessor) => {
doc.addEventListener('click', () => {
let value = fValueAccessor();
ko.isObservable(value) && !ko.isComputed(value) && value('');
errorTip(element);
});
},
update: (element, fValueAccessor) => {
let value = ko.unwrap(fValueAccessor());
errorTip(element, isFunction(value) ? value() : value);
}
},
onEnter: {
init: (element, fValueAccessor, fAllBindings, model) =>
onKey('Enter', element, fValueAccessor, fAllBindings, model)
},
onEsc: {
init: (element, fValueAccessor, fAllBindings, model) =>
onKey('Escape', element, fValueAccessor, fAllBindings, model)
},
onSpace: {
init: (element, fValueAccessor, fAllBindings, model) =>
onKey(' ', element, fValueAccessor, fAllBindings, model)
},
toggle: {
init: (element, fValueAccessor) => {
let observable = fValueAccessor(),
fn = () => observable(!observable());
onEvent(element, 'click', fn);
onEvent(element, 'keydown', event => ' ' == event.key && fn());
}
},
i18nUpdate: {
update: (element, fValueAccessor) => {
ko.unwrap(fValueAccessor());
i18nToNodes(element);
}
},
command: {
init: (element, fValueAccessor, fAllBindings, viewModel, bindingContext) => {
const command = fValueAccessor();
if (!command || !command.canExecute) {
throw Error('Value should be a command');
}
ko.bindingHandlers['FORM'==element.nodeName ? 'submit' : 'click'].init(
element,
fValueAccessor,
fAllBindings,
viewModel,
bindingContext
);
},
update: (element, fValueAccessor) => {
let disabled = !fValueAccessor().canExecute();
element.classList.toggle('disabled', disabled);
if (element.matches('INPUT,TEXTAREA,BUTTON')) {
element.disabled = disabled;
}
}
},
saveTrigger: {
init: (element) => {
let icon = element;
if (element.matches('input,select,textarea')) {
element.classList.add('settings-save-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 === SaveSettingStatus.Saving);
cl.toggle('success', value === SaveSettingStatus.Success);
cl.toggle('error', value === SaveSettingStatus.Failed);
}
cl = element.classList;
cl.toggle('success', value === SaveSettingStatus.Success);
cl.toggle('error', value === SaveSettingStatus.Failed);
}
}
});
// extenders
ko.extenders.toggleSubscribeProperty = (target, options) => {
const prop = options[1];
if (prop) {
target.subscribe(
prev => prev?.[prop]?.(false),
options[0],
'beforeChange'
);
target.subscribe(next => next?.[prop]?.(true), options[0]);
}
return target;
};
ko.extenders.falseTimeout = (target, option) => {
target.subscribe((() => target(false)).debounce(parseInt(option, 10) || 0));
return target;
};
// functions
ko.observable.fn.askDeleteHelper = function() {
return this.extend({ falseTimeout: 3000, toggleSubscribeProperty: [this, 'askDelete'] });
};