Use jQuery.slim

Underscore.js _.uniq(_.compact( to native Array.filter((value, index, self) => !!value && self.indexOf(value) == index)
Underscore.js _.compact to native Array.filter(value => !!value)
Underscore.js _.uniq to native Array.filter((value, index, self) => self.indexOf(value) == index)
Underscore.js _.values to native Object.values
Underscore.js _.flatten to native Array.flat
Underscore.js _.union to native Array.concat + unique filter
Underscore.js _.reduce to native Array.reduce
Underscore.js _.escape replaced with advanced htmlspecialchars()
Underscore.js _.memoize replaced
Now Underscore.js is a slim custom version (only _.debounce, _.defer & _.throttle)
This commit is contained in:
djmaze 2020-07-23 16:06:16 +02:00
parent 996a71ad8a
commit dca0ff02ed
27 changed files with 515 additions and 353 deletions

View file

@ -51,6 +51,12 @@ This fork has the following changes:
* CRLF => LF line endings
* Ongoing removal of jQuery and Underscore.js dependencies (things are native these days)
### slim jQuery and Underscore.js
js: 1.14.0 = 7960367 / native = 5127981 bytes
js/min: 1.14.0 = 1766594 / native = 1405850 bytes
360.744 bytes is nut much, but it already feels faster.
### PHP73 branch
There's a branch with only the PHP 7.3 changes at

View file

@ -86,15 +86,13 @@ class AdminApp extends AbstractApp {
});
if (isArray(data.Result.List)) {
list = _.compact(
data.Result.List.map(item => {
if (item) {
item.loading = ko.observable(!isUnd(loading[item.file]));
return 'core' === item.type && !item.canBeInstalled ? null : item;
}
return null;
})
);
list = data.Result.List.map(item => {
if (item) {
item.loading = ko.observable(!isUnd(loading[item.file]));
return 'core' === item.type && !item.canBeInstalled ? null : item;
}
return null;
}).filter(value => !!value);
}
PackageStore.packages(list);

View file

@ -321,7 +321,8 @@ class AppUser extends AbstractApp {
};
}
this.moveCache[hash].Uid = _.union(this.moveCache[hash].Uid, uidsForMove);
this.moveCache[hash].Uid = this.moveCache[hash].Uid.concat(uidsForMove)
.filter((value, index, self) => self.indexOf(value) == index);
this.messagesMoveTrigger();
}
@ -520,7 +521,9 @@ class AppUser extends AbstractApp {
.getKeyId()
.toHex()
.toLowerCase(),
_.uniq(_.compact(oItem.getKeyIds().map(item => (item && item.toHex ? item.toHex() : null)))),
oItem.getKeyIds()
.map(item => (item && item.toHex ? item.toHex() : null))
.filter((value, index, self) => !!value && self.indexOf(value) == index),
aUsers,
aEmails,
oItem.isPrivate(),
@ -634,12 +637,10 @@ class AppUser extends AbstractApp {
delegateRunOnDestroy(TemplateStore.templates());
TemplateStore.templates(
_.compact(
data.Result.Templates.map(templateData => {
const template = new TemplateModel();
return template.parse(templateData) ? template : null;
})
)
data.Result.Templates.map(templateData => {
const template = new TemplateModel();
return template.parse(templateData) ? template : null;
}).filter(value => !!value)
);
}
});
@ -819,7 +820,8 @@ class AppUser extends AbstractApp {
messages = MessageStore.messageListChecked();
}
rootUids = _.uniq(_.compact(messages.map(oMessage => (oMessage && oMessage.uid ? oMessage.uid : null))));
rootUids = messages.map(oMessage => oMessage && oMessage.uid ? oMessage.uid : null)
.filter((value, index, self) => !!value && self.indexOf(value) == index);
if ('' !== sFolderFullNameRaw && 0 < rootUids.length) {
switch (iSetAction) {
@ -880,7 +882,7 @@ class AppUser extends AbstractApp {
Remote.suggestions((result, data) => {
if (StorageResultType.Success === result && data && isArray(data.Result)) {
autocompleteCallback(
_.compact(data.Result.map(item => (item && item[0] ? new EmailModel(item[0], item[1]) : null)))
data.Result.map(item => (item && item[0] ? new EmailModel(item[0], item[1]) : null)).filter(value => !!value)
);
} else if (StorageResultType.Abort !== result) {
autocompleteCallback([]);
@ -899,10 +901,10 @@ class AppUser extends AbstractApp {
}
if (bExpanded) {
aExpandedList.push(sFullNameHash);
aExpandedList = _.uniq(aExpandedList);
if (!aExpandedList.includes(sFullNameHash))
aExpandedList.push(sFullNameHash);
} else {
aExpandedList = _.without(aExpandedList, sFullNameHash);
aExpandedList = aExpandedList.filter(value => value !== sFullNameHash);
}
Local.set(ClientSideKeyName.ExpandedFolders, aExpandedList);

View file

@ -18,7 +18,49 @@ export const FileType = {
'Presentation': 'presentation',
'Certificate': 'certificate',
'CertificateBin': 'certificate-bin',
'Archive': 'archive'
'Archive': 'archive',
getIconClass: function(type) {
let result = ['icon-file', ''];
switch (type) {
case this.Text:
case this.Eml:
case this.WordText:
result[0] += '-text';
break;
case this.Html:
case this.Code:
result[0] += '-code';
break;
case this.Image:
result[0] += '-image';
break;
case this.Audio:
result[0] += '-music';
break;
case this.Video:
result[0] += '-movie';
break;
case this.Archive:
result[0] += '-zip';
break;
case this.Certificate:
case this.CertificateBin:
result[0] += '-certificate';
break;
case this.Sheet:
result[0] += '-excel';
break;
case this.Presentation:
result[0] += '-chart-graph';
break;
case this.Pdf:
result['icon-none', 'pdf'];
break;
// no default
}
return result;
}
};
/**

View file

@ -11,13 +11,25 @@ import { jassl } from 'Common/Jassl';
const trim = $.trim;
const isArray = Array.isArray;
const isObject = _.isObject;
const isFunc = _.isFunction;
const isUnd = _.isUndefined;
const isObject = v => typeof v === 'object';
const isFunc = v => typeof v === 'function';
const isUnd = v => undefined === v;
const noop = () => {}; // eslint-disable-line no-empty-function
const noopTrue = () => true;
const noopFalse = () => false;
var htmlspecialchars = ((de,se,gt,lt,sq,dq,bt) => {
return (str, quote_style = 3, double_encode = true) => {
str = (''+str)
.replace(double_encode?de:se,'&amp;')
.replace(gt,'&lt;')
.replace(lt,'&gt;')
.replace(bt,'&#x60;');
if (quote_style & 1) { str = str.replace(sq,'&#x27;'); }
return (quote_style & 2) ? str.replace(dq,'&quot;') : str;
};
})(/&/g,/&(?![\w#]+;)/gi,/</g,/>/g,/'/g,/"/g,/`/g);
export { trim, isArray, isObject, isFunc, isUnd, noop, noopTrue, noopFalse, jassl };
/**
@ -167,7 +179,7 @@ export function fakeMd5(len = 32) {
* @returns {string}
*/
export function encodeHtml(text) {
return isNormal(text) ? _.escape(text.toString()) : '';
return isNormal(text) ? htmlspecialchars(text.toString()) : '';
}
/**
@ -449,7 +461,7 @@ export function createCommandLegacy(context, fExecute, fCanExecute = true) {
* @param {string} theme
* @returns {string}
*/
export const convertThemeName = _.memoize((theme) => {
export const convertThemeName = theme => {
if ('@custom' === theme.substr(-7)) {
theme = trim(theme.substring(0, theme.length - 7));
}
@ -458,9 +470,9 @@ export const convertThemeName = _.memoize((theme) => {
theme
.replace(/[^a-zA-Z0-9]+/g, ' ')
.replace(/([A-Z])/g, ' $1')
.replace(/[\s]+/g, ' ')
.replace(/\s+/g, ' ')
);
});
};
/**
* @param {string} name
@ -713,7 +725,7 @@ export function htmlToPlain(html) {
.replace(/[\n]/gm, '<br />')
.replace(/[\r]/gm, '')
: '',
fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + _.escape(args[2]) : ''),
fixAttibuteValue = (...args) => (args && 1 < args.length ? '' + args[1] + htmlspecialchars(args[2]) : ''),
convertLinks = (...args) => (args && 1 < args.length ? trim(args[1]) : '');
text = html
@ -1384,7 +1396,7 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) {
if (!isUnd(params.to)) {
to = EmailModel.parseEmailLine(decodeURIComponent(email + ',' + params.to));
to = _.values(
to = Object.values(
to.reduce((result, value) => {
if (value) {
if (result[value.email]) {

35
dev/External/ko.js vendored
View file

@ -13,7 +13,8 @@ const ko = window.ko,
element.__opentip.deactivate();
}
});
};
},
isFunction = v => typeof v === 'function';
ko.bindingHandlers.updateWidth = {
init: (element, fValueAccessor) => {
@ -145,7 +146,7 @@ ko.bindingHandlers.tooltip = {
Globals = require('Common/Globals');
if (!Globals.bMobileDevice || isMobile) {
const sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue);
const sValue = !ko.isObservable(fValue) && isFunction(fValue) ? fValue() : ko.unwrap(fValue);
element.__opentip = new Opentip(element, {
'style': 'rainloopTip',
@ -203,7 +204,7 @@ ko.bindingHandlers.tooltip = {
Globals = require('Common/Globals');
if ((!Globals.bMobileDevice || isMobile) && element.__opentip) {
const sValue = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue);
const sValue = !ko.isObservable(fValue) && isFunction(fValue) ? fValue() : ko.unwrap(fValue);
if (sValue) {
element.__opentip.setContent(isI18N ? require('Common/Translator').i18n(sValue) : sValue);
element.__opentip.activate();
@ -240,7 +241,7 @@ ko.bindingHandlers.tooltipErrorTip = {
update: (element, fValueAccessor) => {
const $el = $(element),
fValue = fValueAccessor(),
value = !ko.isObservable(fValue) && _.isFunction(fValue) ? fValue() : ko.unwrap(fValue),
value = !ko.isObservable(fValue) && isFunction(fValue) ? fValue() : ko.unwrap(fValue),
openTips = element.__opentip;
if (openTips) {
@ -797,8 +798,7 @@ ko.bindingHandlers.saveTrigger = {
ko.bindingHandlers.emailsTags = {
init: (element, fValueAccessor, fAllBindingsAccessor) => {
const Utils = require('Common/Utils'),
EmailModel = require('Model/Email').default,
const EmailModel = require('Model/Email').default,
$el = $(element),
fValue = fValueAccessor(),
fAllBindings = fAllBindingsAccessor(),
@ -817,20 +817,18 @@ ko.bindingHandlers.emailsTags = {
inputDelimiters: inputDelimiters,
autoCompleteSource: fAutoCompleteSource,
splitHook: (value) => {
const v = Utils.trim(value);
const v = value.trim();
if (v && inputDelimiters.includes(v.substr(-1))) {
return EmailModel.splitEmailLine(value);
}
return null;
},
parseHook: (input) =>
_.flatten(
input.map(inputValue => {
const values = EmailModel.parseEmailLine(inputValue);
return values.length ? values : inputValue;
})
).map(
item => (_.isObject(item) ? [item.toLine(false), item] : [item, null])
input.map(inputValue => {
const values = EmailModel.parseEmailLine(inputValue);
return values.length ? values : inputValue;
}).flat(Infinity).map(
item => (item.toLine ? [item.toLine(false), item] : [item, null])
),
change: (event) => {
$el.data('EmailsTagsValue', event.target.value);
@ -872,7 +870,7 @@ ko.bindingHandlers.command = {
if (!command.canExecute) {
const __realCanExecute = command.__realCanExecute;
if (_.isFunction(__realCanExecute)) {
if (isFunction(__realCanExecute)) {
command.canExecute = ko.computed(() => command.enabled() && __realCanExecute.call(viewModel, viewModel));
} else {
command.canExecute = ko.computed(() => command.enabled() && !!__realCanExecute);
@ -912,11 +910,10 @@ ko.bindingHandlers.command = {
// extenders
ko.extenders.trimmer = (target) => {
const Utils = require('Common/Utils'),
result = ko.computed({
const result = ko.computed({
read: target,
write: (newValue) => {
target(Utils.trim(newValue.toString()));
target(newValue.toString().trim());
}
});
@ -1116,7 +1113,7 @@ ko.observable.fn.deleteAccessHelper = function() {
ko.observable.fn.validateFunc = function(fFunc) {
this.hasFuncError = ko.observable(false);
if (_.isFunction(fFunc)) {
if (isFunction(fFunc)) {
this.subscribe((value) => {
this.hasFuncError(!fFunc(value));
});

View file

@ -1,5 +1,4 @@
import window from 'window';
import _ from '_';
import ko from 'ko';
import { FileType } from 'Common/Enums';
@ -22,159 +21,123 @@ import Audio from 'Common/Audio';
* @param {string} sMimeType
* @returns {string}
*/
export const staticFileType = _.memoize((ext, mimeType) => {
ext = trim(ext).toLowerCase();
mimeType = trim(mimeType).toLowerCase();
export const staticFileType = (() => {
let cache = {};
return (ext, mimeType) => {
ext = trim(ext).toLowerCase();
mimeType = trim(mimeType).toLowerCase();
let result = FileType.Unknown;
const mimeTypeParts = mimeType.split('/');
let key = ext + mimeType;
if (cache[key]) {
return cache[key];
}
switch (true) {
case 'image' === mimeTypeParts[0] || ['png', 'jpg', 'jpeg', 'gif', 'bmp'].includes(ext):
result = FileType.Image;
break;
case 'audio' === mimeTypeParts[0] || ['mp3', 'ogg', 'oga', 'wav'].includes(ext):
result = FileType.Audio;
break;
case 'video' === mimeTypeParts[0] || ['mkv', 'avi'].includes(ext):
result = FileType.Video;
break;
case ['php', 'js', 'css'].includes(ext):
result = FileType.Code;
break;
case 'eml' === ext || ['message/delivery-status', 'message/rfc822'].includes(mimeType):
result = FileType.Eml;
break;
case ('text' === mimeTypeParts[0] && 'html' !== mimeTypeParts[1]) || ['txt', 'log'].includes(ext):
result = FileType.Text;
break;
case 'text/html' === mimeType || ['html'].includes(ext):
result = FileType.Html;
break;
case [
'zip',
'7z',
'tar',
'rar',
'gzip',
'bzip',
'bzip2',
'x-zip',
'x-7z',
'x-rar',
'x-tar',
'x-gzip',
'x-bzip',
'x-bzip2',
'x-zip-compressed',
'x-7z-compressed',
'x-rar-compressed'
].includes(mimeTypeParts[1]) || ['zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2'].includes(ext):
result = FileType.Archive;
break;
case ['pdf', 'x-pdf'].includes(mimeTypeParts[1]) || ['pdf'].includes(ext):
result = FileType.Pdf;
break;
case ['application/pgp-signature', 'application/pgp-keys'].includes(mimeType) ||
['asc', 'pem', 'ppk'].includes(ext):
result = FileType.Certificate;
break;
case ['application/pkcs7-signature'].includes(mimeType) || ['p7s'].includes(ext):
result = FileType.CertificateBin;
break;
case [
'rtf',
'msword',
'vnd.msword',
'vnd.openxmlformats-officedocument.wordprocessingml.document',
'vnd.openxmlformats-officedocument.wordprocessingml.template',
'vnd.ms-word.document.macroEnabled.12',
'vnd.ms-word.template.macroEnabled.12'
].includes(mimeTypeParts[1]):
result = FileType.WordText;
break;
case [
'excel',
'ms-excel',
'vnd.ms-excel',
'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'vnd.openxmlformats-officedocument.spreadsheetml.template',
'vnd.ms-excel.sheet.macroEnabled.12',
'vnd.ms-excel.template.macroEnabled.12',
'vnd.ms-excel.addin.macroEnabled.12',
'vnd.ms-excel.sheet.binary.macroEnabled.12'
].includes(mimeTypeParts[1]):
result = FileType.Sheet;
break;
case [
'powerpoint',
'ms-powerpoint',
'vnd.ms-powerpoint',
'vnd.openxmlformats-officedocument.presentationml.presentation',
'vnd.openxmlformats-officedocument.presentationml.template',
'vnd.openxmlformats-officedocument.presentationml.slideshow',
'vnd.ms-powerpoint.addin.macroEnabled.12',
'vnd.ms-powerpoint.presentation.macroEnabled.12',
'vnd.ms-powerpoint.template.macroEnabled.12',
'vnd.ms-powerpoint.slideshow.macroEnabled.12'
].includes(mimeTypeParts[1]):
result = FileType.Presentation;
break;
// no default
}
let result = FileType.Unknown;
const mimeTypeParts = mimeType.split('/');
return result;
});
switch (true) {
case 'image' === mimeTypeParts[0] || ['png', 'jpg', 'jpeg', 'gif', 'bmp'].includes(ext):
result = FileType.Image;
break;
case 'audio' === mimeTypeParts[0] || ['mp3', 'ogg', 'oga', 'wav'].includes(ext):
result = FileType.Audio;
break;
case 'video' === mimeTypeParts[0] || ['mkv', 'avi'].includes(ext):
result = FileType.Video;
break;
case ['php', 'js', 'css'].includes(ext):
result = FileType.Code;
break;
case 'eml' === ext || ['message/delivery-status', 'message/rfc822'].includes(mimeType):
result = FileType.Eml;
break;
case ('text' === mimeTypeParts[0] && 'html' !== mimeTypeParts[1]) || ['txt', 'log'].includes(ext):
result = FileType.Text;
break;
case 'text/html' === mimeType || ['html'].includes(ext):
result = FileType.Html;
break;
case [
'zip',
'7z',
'tar',
'rar',
'gzip',
'bzip',
'bzip2',
'x-zip',
'x-7z',
'x-rar',
'x-tar',
'x-gzip',
'x-bzip',
'x-bzip2',
'x-zip-compressed',
'x-7z-compressed',
'x-rar-compressed'
].includes(mimeTypeParts[1]) || ['zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2'].includes(ext):
result = FileType.Archive;
break;
case ['pdf', 'x-pdf'].includes(mimeTypeParts[1]) || ['pdf'].includes(ext):
result = FileType.Pdf;
break;
case ['application/pgp-signature', 'application/pgp-keys'].includes(mimeType) ||
['asc', 'pem', 'ppk'].includes(ext):
result = FileType.Certificate;
break;
case ['application/pkcs7-signature'].includes(mimeType) || ['p7s'].includes(ext):
result = FileType.CertificateBin;
break;
case [
'rtf',
'msword',
'vnd.msword',
'vnd.openxmlformats-officedocument.wordprocessingml.document',
'vnd.openxmlformats-officedocument.wordprocessingml.template',
'vnd.ms-word.document.macroEnabled.12',
'vnd.ms-word.template.macroEnabled.12'
].includes(mimeTypeParts[1]):
result = FileType.WordText;
break;
case [
'excel',
'ms-excel',
'vnd.ms-excel',
'vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'vnd.openxmlformats-officedocument.spreadsheetml.template',
'vnd.ms-excel.sheet.macroEnabled.12',
'vnd.ms-excel.template.macroEnabled.12',
'vnd.ms-excel.addin.macroEnabled.12',
'vnd.ms-excel.sheet.binary.macroEnabled.12'
].includes(mimeTypeParts[1]):
result = FileType.Sheet;
break;
case [
'powerpoint',
'ms-powerpoint',
'vnd.ms-powerpoint',
'vnd.openxmlformats-officedocument.presentationml.presentation',
'vnd.openxmlformats-officedocument.presentationml.template',
'vnd.openxmlformats-officedocument.presentationml.slideshow',
'vnd.ms-powerpoint.addin.macroEnabled.12',
'vnd.ms-powerpoint.presentation.macroEnabled.12',
'vnd.ms-powerpoint.template.macroEnabled.12',
'vnd.ms-powerpoint.slideshow.macroEnabled.12'
].includes(mimeTypeParts[1]):
result = FileType.Presentation;
break;
// no default
}
return cache[key] = result;
};
})();
/**
* @param {string} sFileType
* @returns {string}
*/
export const staticIconClass = _.memoize((fileType) => {
let resultText = '',
resultClass = 'icon-file';
switch (fileType) {
case FileType.Text:
case FileType.Eml:
case FileType.WordText:
resultClass = 'icon-file-text';
break;
case FileType.Html:
case FileType.Code:
resultClass = 'icon-file-code';
break;
case FileType.Image:
resultClass = 'icon-file-image';
break;
case FileType.Audio:
resultClass = 'icon-file-music';
break;
case FileType.Video:
resultClass = 'icon-file-movie';
break;
case FileType.Archive:
resultClass = 'icon-file-zip';
break;
case FileType.Certificate:
case FileType.CertificateBin:
resultClass = 'icon-file-certificate';
break;
case FileType.Sheet:
resultClass = 'icon-file-excel';
break;
case FileType.Presentation:
resultClass = 'icon-file-chart-graph';
break;
case FileType.Pdf:
resultText = 'pdf';
resultClass = 'icon-none';
break;
// no default
}
return [resultClass, resultText];
});
export const staticIconClass = fileType => FileType.getIconClass(fileType);
/**
* @static
@ -187,7 +150,8 @@ export const staticCombinedIconClass = (data) => {
if (isNonEmptyArray(data)) {
result = 'icon-attachment';
types = _.uniq(_.compact(data.map(item => (item ? staticFileType(getFileExtension(item[0]), item[1]) : ''))));
types = data.map(item => item ? staticFileType(getFileExtension(item[0]), item[1]) : '')
.filter((value, index, self) => !!value && self.indexOf(value) == index);
if (types && 1 === types.length && types[0]) {
switch (types[0]) {

View file

@ -1,4 +1,3 @@
import _ from '_';
import addressparser from 'emailjs-addressparser';
import { trim, encodeHtml, isNonEmptyArray } from 'Common/Utils';
@ -184,11 +183,9 @@ class EmailModel {
static parseEmailLine(line) {
const parsedResult = addressparser(line);
if (isNonEmptyArray(parsedResult)) {
return _.compact(
parsedResult.map(item =>
item.address ? new EmailModel(item.address.replace(/^[<]+(.*)[>]+$/g, '$1'), item.name || '') : null
)
);
return parsedResult.map(item =>
item.address ? new EmailModel(item.address.replace(/^[<]+(.*)[>]+$/g, '$1'), item.name || '') : null
).filter(value => !!value);
}
return [];

View file

@ -1,4 +1,3 @@
import _ from '_';
import ko from 'ko';
import { FilterRulesType, FiltersAction } from 'Common/Enums';
@ -230,12 +229,10 @@ class FilterModel extends AbstractModel {
if (isNonEmptyArray(json.Conditions)) {
this.conditions(
_.compact(
json.Conditions.map(aData => {
const filterCondition = new FilterConditionModel();
return filterCondition && filterCondition.parse(aData) ? filterCondition : null;
})
)
json.Conditions.map(aData => {
const filterCondition = new FilterConditionModel();
return filterCondition && filterCondition.parse(aData) ? filterCondition : null;
}).filter(value => !!value)
);
}

View file

@ -1,4 +1,3 @@
import _ from '_';
import $ from '$';
import ko from 'ko';
import moment from 'moment';
@ -200,13 +199,9 @@ class MessageModel extends AbstractModel {
* @returns {Array}
*/
getEmails(properties) {
return _.compact(
_.uniq(
_.reduce(properties, (carry, property) => carry.concat(this[property]), []).map(
(oItem) => (oItem ? oItem.email : '')
)
)
);
return properties.reduce((carry, property) => carry.concat(this[property]), []).map(
oItem => oItem ? oItem.email : ''
).filter((value, index, self) => !!value && self.indexOf(value) == index);
}
/**

View file

@ -1,4 +1,3 @@
import _ from '_';
import ko from 'ko';
import { windowResizeCallback, isArray, trim, delegateRunOnDestroy } from 'Common/Utils';
@ -113,12 +112,10 @@ class FiltersUserSettings {
this.serverError(false);
this.filters(
_.compact(
data.Result.Filters.map(aItem => {
const filter = new FilterModel();
return filter && filter.parse(aItem) ? filter : null;
})
)
data.Result.Filters.map(aItem => {
const filter = new FilterModel();
return filter && filter.parse(aItem) ? filter : null;
}).filter(value => !!value)
);
this.modules(data.Result.Modules ? data.Result.Modules : {});

View file

@ -1,5 +1,4 @@
import ko from 'ko';
import _ from '_';
import { Magics } from 'Common/Enums';
import * as Settings from 'Storage/Settings';
@ -17,7 +16,9 @@ class AccountUserStore {
}
computers() {
this.accountsEmails = ko.computed(() => _.compact(this.accounts().map(item => (item ? item.email : null))));
this.accountsEmails = ko.computed(
() => this.accounts().map(item => (item ? item.email : null)).filter(value => !!value)
);
this.accountsUnreadCount = ko.computed(() => 0);
// this.accountsUnreadCount = ko.computed(() => {

View file

@ -1,5 +1,4 @@
import ko from 'ko';
import _ from '_';
import { settingsGet } from 'Storage/Settings';
@ -95,7 +94,7 @@ class FolderUserStore {
});
this.folderListSystem = ko.computed(() =>
_.compact(this.folderListSystemNames().map(name => getFolderFromCacheList(name)))
this.folderListSystemNames().map(name => getFolderFromCacheList(name)).filter(value => !!value)
);
this.folderMenuForMove = ko.computed(() =>
@ -203,7 +202,7 @@ class FolderUserStore {
return limit <= result.length;
});
return _.uniq(result);
return result.filter((value, index, self) => self.indexOf(value) == index);
}
}

View file

@ -1,4 +1,3 @@
import _ from '_';
import ko from 'ko';
class IdentityUserStore {
@ -6,7 +5,9 @@ class IdentityUserStore {
this.identities = ko.observableArray([]);
this.identities.loading = ko.observable(false).extend({ throttle: 100 });
this.identitiesIDS = ko.computed(() => _.compact(this.identities().map(item => (item ? item.id : null))));
this.identitiesIDS = ko.computed(
() => this.identities().map(item => (item ? item.id : null)).filter(value => !!value)
);
}
}

View file

@ -153,7 +153,9 @@ class MessageUserStore {
focusedMessage = this.selectorMessageFocused();
if (checked.length) {
return _.union(checked, selectedMessage ? [selectedMessage] : []);
return selectedMessage
? checked.concat([selectedMessage]).filter((value, index, self) => self.indexOf(value) == index)
: checked;
} else if (selectedMessage) {
return [selectedMessage];
}
@ -167,7 +169,7 @@ class MessageUserStore {
if (message) {
result.push(message.uid);
if (1 < message.threadsLen()) {
result = _.union(result, message.threads());
result = result.concat(message.threads()).filter((value, index, self) => self.indexOf(value) == index);
}
}
});

View file

@ -42,62 +42,36 @@ class PgpUserStore {
}
findPublicKeysByEmail(email) {
return _.compact(
_.flatten(
this.openpgpkeysPublic().map(item => {
const key = item && item.emails.includes(email) ? item : null;
return key ? key.getNativeKeys() : [null];
}),
true
)
);
return this.openpgpkeysPublic().map(item => {
const key = item && item.emails.includes(email) ? item : null;
return key ? key.getNativeKeys() : [null];
}).flat().filter(value => !!value);
}
findPublicKeysBySigningKeyIds(signingKeyIds) {
return _.compact(
_.flatten(
signingKeyIds.map(id => {
const key = id && id.toHex ? this.findPublicKeyByHex(id.toHex()) : null;
return key ? key.getNativeKeys() : [null];
}),
true
)
);
return signingKeyIds.map(id => {
const key = id && id.toHex ? this.findPublicKeyByHex(id.toHex()) : null;
return key ? key.getNativeKeys() : [null];
}).flat().filter(value => !!value);
}
findPrivateKeysByEncryptionKeyIds(encryptionKeyIds, recipients, returnWrapKeys) {
let result = isArray(encryptionKeyIds)
? _.compact(
_.flatten(
encryptionKeyIds.map(id => {
const key = id && id.toHex ? this.findPrivateKeyByHex(id.toHex()) : null;
return key ? (returnWrapKeys ? [key] : key.getNativeKeys()) : [null];
}),
true
)
)
? encryptionKeyIds.map(id => {
const key = id && id.toHex ? this.findPrivateKeyByHex(id.toHex()) : null;
return key ? (returnWrapKeys ? [key] : key.getNativeKeys()) : [null];
}).flat().filter(value => !!value)
: [];
if (0 === result.length && isNonEmptyArray(recipients)) {
result = _.uniq(
_.compact(
_.flatten(
recipients.map(sEmail => {
const keys = sEmail ? this.findAllPrivateKeysByEmailNotNative(sEmail) : null;
return keys
? returnWrapKeys
? keys
: _.flatten(
keys.map(key => key.getNativeKeys()),
true
)
: [null];
}),
true
)
),
(key) => key.id
);
result = recipients.map(sEmail => {
const keys = sEmail ? this.findAllPrivateKeysByEmailNotNative(sEmail) : null;
return keys
? returnWrapKeys
? keys
: keys.map(key => key.getNativeKeys()).flat()
: [null];
}).flat().filter((key, index, self) => key => !!key.id && self.indexOf(key) == index);
}
return result;
@ -297,7 +271,7 @@ class PgpUserStore {
} else if (validPrivateKey) {
const keyIds = isNonEmptyArray(signingKeyIds) ? signingKeyIds : null,
additional = keyIds
? _.compact(keyIds.map(item => (item && item.toHex ? item.toHex() : null))).join(', ')
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
: '';
store.controlsHelper(
@ -354,7 +328,7 @@ class PgpUserStore {
} else {
const keyIds = isNonEmptyArray(signingKeyIds) ? signingKeyIds : null,
additional = keyIds
? _.compact(keyIds.map(item => (item && item.toHex ? item.toHex() : null))).join(', ')
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(value => !!value).join(', ')
: '';
store.controlsHelper(

View file

@ -1,5 +1,4 @@
import ko from 'ko';
import _ from '_';
// import Remote from 'Remote/User/Ajax';
@ -16,7 +15,7 @@ class TemplateUserStore {
subscribers() {
this.templates.subscribe((list) => {
this.templatesNames(_.compact(list.map(item => (item ? item.name : null))));
this.templatesNames(list.map(item => (item ? item.name : null)).filter(value => !!value));
});
// this.templatesNames.subscribe((aList) => {

View file

@ -600,13 +600,13 @@ class ComposePopupView extends AbstractViewNext {
case ComposeType.ReplyAll:
case ComposeType.Forward:
case ComposeType.ForwardAsAttachment:
_.union(message.to, message.cc, message.bcc).forEach(fEachHelper);
message.to.concat(message.cc, message.bcc).forEach(fEachHelper);
if (!resultIdentity) {
message.deliveredTo.forEach(fEachHelper);
}
break;
case ComposeType.Draft:
_.union(message.from, message.replyTo).forEach(fEachHelper);
message.from.concat(message.replyTo).forEach(fEachHelper);
break;
// no default
}
@ -859,7 +859,8 @@ class ComposePopupView extends AbstractViewNext {
addEmailsTo(fKoValue, emails) {
if (isNonEmptyArray(emails)) {
const value = trim(fKoValue()),
values = _.uniq(_.compact(emails.map(item => (item ? item.toLine(false) : null))));
values = emails.map(item => item ? item.toLine(false) : null)
.filter((value, index, self) => !!value && self.indexOf(value) == index);
fKoValue(value + ('' === value ? '' : ', ') + trim(values.join(', ')));
}

View file

@ -1,4 +1,3 @@
import _ from '_';
import $ from '$';
import ko from 'ko';
import key from 'key';
@ -44,7 +43,9 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
this.signKey = ko.observable(null);
this.encryptKeys = ko.observableArray([]);
this.encryptKeysView = ko.computed(() => _.compact(this.encryptKeys().map(oKey => (oKey ? oKey.key : null))));
this.encryptKeysView = ko.computed(
() => this.encryptKeys().map(oKey => (oKey ? oKey.key : null)).filter(value => !!value)
);
this.privateKeysOptions = ko.computed(() => {
const opts = PgpStore.openpgpkeysPrivate().map((oKey, iIndex) => {
@ -59,7 +60,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
}));
});
return _.compact(_.flatten(opts, true));
return opts.flat().filter(value => !!value);
});
this.publicKeysOptions = ko.computed(() => {
@ -74,7 +75,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
'class': index % 2 ? 'odd' : 'even'
}));
});
return _.compact(_.flatten(opts, true));
return opts.flat().filter(value => !!value);
});
this.submitRequest = ko.observable(false);
@ -159,7 +160,7 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
this.encryptKeys().forEach(oKey => {
if (oKey && oKey.key) {
aPublicKeys = aPublicKeys.concat(_.compact(_.flatten(oKey.key.getNativeKeys())));
aPublicKeys = aPublicKeys.concat(oKey.key.getNativeKeys().flat(Infinity).filter(value => !!value));
} else if (oKey && oKey.email) {
this.notification(
i18n('PGP_NOTIFICATIONS/NO_PUBLIC_KEYS_FOUND_FOR', {
@ -355,13 +356,11 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
}
rec = rec.join(', ').split(',');
rec = _.compact(
rec.map(value => {
rec = rec.map(value => {
email.clear();
email.parse(trim(value));
return '' === email.email ? false : email.email;
})
);
}).filter(value => !!value);
if (identity && identity.email()) {
emailLine = identity.email();
@ -383,28 +382,22 @@ class ComposeOpenPgpPopupView extends AbstractViewNext {
if (rec && 0 < rec.length) {
this.encryptKeys(
_.uniq(
_.compact(
_.flatten(
rec.map(recEmail => {
const keys = PgpStore.findAllPublicKeysByEmailNotNative(recEmail);
return keys
? keys.map(publicKey => ({
'empty': !publicKey,
'selected': ko.observable(!!publicKey),
'removable': ko.observable(
!this.sign() || !this.signKey() || this.signKey().key.id !== publicKey.id
),
'users': publicKey ? publicKey.users || [recEmail] : [recEmail],
'hash': publicKey ? publicKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() : '',
'key': publicKey
}))
: [];
}),
true
)
),
(encryptKey) => encryptKey.hash
rec.map(recEmail => {
const keys = PgpStore.findAllPublicKeysByEmailNotNative(recEmail);
return keys
? keys.map(publicKey => ({
'empty': !publicKey,
'selected': ko.observable(!!publicKey),
'removable': ko.observable(
!this.sign() || !this.signKey() || this.signKey().key.id !== publicKey.id
),
'users': publicKey ? publicKey.users || [recEmail] : [recEmail],
'hash': publicKey ? publicKey.id.substr(KEY_NAME_SUBSTR).toUpperCase() : '',
'key': publicKey
}))
: [];
}).flat().filter(
(encryptKey, index, self) => encryptKey => !!encryptKey.hash && self.indexOf(encryptKey) == index
)
);

View file

@ -1,5 +1,4 @@
import window from 'window';
import _ from '_';
import $ from '$';
import ko from 'ko';
import key from 'key';
@ -171,7 +170,9 @@ class ContactsPopupView extends AbstractViewNext {
const checked = this.contactsChecked(),
selected = this.currentContact();
return _.union(checked, selected ? [selected] : []);
return selected
? checked.concat([selected]).filter((value, index, self) => self.indexOf(value) == index)
: checked;
});
this.contactsCheckedOrSelectedUids = ko.computed(() =>
@ -253,7 +254,7 @@ class ContactsPopupView extends AbstractViewNext {
return null;
});
aE = _.compact(aE);
aE = aE.filter(value => !!value);
}
if (isNonEmptyArray(aE)) {
@ -596,7 +597,7 @@ class ContactsPopupView extends AbstractViewNext {
return contact.parse(item) ? contact : null;
});
list = _.compact(list);
list = list.filter(value => !!value);
count = pInt(data.Result.Count);
count = 0 < count ? count : 0;

View file

@ -453,11 +453,11 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
// aTo = [],
// EmailModel = require('Model/Email').default,
// fParseEmailLine = function(sLine) {
// return sLine ? _.compact([window.decodeURIComponent(sLine)].map(sItem => {
// return sLine ? [window.decodeURIComponent(sLine)].map(sItem => {
// var oEmailModel = new EmailModel();
// oEmailModel.parse(sItem);
// return '' !== oEmailModel.email ? oEmailModel : null;
// })) : null;
// }).filter(value => !!value) : null;
// }
// ;
//
@ -477,26 +477,24 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
listIndex = 0;
const div = $('<div>'),
dynamicEls = _.compact(
this.message().attachments().map(item => {
if (item && !item.isLinked && item.isImage()) {
if (item === attachment) {
index = listIndex;
}
listIndex += 1;
return {
src: item.linkPreview(),
thumb: item.linkThumbnail(),
subHtml: item.fileName,
downloadUrl: item.linkPreview()
};
dynamicEls = this.message().attachments().map(item => {
if (item && !item.isLinked && item.isImage()) {
if (item === attachment) {
index = listIndex;
}
return null;
})
);
listIndex += 1;
return {
src: item.linkPreview(),
thumb: item.linkThumbnail(),
subHtml: item.fileName,
downloadUrl: item.linkPreview()
};
}
return null;
}).filter(value => !!value);
if (0 < dynamicEls.length) {
div.on('onBeforeOpen.lg', () => {
@ -885,7 +883,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
getAttachmentsHashes() {
const atts = this.message() ? this.message().attachments() : [];
return _.compact(atts.map(item => (item && !item.isLinked && item.checked() ? item.download : '')));
return atts.map(item => (item && !item.isLinked && item.checked() ? item.download : '')).filter(value => !!value);
}
downloadAsZip() {

16
dev/polyfill.js Normal file
View file

@ -0,0 +1,16 @@
Array.prototype.flat || Object.defineProperty(Array.prototype, 'flat', {
configurable: true,
value: function flat(depth) {
depth = isNaN(depth) ? 1 : Number(depth);
return depth ? Array.prototype.reduce.call(this, (acc, cur) => {
if (Array.isArray(cur)) {
acc.push.apply(acc, flat.call(cur, depth - 1));
} else {
acc.push(cur);
}
return acc;
}, []) : this.slice();
},
writable: true
});

View file

@ -103,8 +103,7 @@
"raw-loader": "4.0.0",
"rimraf": "3.0.2",
"simplestatemanager": "4.1.1",
"style-loader": "1.1.3",
"underscore": "1.9.2"
"style-loader": "1.1.3"
},
"dependencies": {
"gulp-terser": "^1.2.0",

View file

@ -72,7 +72,8 @@ config.paths.js = {
libs: {
name: 'libs.js',
src: [
'node_modules/jquery/dist/jquery.min.js',
'dev/polyfill.js',
'node_modules/jquery/dist/jquery.slim.min.js',
'vendors/jquery-ui/js/jquery-ui-1.12.1.custom.min.js', // custom
'vendors/inputosaurus/inputosaurus.js', // custom (modified)
'vendors/routes/signals.min.js', // fixed
@ -82,7 +83,7 @@ config.paths.js = {
'vendors/keymaster/keymaster.js', // custom (modified)
'vendors/qr.js/qr.min.js', // fixed (license)
'vendors/bootstrap/js/bootstrap.min.js', // fixed
'node_modules/underscore/underscore-min.js',
'vendors/underscore/underscore-min.custom.js',
'node_modules/moment/min/moment.min.js',
'node_modules/knockout/build/output/knockout-latest.js',
'node_modules/knockout-sortable/build/knockout-sortable.min.js ',

View file

@ -0,0 +1,6 @@
// Underscore.js 1.9.2
// https://underscorejs.org
// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(()=>{var e=function(t){return t instanceof e?t:this instanceof e?void 0:new e(t)};"undefined"==typeof exports||exports.nodeType?("object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||this||{})._=e:("undefined"!=typeof module&&!module.nodeType&&module.exports&&(exports=module.exports=e),exports._=e);e.VERSION="1.9.2";var t=function(e,t,n,o,l){if(!(o instanceof t))return e.apply(n,l);var r,u="object"!=typeof(r=e.prototype)?{}:Object.create(r),i=e.apply(u,l);return"object"==typeof i?i:u};e.defer=(()=>{var e=(e,t,...n)=>setTimeout(()=>e.apply(null,n),t),n=function(...o){for(var l=0,r=[o[l++],1];l<o.length;)r.push(o[l++]);return t(e,n,this,this,r)};return n})(),e.throttle=function(e,t,n){var o,l,r,u,i=0;n||(n={});var a=function(){i=!1===n.leading?0:Date.now(),o=null,u=e.apply(l,r),o||(l=r=null)},f=function(){var f=Date.now();i||!1!==n.leading||(i=f);var p=t-(f-i);return l=this,r=arguments,p<=0||p>t?(o&&(clearTimeout(o),o=null),i=f,u=e.apply(l,r),o||(l=r=null)):o||!1===n.trailing||(o=setTimeout(a,p)),u};return f.cancel=function(){clearTimeout(o),i=0,o=l=r=null},f},e.debounce=function(e,t,n){var o,l,r=function(t,n){o=null,n&&(l=e.apply(t,n))},u=function(...u){if(o&&clearTimeout(o),n){var i=!o;o=setTimeout(r,t),i&&(l=e.apply(this,u))}else{var a=this;o=setTimeout(()=>r.apply(null,[a,u]),t)}return l};return u.cancel=function(){clearTimeout(o),o=null},u},"function"==typeof define&&define.amd&&define("underscore",[],function(){return e})})();

169
vendors/underscore/underscore.custom.js vendored Normal file
View file

@ -0,0 +1,169 @@
// Underscore.js 1.9.2
// https://underscorejs.org
// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
/*
_.debounce
_.defer
_.throttle
*/
(() => {
// Baseline setup
// --------------
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for their old module API. If we're in
// the browser, add `_` as a global object.
// (`nodeType` is checked to ensure that `module`
// and `exports` are not HTML elements.)
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this ||
{};
root._ = _;
}
// Current version.
_.VERSION = '1.9.2';
// An internal function for creating a new object that inherits from another.
var baseCreate = function(prototype) {
if (typeof prototype !== 'object') return {};
return Object.create(prototype);
};
// Function (ahem) Functions
// ------------------
// Determines whether to execute a function as a constructor
// or a normal function with the provided arguments.
var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
var self = baseCreate(sourceFunc.prototype);
var result = sourceFunc.apply(self, args);
return (typeof result === 'object') ? result : self;
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = (() => {
var func = (func, wait, ...args) => setTimeout(() => func.apply(null, args), wait);
var bound = function(...params) {
var position = 0;
var args = [params[position++], 1];
while (position < params.length) args.push(params[position++]);
return executeBound(func, bound, this, this, args);
};
return bound;
})();
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = function(...args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
var obj = this;
timeout = setTimeout(() => later.apply(null, [obj, args]), wait);
}
return result;
};
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define == 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
})();

View file

@ -6775,11 +6775,6 @@ unc-path-regex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
underscore@1.9.2:
version "1.9.2"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.2.tgz#0c8d6f536d6f378a5af264a72f7bec50feb7cf2f"
integrity sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==
undertaker-registry@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50"