mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-06-28 14:55:48 +00:00
Revamp array filtering
Replaced fakeMd5 with new Jua.randomId Cleanup more code
This commit is contained in:
18
README.md
18
README.md
@@ -106,22 +106,22 @@ RainLoop 1.14 vs SnappyMail
|
||||
|
||||
|js/* |RainLoop |Snappy |
|
||||
|----------- |--------: |--------: |
|
||||
|admin.js |2.130.942 | 798.252 |
|
||||
|app.js |4.184.455 |2.450.286 |
|
||||
|admin.js |2.130.942 | 776.615 |
|
||||
|app.js |4.184.455 |2.430.639 |
|
||||
|boot.js | 671.522 | 5.285 |
|
||||
|libs.js | 647.614 | 254.910 |
|
||||
|libs.js | 647.614 | 255.041 |
|
||||
|polyfills.js | 325.834 | 0 |
|
||||
|TOTAL |7.960.367 |3.508.733 |
|
||||
|TOTAL |7.960.367 |3.467.580 |
|
||||
|
||||
|js/min/* |RainLoop |Snappy |Rain gzip |gzip |brotli |
|
||||
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
||||
|admin.min.js | 252.147 | 106.871 | 73.657 | 28.834 | 25.063 |
|
||||
|app.min.js | 511.202 | 332.055 |140.462 | 85.613 | 69.336 |
|
||||
|admin.min.js | 252.147 | 104.046 | 73.657 | 27.739 | 24.058 |
|
||||
|app.min.js | 511.202 | 329.887 |140.462 | 84.844 | 68.843 |
|
||||
|boot.min.js | 66.007 | 2.935 | 22.567 | 1.510 | 1.285 |
|
||||
|libs.min.js | 572.545 | 149.580 |176.720 | 52.695 | 46.823 |
|
||||
|libs.min.js | 572.545 | 149.712 |176.720 | 52.703 | 46.853 |
|
||||
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 |
|
||||
|TOTAL |1.434.353 | 591.441 |424.718 |168.652 |142.507 |
|
||||
|TOTAL (no admin) |1.182.206 | 484.570 |351.061 |139.818 |117.444 |
|
||||
|TOTAL |1.434.353 | 586.580 |424.718 |166.796 |141.039 |
|
||||
|TOTAL (no admin) |1.182.206 | 482.534 |351.061 |139.057 |116.981 |
|
||||
|
||||
For a user its around 60% smaller and faster than traditional RainLoop.
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class AdminApp extends AbstractApp {
|
||||
return 'core' === item.type && !item.canBeInstalled ? null : item;
|
||||
}
|
||||
return null;
|
||||
}).filter(value => !!value);
|
||||
}).filter(v => v);
|
||||
}
|
||||
|
||||
PackageStore.packages(list);
|
||||
|
||||
@@ -255,8 +255,7 @@ class AppUser extends AbstractApp {
|
||||
};
|
||||
}
|
||||
|
||||
this.moveCache[hash].Uid = this.moveCache[hash].Uid.concat(uidsForMove)
|
||||
.filter((value, index, self) => self.indexOf(value) == index);
|
||||
this.moveCache[hash].Uid = this.moveCache[hash].Uid.concat(uidsForMove).unique();
|
||||
this.messagesMoveTrigger();
|
||||
}
|
||||
|
||||
@@ -457,7 +456,7 @@ class AppUser extends AbstractApp {
|
||||
.toLowerCase(),
|
||||
oItem.getKeyIds()
|
||||
.map(item => (item && item.toHex ? item.toHex() : null))
|
||||
.filter((value, index, self) => !!value && self.indexOf(value) == index),
|
||||
.validUnique(),
|
||||
aUsers,
|
||||
aEmails,
|
||||
oItem.isPrivate(),
|
||||
@@ -537,7 +536,7 @@ class AppUser extends AbstractApp {
|
||||
data.Result.Templates.map(templateData => {
|
||||
const template = new TemplateModel();
|
||||
return template.parse(templateData) ? template : null;
|
||||
}).filter(value => !!value)
|
||||
}).filter(v => v)
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -718,7 +717,7 @@ class AppUser extends AbstractApp {
|
||||
}
|
||||
|
||||
rootUids = messages.map(oMessage => oMessage && oMessage.uid ? oMessage.uid : null)
|
||||
.filter((value, index, self) => !!value && self.indexOf(value) == index);
|
||||
.validUnique();
|
||||
|
||||
if (sFolderFullNameRaw && rootUids.length) {
|
||||
switch (iSetAction) {
|
||||
@@ -779,7 +778,7 @@ class AppUser extends AbstractApp {
|
||||
Remote.suggestions((result, data) => {
|
||||
if (StorageResultType.Success === result && data && Array.isArray(data.Result)) {
|
||||
autocompleteCallback(
|
||||
data.Result.map(item => (item && item[0] ? new EmailModel(item[0], item[1]) : null)).filter(value => !!value)
|
||||
data.Result.map(item => (item && item[0] ? new EmailModel(item[0], item[1]) : null)).filter(v => v)
|
||||
);
|
||||
} else if (StorageResultType.Abort !== result) {
|
||||
autocompleteCallback([]);
|
||||
|
||||
@@ -301,7 +301,7 @@ export const File = {
|
||||
if (Array.isNotEmpty(data)) {
|
||||
let icons = data
|
||||
.map(item => item ? File.getIconClass(File.getExtension(item[0]), item[1])[0] : '')
|
||||
.filter((value, index, self) => value && self.indexOf(value) == index);
|
||||
.validUnique();
|
||||
|
||||
return (icons && 1 === icons.length && 'icon-file' !== icons[0])
|
||||
? icons[0]
|
||||
|
||||
@@ -40,38 +40,6 @@ export function pString(value) {
|
||||
return null != value ? '' + value : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} queryString
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function simpleQueryParser(queryString) {
|
||||
const queries = queryString.split('&'),
|
||||
params = {};
|
||||
|
||||
queries.forEach(temp => {
|
||||
temp = temp.split('=');
|
||||
params[decodeURIComponent(temp[0])] = decodeURIComponent(temp[1]);
|
||||
});
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number=} len = 32
|
||||
* @returns {string}
|
||||
*/
|
||||
export function fakeMd5(len = 32) {
|
||||
const line = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
len = pInt(len);
|
||||
|
||||
let result = '';
|
||||
while (len--)
|
||||
result += line.substr(Math.round(Math.random() * 36), 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
@@ -85,7 +53,7 @@ export function encodeHtml(text) {
|
||||
* @param {number=} len = 100
|
||||
* @returns {string}
|
||||
*/
|
||||
export function splitPlainText(text, len = 100) {
|
||||
function splitPlainText(text, len = 100) {
|
||||
let prefix = '',
|
||||
subText = '',
|
||||
result = text,
|
||||
@@ -139,72 +107,6 @@ export function inFocus() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean} force
|
||||
* @returns {void}
|
||||
*/
|
||||
export function removeInFocus(force) {
|
||||
if (doc.activeElement && doc.activeElement.blur) {
|
||||
try {
|
||||
if (force || doc.activeElement.matches('input,textarea')) {
|
||||
doc.activeElement.blur();
|
||||
}
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
export function removeSelection() {
|
||||
try {
|
||||
getSelection().removeAllRanges();
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} prefix
|
||||
* @param {string} subject
|
||||
* @returns {string}
|
||||
*/
|
||||
export function replySubjectAdd(prefix, subject) {
|
||||
prefix = prefix.toUpperCase().trim();
|
||||
subject = subject.replace(/[\s]+/g, ' ').trim();
|
||||
|
||||
let drop = false,
|
||||
re = 'RE' === prefix,
|
||||
fwd = 'FWD' === prefix;
|
||||
|
||||
const parts = [],
|
||||
prefixIsRe = !fwd;
|
||||
|
||||
if (subject) {
|
||||
subject.split(':').forEach(part => {
|
||||
const trimmedPart = part.trim();
|
||||
if (!drop && (/^(RE|FWD)$/i.test(trimmedPart) || /^(RE|FWD)[[(][\d]+[\])]$/i.test(trimmedPart))) {
|
||||
if (!re) {
|
||||
re = !!/^RE/i.test(trimmedPart);
|
||||
}
|
||||
|
||||
if (!fwd) {
|
||||
fwd = !!/^FWD/i.test(trimmedPart);
|
||||
}
|
||||
} else {
|
||||
parts.push(part);
|
||||
drop = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (prefixIsRe) {
|
||||
re = false;
|
||||
} else {
|
||||
fwd = false;
|
||||
}
|
||||
|
||||
return ((prefixIsRe ? 'Re: ' : 'Fwd: ') + (re ? 'Re: ' : '') + (fwd ? 'Fwd: ' : '') + parts.join(':').trim()).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(number|string)} sizeInBytes
|
||||
* @returns {string}
|
||||
@@ -256,88 +158,18 @@ export function defautOptionsAfterRender(domItem, item) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} fCallback
|
||||
* @param {?} koTrigger
|
||||
* @param {?} context = null
|
||||
* @param {number=} timer = 1000
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function settingsSaveHelperFunction(fCallback, koTrigger, context = null, timer = 1000) {
|
||||
timer = pInt(timer);
|
||||
return (type, data, cached, requestAction, requestParameters) => {
|
||||
koTrigger.call(context, data && data.Result ? SaveSettingsStep.TrueResult : SaveSettingsStep.FalseResult);
|
||||
if (fCallback) {
|
||||
fCallback.call(context, type, data, cached, requestAction, requestParameters);
|
||||
}
|
||||
setTimeout(() => {
|
||||
koTrigger.call(context, SaveSettingsStep.Idle);
|
||||
}, timer);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} koTrigger
|
||||
* @param {mixed} context
|
||||
* @returns {mixed}
|
||||
*/
|
||||
export function settingsSaveHelperSimpleFunction(koTrigger, context) {
|
||||
return settingsSaveHelperFunction(null, koTrigger, context, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} remote
|
||||
* @param {string} settingName
|
||||
* @param {string} type
|
||||
* @param {function} fTriggerFunction
|
||||
* @returns {function}
|
||||
*/
|
||||
export function settingsSaveHelperSubscribeFunction(remote, settingName, type, fTriggerFunction) {
|
||||
return (value) => {
|
||||
if (remote) {
|
||||
switch (type) {
|
||||
case 'bool':
|
||||
case 'boolean':
|
||||
value = value ? '1' : '0';
|
||||
break;
|
||||
case 'int':
|
||||
case 'integer':
|
||||
case 'number':
|
||||
value = pInt(value);
|
||||
break;
|
||||
case 'trim':
|
||||
value = value.trim();
|
||||
break;
|
||||
default:
|
||||
value = pString(value);
|
||||
break;
|
||||
}
|
||||
|
||||
const data = {};
|
||||
data[settingName] = value;
|
||||
|
||||
if (remote.saveAdminConfig) {
|
||||
remote.saveAdminConfig(fTriggerFunction || null, data);
|
||||
} else if (remote.saveSettings) {
|
||||
remote.saveSettings(fTriggerFunction || null, data);
|
||||
}
|
||||
}
|
||||
return (type, data) => {
|
||||
koTrigger.call(context, data && data.Result ? SaveSettingsStep.TrueResult : SaveSettingsStep.FalseResult);
|
||||
setTimeout(() => koTrigger.call(context, SaveSettingsStep.Idle), 1000);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
* @returns {string}
|
||||
*/
|
||||
/*eslint-disable max-len*/
|
||||
const url = /(^|[\s\n]|\/?>)(https:\/\/[-A-Z0-9+\u0026\u2019#/%?=()~_|!:,.;]*[-A-Z0-9+\u0026#/%=~()_|])/gi,
|
||||
email = /(^|[\s\n]|\/?>)((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x21\x23-\x5b\x5d-\x7f]|\\[\x21\x23-\x5b\x5d-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x21-\x5a\x53-\x7f]|\\[\x21\x23-\x5b\x5d-\x7f])+)\]))/gi;
|
||||
export function findEmailAndLinks(html) {
|
||||
return html
|
||||
.replace(url, '$1<a href="$2" target="_blank">$2</a>')
|
||||
.replace(email, '$1<a href="mailto:$2">$2</a>');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
* @returns {string}
|
||||
@@ -644,18 +476,6 @@ export function folderListOptionsBuilder(
|
||||
return aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} element
|
||||
* @returns {void}
|
||||
*/
|
||||
export function selectElement(element) {
|
||||
let sel = getSelection(),
|
||||
range = doc.createRange();
|
||||
sel.removeAllRanges();
|
||||
range.selectNodeContents(element);
|
||||
sel.addRange(range);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object|Array} objectOrObjects
|
||||
* @returns {void}
|
||||
@@ -827,38 +647,6 @@ export function isTransparent(color) {
|
||||
return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {number} value
|
||||
* @param {Function} fCallback
|
||||
*/
|
||||
export function resizeAndCrop(url, value, fCallback) {
|
||||
const img = new Image();
|
||||
img.onload = function() {
|
||||
let diff = [0, 0];
|
||||
|
||||
const canvas = doc.createElement('canvas'),
|
||||
ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = value;
|
||||
canvas.height = value;
|
||||
|
||||
if (this.width > this.height) {
|
||||
diff = [this.width - this.height, 0];
|
||||
} else {
|
||||
diff = [0, this.height - this.width];
|
||||
}
|
||||
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, value, value);
|
||||
ctx.drawImage(this, diff[0] / 2, diff[1] / 2, this.width - diff[0], this.height - diff[1], 0, 0, value, value);
|
||||
|
||||
fCallback(canvas.toDataURL('image/jpeg'));
|
||||
};
|
||||
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} mailToUrl
|
||||
* @param {Function} PopupComposeViewModel
|
||||
@@ -888,7 +676,10 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) {
|
||||
query = mailToUrl.replace(/^[^?]*\?/, ''),
|
||||
EmailModel = require('Model/Email').default;
|
||||
|
||||
params = simpleQueryParser(query);
|
||||
query.split('&').forEach(temp => {
|
||||
temp = temp.split('=');
|
||||
params[decodeURIComponent(temp[0])] = decodeURIComponent(temp[1]);
|
||||
});
|
||||
|
||||
if (undefined !== params.to) {
|
||||
to = EmailModel.parseEmailLine(decodeURIComponent(email + ',' + params.to));
|
||||
|
||||
@@ -440,7 +440,7 @@ class EmailModel {
|
||||
if (parsedResult.length) {
|
||||
return parsedResult.map(item =>
|
||||
item.address ? new EmailModel(item.address.replace(/^[<]+(.*)[>]+$/g, '$1'), item.name || '') : null
|
||||
).filter(value => !!value);
|
||||
).filter(v => v);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ko from 'ko';
|
||||
|
||||
import { FilterRulesType, FiltersAction } from 'Common/Enums';
|
||||
import { pString, fakeMd5, delegateRunOnDestroy } from 'Common/Utils';
|
||||
import { pString, delegateRunOnDestroy } from 'Common/Utils';
|
||||
import { i18n } from 'Common/Translator';
|
||||
import { getFolderFromCacheList } from 'Common/Cache';
|
||||
|
||||
@@ -133,7 +133,7 @@ class FilterModel extends AbstractModel {
|
||||
}
|
||||
|
||||
generateID() {
|
||||
this.id = fakeMd5();
|
||||
this.id = Jua.randomId();
|
||||
}
|
||||
|
||||
verify() {
|
||||
@@ -230,7 +230,7 @@ class FilterModel extends AbstractModel {
|
||||
json.Conditions.map(aData => {
|
||||
const filterCondition = new FilterConditionModel();
|
||||
return filterCondition && filterCondition.parse(aData) ? filterCondition : null;
|
||||
}).filter(value => !!value)
|
||||
}).filter(v => v)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ class MessageModel extends AbstractModel {
|
||||
getEmails(properties) {
|
||||
return properties.reduce((carry, property) => carry.concat(this[property]), []).map(
|
||||
oItem => oItem ? oItem.email : ''
|
||||
).filter((value, index, self) => !!value && self.indexOf(value) == index);
|
||||
).validUnique();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ class FiltersUserSettings {
|
||||
data.Result.Filters.map(aItem => {
|
||||
const filter = new FilterModel();
|
||||
return filter && filter.parse(aItem) ? filter : null;
|
||||
}).filter(value => !!value)
|
||||
}).filter(v => v)
|
||||
);
|
||||
|
||||
this.modules(data.Result.Modules ? data.Result.Modules : {});
|
||||
|
||||
@@ -10,7 +10,7 @@ class AccountUserStore {
|
||||
this.accounts = ko.observableArray([]);
|
||||
this.accounts.loading = ko.observable(false).extend({ throttle: 100 });
|
||||
|
||||
this.getEmailAddresses = () => this.accounts().map(item => item ? item.email : null).filter(value => !!value);
|
||||
this.getEmailAddresses = () => this.accounts().map(item => item ? item.email : null).filter(v => v);
|
||||
|
||||
this.accountsUnreadCount = ko.computed(() => 0);
|
||||
// this.accountsUnreadCount = ko.computed(() => {
|
||||
|
||||
@@ -85,7 +85,7 @@ class FolderUserStore {
|
||||
});
|
||||
|
||||
this.folderListSystem = ko.computed(() =>
|
||||
this.folderListSystemNames().map(name => getFolderFromCacheList(name)).filter(value => !!value)
|
||||
this.folderListSystemNames().map(name => getFolderFromCacheList(name)).filter(v => v)
|
||||
);
|
||||
|
||||
this.folderMenuForMove = ko.computed(() =>
|
||||
|
||||
@@ -5,8 +5,7 @@ import { Layout, Focused, MessageSetAction, StorageResultType, Notification } fr
|
||||
import {
|
||||
pInt,
|
||||
pString,
|
||||
plainToHtml,
|
||||
findEmailAndLinks
|
||||
plainToHtml
|
||||
} from 'Common/Utils';
|
||||
|
||||
import {
|
||||
@@ -44,6 +43,14 @@ const
|
||||
const result = hcont.clientHeight;
|
||||
hcont.innerHTML = '';
|
||||
return result;
|
||||
},
|
||||
/*eslint-disable max-len*/
|
||||
url = /(^|[\s\n]|\/?>)(https:\/\/[-A-Z0-9+\u0026\u2019#/%?=()~_|!:,.;]*[-A-Z0-9+\u0026#/%=~()_|])/gi,
|
||||
email = /(^|[\s\n]|\/?>)((?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x21\x23-\x5b\x5d-\x7f]|\\[\x21\x23-\x5b\x5d-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x21-\x5a\x53-\x7f]|\\[\x21\x23-\x5b\x5d-\x7f])+)\]))/gi,
|
||||
findEmailAndLinks = html => {
|
||||
return html
|
||||
.replace(url, '$1<a href="$2" target="_blank">$2</a>')
|
||||
.replace(email, '$1<a href="mailto:$2">$2</a>');
|
||||
};
|
||||
|
||||
let iMessageBodyCacheCount = 0;
|
||||
@@ -142,7 +149,7 @@ class MessageUserStore {
|
||||
|
||||
if (checked.length) {
|
||||
return selectedMessage
|
||||
? checked.concat([selectedMessage]).filter((value, index, self) => self.indexOf(value) == index)
|
||||
? checked.concat([selectedMessage]).unique()
|
||||
: checked;
|
||||
}
|
||||
|
||||
@@ -155,7 +162,7 @@ class MessageUserStore {
|
||||
if (message) {
|
||||
result.push(message.uid);
|
||||
if (1 < message.threadsLen()) {
|
||||
result = result.concat(message.threads()).filter((value, index, self) => self.indexOf(value) == index);
|
||||
result = result.concat(message.threads()).unique();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +53,7 @@ function domControlEncryptedClickHelper(store, dom, armoredMessage, recipients)
|
||||
} else if (validPrivateKey) {
|
||||
const keyIds = Array.isNotEmpty(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
|
||||
: '';
|
||||
|
||||
controlsHelper(
|
||||
@@ -109,7 +109,7 @@ function domControlSignedClickHelper(store, dom, armoredMessage) {
|
||||
} else {
|
||||
const keyIds = Array.isNotEmpty(signingKeyIds) ? signingKeyIds : null,
|
||||
additional = keyIds
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
|
||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
|
||||
: '';
|
||||
|
||||
controlsHelper(
|
||||
@@ -138,8 +138,8 @@ class PgpUserStore {
|
||||
this.openpgpkeys = ko.observableArray([]);
|
||||
this.openpgpKeyring = null;
|
||||
|
||||
this.openpgpkeysPublic = ko.computed(() => this.openpgpkeys().filter(item => !!(item && !item.isPrivate)));
|
||||
this.openpgpkeysPrivate = ko.computed(() => this.openpgpkeys().filter(item => !!(item && item.isPrivate)));
|
||||
this.openpgpkeysPublic = ko.computed(() => this.openpgpkeys().filter(item => item && !item.isPrivate));
|
||||
this.openpgpkeysPrivate = ko.computed(() => this.openpgpkeys().filter(item => item && item.isPrivate));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,14 +165,14 @@ class PgpUserStore {
|
||||
return this.openpgpkeysPublic().map(item => {
|
||||
const key = item && item.emails.includes(email) ? item : null;
|
||||
return key ? key.getNativeKeys() : [null];
|
||||
}).flat().filter(value => !!value);
|
||||
}).flat().filter(v => v);
|
||||
}
|
||||
|
||||
findPublicKeysBySigningKeyIds(signingKeyIds) {
|
||||
return signingKeyIds.map(id => {
|
||||
const key = id && id.toHex ? this.findPublicKeyByHex(id.toHex()) : null;
|
||||
return key ? key.getNativeKeys() : [null];
|
||||
}).flat().filter(value => !!value);
|
||||
}).flat().filter(v => v);
|
||||
}
|
||||
|
||||
findPrivateKeysByEncryptionKeyIds(encryptionKeyIds, recipients, returnWrapKeys) {
|
||||
@@ -180,7 +180,7 @@ class PgpUserStore {
|
||||
? encryptionKeyIds.map(id => {
|
||||
const key = id && id.toHex ? this.findPrivateKeyByHex(id.toHex()) : null;
|
||||
return key ? (returnWrapKeys ? [key] : key.getNativeKeys()) : [null];
|
||||
}).flat().filter(value => !!value)
|
||||
}).flat().filter(v => v)
|
||||
: [];
|
||||
|
||||
if (!result.length && Array.isNotEmpty(recipients)) {
|
||||
@@ -191,7 +191,7 @@ class PgpUserStore {
|
||||
? keys
|
||||
: keys.map(key => key.getNativeKeys()).flat()
|
||||
: [null];
|
||||
}).flat().filter((key, index, self) => key => !!key.id && self.indexOf(key) == index);
|
||||
}).flat().validUnique(key => key.id);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -15,7 +15,7 @@ class TemplateUserStore {
|
||||
|
||||
subscribers() {
|
||||
this.templates.subscribe((list) => {
|
||||
this.templatesNames(list.map(item => (item ? item.name : null)).filter(value => !!value));
|
||||
this.templatesNames(list.map(item => (item ? item.name : null)).filter(v => v));
|
||||
});
|
||||
|
||||
// this.templatesNames.subscribe((aList) => {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
} from 'Common/Enums';
|
||||
|
||||
import {
|
||||
replySubjectAdd,
|
||||
encodeHtml,
|
||||
inFocus,
|
||||
delegateRunOnDestroy,
|
||||
@@ -42,7 +41,50 @@ import { ComposeAttachmentModel } from 'Model/ComposeAttachment';
|
||||
import { popup, command, isPopupVisible, showScreenPopup, hideScreenPopup } from 'Knoin/Knoin';
|
||||
import { AbstractViewNext } from 'Knoin/AbstractViewNext';
|
||||
|
||||
const Settings = rl.settings;
|
||||
const Settings = rl.settings,
|
||||
/**
|
||||
* @param {string} prefix
|
||||
* @param {string} subject
|
||||
* @returns {string}
|
||||
*/
|
||||
replySubjectAdd = (prefix, subject) => {
|
||||
prefix = prefix.toUpperCase().trim();
|
||||
subject = subject.replace(/[\s]+/g, ' ').trim();
|
||||
|
||||
let drop = false,
|
||||
re = 'RE' === prefix,
|
||||
fwd = 'FWD' === prefix;
|
||||
|
||||
const parts = [],
|
||||
prefixIsRe = !fwd;
|
||||
|
||||
if (subject) {
|
||||
subject.split(':').forEach(part => {
|
||||
const trimmedPart = part.trim();
|
||||
if (!drop && (/^(RE|FWD)$/i.test(trimmedPart) || /^(RE|FWD)[[(][\d]+[\])]$/i.test(trimmedPart))) {
|
||||
if (!re) {
|
||||
re = !!/^RE/i.test(trimmedPart);
|
||||
}
|
||||
|
||||
if (!fwd) {
|
||||
fwd = !!/^FWD/i.test(trimmedPart);
|
||||
}
|
||||
} else {
|
||||
parts.push(part);
|
||||
drop = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (prefixIsRe) {
|
||||
re = false;
|
||||
} else {
|
||||
fwd = false;
|
||||
}
|
||||
|
||||
return ((prefixIsRe ? 'Re: ' : 'Fwd: ') + (re ? 'Re: ' : '')
|
||||
+ (fwd ? 'Fwd: ' : '') + parts.join(':').trim()).trim();
|
||||
};
|
||||
|
||||
ko.extenders.toggleSubscribe = (target, options) => {
|
||||
target.subscribe(options[1], options[0], 'beforeChange');
|
||||
@@ -773,7 +815,7 @@ class ComposePopupView extends AbstractViewNext {
|
||||
if (Array.isNotEmpty(emails)) {
|
||||
const value = fKoValue().trim(),
|
||||
values = emails.map(item => item ? item.toLine(false) : null)
|
||||
.filter((value, index, self) => !!value && self.indexOf(value) == index);
|
||||
.validUnique();
|
||||
|
||||
fKoValue(value + (value ? ', ' : '') + values.join(', ').trim());
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||
this.encryptKeys = ko.observableArray([]);
|
||||
|
||||
this.encryptKeysView = ko.computed(
|
||||
() => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(value => !!value)
|
||||
() => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(v => v)
|
||||
);
|
||||
|
||||
this.privateKeysOptions = ko.computed(() => {
|
||||
@@ -56,7 +56,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||
}));
|
||||
});
|
||||
|
||||
return opts.flat().filter(value => !!value);
|
||||
return opts.flat().filter(v => v);
|
||||
});
|
||||
|
||||
this.publicKeysOptions = ko.computed(() => {
|
||||
@@ -71,7 +71,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||
'class': index % 2 ? 'odd' : 'even'
|
||||
}));
|
||||
});
|
||||
return opts.flat().filter(value => !!value);
|
||||
return opts.flat().filter(v => v);
|
||||
});
|
||||
|
||||
this.submitRequest = ko.observable(false);
|
||||
@@ -156,7 +156,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||
|
||||
this.encryptKeys().forEach(oKey => {
|
||||
if (oKey && oKey.key) {
|
||||
aPublicKeys = aPublicKeys.concat(oKey.key.getNativeKeys().flat(Infinity).filter(value => !!value));
|
||||
aPublicKeys = aPublicKeys.concat(oKey.key.getNativeKeys().flat(Infinity).filter(v => v));
|
||||
} else if (oKey && oKey.email) {
|
||||
this.notification(
|
||||
i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
|
||||
@@ -349,7 +349,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||
email.clear();
|
||||
email.parse(value.trim());
|
||||
return email.email || false;
|
||||
}).filter(value => !!value);
|
||||
}).filter(v => v);
|
||||
|
||||
if (identity && identity.email()) {
|
||||
emailLine = identity.email();
|
||||
@@ -385,9 +385,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
|
||||
'key': publicKey
|
||||
}))
|
||||
: [];
|
||||
}).flat().filter(
|
||||
(encryptKey, index, self) => encryptKey => !!encryptKey.hash && self.indexOf(encryptKey) == index
|
||||
)
|
||||
}).flat().validUnique(encryptKey => encryptKey.hash)
|
||||
);
|
||||
|
||||
if (this.encryptKeys().length) {
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import {
|
||||
delegateRunOnDestroy,
|
||||
computedPagenatorHelper,
|
||||
fakeMd5,
|
||||
pInt
|
||||
} from 'Common/Utils';
|
||||
|
||||
@@ -154,7 +153,7 @@ class ContactsPopupView extends AbstractViewNext {
|
||||
selected = this.currentContact();
|
||||
|
||||
return selected
|
||||
? checked.concat([selected]).filter((value, index, self) => self.indexOf(value) == index)
|
||||
? checked.concat([selected]).unique()
|
||||
: checked;
|
||||
});
|
||||
|
||||
@@ -282,7 +281,7 @@ class ContactsPopupView extends AbstractViewNext {
|
||||
this.viewSaving(true);
|
||||
this.viewSaveTrigger(SaveSettingsStep.Animate);
|
||||
|
||||
const requestUid = fakeMd5(),
|
||||
const requestUid = Jua.randomId(),
|
||||
properties = [];
|
||||
|
||||
this.viewProperties().forEach(oItem => {
|
||||
@@ -579,7 +578,7 @@ class ContactsPopupView extends AbstractViewNext {
|
||||
return contact.parse(item) ? contact : null;
|
||||
});
|
||||
|
||||
list = list.filter(value => !!value);
|
||||
list = list.filter(v => v);
|
||||
|
||||
count = pInt(data.Result.Count);
|
||||
count = 0 < count ? count : 0;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import ko from 'ko';
|
||||
|
||||
import { StorageResultType, Notification } from 'Common/Enums';
|
||||
import { fakeMd5 } from 'Common/Utils';
|
||||
import { getNotification } from 'Common/Translator';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
@@ -158,7 +157,7 @@ class IdentityPopupView extends AbstractViewNext {
|
||||
|
||||
this.owner(!this.id);
|
||||
} else {
|
||||
this.id = fakeMd5();
|
||||
this.id = Jua.randomId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import ko from 'ko';
|
||||
|
||||
import { KeyState } from 'Common/Enums';
|
||||
import { selectElement } from 'Common/Utils';
|
||||
|
||||
import { popup } from 'Knoin/Knoin';
|
||||
import { AbstractViewNext } from 'Knoin/AbstractViewNext';
|
||||
@@ -27,7 +26,11 @@ class ViewOpenPgpKeyPopupView extends AbstractViewNext {
|
||||
selectKey() {
|
||||
const el = this.keyDom();
|
||||
if (el) {
|
||||
selectElement(el);
|
||||
let sel = getSelection(),
|
||||
range = document.createRange();
|
||||
sel.removeAllRanges();
|
||||
range.selectNodeContents(el);
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction } from 'Common/Glo
|
||||
|
||||
import {
|
||||
inFocus,
|
||||
removeSelection,
|
||||
mailToHelper,
|
||||
isTransparent
|
||||
} from 'Common/Utils';
|
||||
@@ -336,7 +335,9 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
removeSelection();
|
||||
try {
|
||||
getSelection().removeAllRanges();
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
|
||||
this.fullScreenMode(!this.fullScreenMode());
|
||||
}
|
||||
@@ -365,7 +366,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
// var oEmailModel = new EmailModel();
|
||||
// oEmailModel.parse(sItem);
|
||||
// return oEmailModel.email ? oEmailModel : null;
|
||||
// }).filter(value => !!value) : null;
|
||||
// }).filter(v => v) : null;
|
||||
// }
|
||||
// ;
|
||||
//
|
||||
@@ -395,7 +396,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}).filter(value => !!value);
|
||||
}).filter(v => v);
|
||||
|
||||
if (items.length) {
|
||||
}
|
||||
@@ -712,7 +713,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||
|
||||
getAttachmentsHashes() {
|
||||
const atts = this.message() ? this.message().attachments() : [];
|
||||
return atts.map(item => (item && !item.isLinked && item.checked() ? item.download : '')).filter(value => !!value);
|
||||
return atts.map(item => (item && !item.isLinked && item.checked() ? item.download : '')).filter(v => v);
|
||||
}
|
||||
|
||||
downloadAsZip() {
|
||||
|
||||
6
dev/prototype.js
vendored
6
dev/prototype.js
vendored
@@ -2,6 +2,9 @@
|
||||
(w=>{
|
||||
Array.isNotEmpty = array => Array.isArray(array) && array.length;
|
||||
Array.prototype.unique = function() { return this.filter((v, i, a) => a.indexOf(v) === i); };
|
||||
Array.prototype.validUnique = function(fn) {
|
||||
return this.filter((v, i, a) => (fn ? fn(v) : v) && a.indexOf(v) === i);
|
||||
};
|
||||
|
||||
// Import momentjs locales function
|
||||
w.moment = {
|
||||
@@ -45,7 +48,6 @@
|
||||
}
|
||||
},
|
||||
pad2 = v => 10 > v ? '0' + v : v,
|
||||
pad3 = v => 10 > v ? '00' + v : (100 > v ? '0' + v : v),
|
||||
getISODay = x => x.getDay() || 7,
|
||||
getDayOfYear = x => Math.floor((Date.UTC(x.getFullYear(),x.getMonth(),x.getDate())
|
||||
- Date.UTC(x.getFullYear(),0,1)) / 86400000),
|
||||
@@ -149,7 +151,7 @@
|
||||
case 'H': return pad2(d.H);
|
||||
case 'i': return pad2(UTC?x.getUTCMinutes():x.getMinutes());
|
||||
case 's': return pad2(UTC?x.getUTCSeconds():x.getSeconds());
|
||||
case 'u': return pad3(UTC?x.getUTCMilliseconds():x.getMilliseconds());
|
||||
case 'u': return (UTC?x.getUTCMilliseconds():x.getMilliseconds()).toString().padStart(3,'0');
|
||||
// Timezone
|
||||
case 'I': return UTC ? 0 : isDST(x) ? 1 : 0;
|
||||
case 'O': return UTC ? 'Z' : (d.Z > 0 ? '+' : '-') + pad2(Math.abs(d.Z / 60)) + '00';
|
||||
|
||||
14
vendors/jua/jua.js
vendored
14
vendors/jua/jua.js
vendored
@@ -488,13 +488,7 @@
|
||||
*/
|
||||
addNewFile(oFileInfo)
|
||||
{
|
||||
let iLen = 16,
|
||||
fakeMd5 = '';
|
||||
|
||||
while (iLen--)
|
||||
fakeMd5 += '0123456789abcdefghijklmnopqrstuvwxyz'.substr(Math.round(Math.random() * 36), 1);
|
||||
|
||||
this.addFile('jua-uid-' + fakeMd5 + '-' + (Date.now().toString()), oFileInfo);
|
||||
this.addFile('jua-uid-' + Jua.randomId(16) + '-' + (Date.now().toString()), oFileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -516,6 +510,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
Jua.randomId = len => {
|
||||
let arr = new Uint8Array((len || 32) / 2);
|
||||
crypto.getRandomValues(arr);
|
||||
return arr.map(dec => dec.toString(16).padStart(2,'0')).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
|
||||
2
vendors/jua/jua.min.js
vendored
2
vendors/jua/jua.min.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user