mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Embed cleaner node modules
This commit is contained in:
parent
e7180a86ce
commit
aaf4933b0a
6 changed files with 383 additions and 20 deletions
16
README.md
16
README.md
|
|
@ -75,23 +75,23 @@ Things might work in Edge 15-18, Firefox 47-62 and Chrome 54-68 due to one polyf
|
||||||
|
|
||||||
|js/* |1.14.0 |native |gzip 1.14 |gzip |
|
|js/* |1.14.0 |native |gzip 1.14 |gzip |
|
||||||
|----------- |--------: |--------: |--------: |--------: |
|
|----------- |--------: |--------: |--------: |--------: |
|
||||||
|admin.js |2.130.942 |1.172.413 | 485.481 | 291.715 |
|
|admin.js |2.130.942 |1.133.275 | 485.481 | 274.632 |
|
||||||
|app.js |4.184.455 |2.888.668 | 932.725 | 682.438 |
|
|app.js |4.184.455 |2.844.189 | 932.725 | 664.075 |
|
||||||
|boot.js | 671.522 | 44.394 | 169.502 | 15.460 |
|
|boot.js | 671.522 | 44.029 | 169.502 | 15.460 |
|
||||||
|libs.js | 647.614 | 431.049 | 194.728 | 132.315 |
|
|libs.js | 647.614 | 431.049 | 194.728 | 132.315 |
|
||||||
|polyfills.js | 325.834 | 0 | 71.825 | 0 |
|
|polyfills.js | 325.834 | 0 | 71.825 | 0 |
|
||||||
|TOTAL js |7.960.367 |4.537.200 |1.854.261 |1.121.928 |
|
|TOTAL js |7.960.367 |4.452.542 |1.854.261 |1.086.482 |
|
||||||
|
|
||||||
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |
|
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |
|
||||||
|--------------- |--------: |--------: |--------: |--------: |
|
|--------------- |--------: |--------: |--------: |--------: |
|
||||||
|admin.min.js | 252.147 | 153.689 | 73.657 | 44.000 |
|
|admin.min.js | 252.147 | 153.095 | 73.657 | 43.734 |
|
||||||
|app.min.js | 511.202 | 380.294 |140.462 |100.236 |
|
|app.min.js | 511.202 | 378.695 |140.462 | 99.511 |
|
||||||
|boot.min.js | 66.007 | 5.589 | 22.567 | 2.333 |
|
|boot.min.js | 66.007 | 5.589 | 22.567 | 2.333 |
|
||||||
|libs.min.js | 572.545 | 387.636 |176.720 |122.519 |
|
|libs.min.js | 572.545 | 387.636 |176.720 |122.519 |
|
||||||
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 |
|
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 |
|
||||||
|TOTAL js/min |1.434.353 | 927.208 |424.718 |269.088 |
|
|TOTAL js/min |1.434.353 | 925.015 |424.718 |268.097 |
|
||||||
|
|
||||||
507.145 bytes (155.630 gzip) is not much, but it feels faster.
|
509.338 bytes (156.621 gzip) is not much, but it feels faster.
|
||||||
|
|
||||||
|
|
||||||
|css/* |1.14.0 |native |
|
|css/* |1.14.0 |native |
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,114 @@
|
||||||
import Cookies from 'js-cookie';
|
|
||||||
import { CLIENT_SIDE_STORAGE_INDEX_NAME } from 'Common/Consts';
|
import { CLIENT_SIDE_STORAGE_INDEX_NAME } from 'Common/Consts';
|
||||||
|
|
||||||
|
var Cookies = (() => {
|
||||||
|
function extend (...args) {
|
||||||
|
var result = {};
|
||||||
|
args.forEach(attributes => {
|
||||||
|
for (var key in attributes) {
|
||||||
|
result[key] = attributes[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode (s) {
|
||||||
|
return s.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set (key, value, attributes) {
|
||||||
|
if (typeof document === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes = extend({
|
||||||
|
path: '/'
|
||||||
|
}, attributes);
|
||||||
|
|
||||||
|
if (typeof attributes.expires === 'number') {
|
||||||
|
attributes.expires = new Date(new Date() * 1 + attributes.expires * 864e+5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're using "expires" because "max-age" is not supported by IE
|
||||||
|
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = JSON.stringify(value);
|
||||||
|
if (/^[{[]/.test(result)) {
|
||||||
|
value = result;
|
||||||
|
}
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
value = encodeURIComponent(String(value))
|
||||||
|
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
|
||||||
|
|
||||||
|
key = encodeURIComponent(String(key))
|
||||||
|
.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent)
|
||||||
|
.replace(/[()]/g, escape);
|
||||||
|
|
||||||
|
var stringifiedAttributes = '';
|
||||||
|
for (var attributeName in attributes) {
|
||||||
|
if (!attributes[attributeName]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stringifiedAttributes += '; ' + attributeName;
|
||||||
|
if (attributes[attributeName] === true) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Considers RFC 6265 section 5.2:
|
||||||
|
// ...
|
||||||
|
// 3. If the remaining unparsed-attributes contains a %x3B (";")
|
||||||
|
// character:
|
||||||
|
// Consume the characters of the unparsed-attributes up to,
|
||||||
|
// not including, the first %x3B (";") character.
|
||||||
|
// ...
|
||||||
|
stringifiedAttributes += '=' + attributes[attributeName].split(';')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (document.cookie = key + '=' + value + stringifiedAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get (key) {
|
||||||
|
if (typeof document === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jar = {};
|
||||||
|
// To prevent the for loop in the first place assign an empty array
|
||||||
|
// in case there are no cookies at all.
|
||||||
|
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (; i < cookies.length; i++) {
|
||||||
|
var parts = cookies[i].split('=');
|
||||||
|
var cookie = parts.slice(1).join('=');
|
||||||
|
|
||||||
|
try {
|
||||||
|
var name = decode(parts[0]);
|
||||||
|
cookie = decode(cookie);
|
||||||
|
|
||||||
|
try {
|
||||||
|
cookie = JSON.parse(cookie);
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
jar[name] = cookie;
|
||||||
|
|
||||||
|
if (key === name) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
}
|
||||||
|
|
||||||
|
return key ? jar[key] : jar;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
set: set,
|
||||||
|
getJSON: key => get(key)
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
class CookieDriver {
|
class CookieDriver {
|
||||||
/**
|
/**
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,267 @@
|
||||||
import addressparser from 'emailjs-addressparser';
|
|
||||||
import { encodeHtml, isNonEmptyArray } from 'Common/Utils';
|
import { encodeHtml, isNonEmptyArray } from 'Common/Utils';
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses structured e-mail addresses from an address field
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* "Name <address@domain>"
|
||||||
|
*
|
||||||
|
* will be converted to
|
||||||
|
*
|
||||||
|
* [{name: "Name", address: "address@domain"}]
|
||||||
|
*
|
||||||
|
* @param {String} str Address field
|
||||||
|
* @return {Array} An array of address objects
|
||||||
|
*/
|
||||||
|
function addressparser(str) {
|
||||||
|
var tokenizer = new Tokenizer(str);
|
||||||
|
var tokens = tokenizer.tokenize();
|
||||||
|
var addresses = [];
|
||||||
|
var address = [];
|
||||||
|
var parsedAddresses = [];
|
||||||
|
|
||||||
|
tokens.forEach(token => {
|
||||||
|
if (token.type === 'operator' && (token.value === ',' || token.value === ';')) {
|
||||||
|
if (address.length) {
|
||||||
|
addresses.push(address);
|
||||||
|
}
|
||||||
|
address = [];
|
||||||
|
} else {
|
||||||
|
address.push(token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (address.length) {
|
||||||
|
addresses.push(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses.forEach(address => {
|
||||||
|
address = _handleAddress(address);
|
||||||
|
if (address.length) {
|
||||||
|
parsedAddresses = parsedAddresses.concat(address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return parsedAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts tokens for a single address into an address object
|
||||||
|
*
|
||||||
|
* @param {Array} tokens Tokens object
|
||||||
|
* @return {Object} Address object
|
||||||
|
*/
|
||||||
|
function _handleAddress(tokens) {
|
||||||
|
var isGroup = false;
|
||||||
|
var state = 'text';
|
||||||
|
var address = void 0;
|
||||||
|
var addresses = [];
|
||||||
|
var data = {
|
||||||
|
address: [],
|
||||||
|
comment: [],
|
||||||
|
group: [],
|
||||||
|
text: []
|
||||||
|
|
||||||
|
// Filter out <addresses>, (comments) and regular text
|
||||||
|
};
|
||||||
|
for (var i = 0, len = tokens.length; i < len; i++) {
|
||||||
|
var token = tokens[i];
|
||||||
|
|
||||||
|
if (token.type === 'operator') {
|
||||||
|
switch (token.value) {
|
||||||
|
case '<':
|
||||||
|
state = 'address';
|
||||||
|
break;
|
||||||
|
case '(':
|
||||||
|
state = 'comment';
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
state = 'group';
|
||||||
|
isGroup = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = 'text';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (token.value) {
|
||||||
|
data[state].push(token.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no text but a comment, replace the two
|
||||||
|
if (!data.text.length && data.comment.length) {
|
||||||
|
data.text = data.comment;
|
||||||
|
data.comment = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGroup) {
|
||||||
|
// http://tools.ietf.org/html/rfc2822#appendix-A.1.3
|
||||||
|
data.text = data.text.join(' ');
|
||||||
|
addresses.push({
|
||||||
|
name: data.text || address && address.name,
|
||||||
|
group: data.group.length ? addressparser(data.group.join(',')) : []
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If no address was found, try to detect one from regular text
|
||||||
|
if (!data.address.length && data.text.length) {
|
||||||
|
for (var _i = data.text.length - 1; _i >= 0; _i--) {
|
||||||
|
if (data.text[_i].match(/^[^@\s]+@[^@\s]+$/)) {
|
||||||
|
data.address = data.text.splice(_i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _regexHandler = function _regexHandler(address) {
|
||||||
|
if (!data.address.length) {
|
||||||
|
data.address = [address.trim()];
|
||||||
|
return ' ';
|
||||||
|
}
|
||||||
|
return address;
|
||||||
|
};
|
||||||
|
|
||||||
|
// still no address
|
||||||
|
if (!data.address.length) {
|
||||||
|
for (var _i2 = data.text.length - 1; _i2 >= 0; _i2--) {
|
||||||
|
data.text[_i2] = data.text[_i2].replace(/\s*\b[^@\s]+@[^@\s]+\b\s*/, _regexHandler).trim();
|
||||||
|
if (data.address.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's still is no text but a comment exixts, replace the two
|
||||||
|
if (!data.text.length && data.comment.length) {
|
||||||
|
data.text = data.comment;
|
||||||
|
data.comment = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep only the first address occurence, push others to regular text
|
||||||
|
if (data.address.length > 1) {
|
||||||
|
data.text = data.text.concat(data.address.splice(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join values with spaces
|
||||||
|
data.text = data.text.join(' ');
|
||||||
|
data.address = data.address.join(' ');
|
||||||
|
|
||||||
|
if (!data.address && isGroup) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
address = {
|
||||||
|
address: data.address || data.text || '',
|
||||||
|
name: data.text || data.address || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (address.address === address.name) {
|
||||||
|
if ((address.address || '').match(/@/)) {
|
||||||
|
address.name = '';
|
||||||
|
} else {
|
||||||
|
address.address = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses.push(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Operator tokens and which tokens are expected to end the sequence
|
||||||
|
*/
|
||||||
|
var OPERATORS = {
|
||||||
|
'"': '"',
|
||||||
|
'(': ')',
|
||||||
|
'<': '>',
|
||||||
|
',': '',
|
||||||
|
// Groups are ended by semicolons
|
||||||
|
':': ';',
|
||||||
|
// Semicolons are not a legal delimiter per the RFC2822 grammar other
|
||||||
|
// than for terminating a group, but they are also not valid for any
|
||||||
|
// other use in this context. Given that some mail clients have
|
||||||
|
// historically allowed the semicolon as a delimiter equivalent to the
|
||||||
|
// comma in their UI, it makes sense to treat them the same as a comma
|
||||||
|
// when used outside of a group.
|
||||||
|
';': ''
|
||||||
|
};
|
||||||
|
|
||||||
|
class Tokenizer
|
||||||
|
{
|
||||||
|
constructor(str) {
|
||||||
|
this.str = (str || '').toString();
|
||||||
|
this.operatorCurrent = '';
|
||||||
|
this.operatorExpecting = '';
|
||||||
|
this.node = null;
|
||||||
|
this.escaped = false;
|
||||||
|
this.list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenize() {
|
||||||
|
var list = [], i = this.str.length;
|
||||||
|
while (i--) this.checkChar(this.str[i]);
|
||||||
|
|
||||||
|
this.list.forEach(node => {
|
||||||
|
node.value = (node.value || '').toString().trim();
|
||||||
|
if (node.value) {
|
||||||
|
list.push(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkChar(chr) {
|
||||||
|
if ((chr in OPERATORS || chr === '\\') && this.escaped) {
|
||||||
|
this.escaped = false;
|
||||||
|
} else if (this.operatorExpecting && chr === this.operatorExpecting) {
|
||||||
|
this.node = {
|
||||||
|
type: 'operator',
|
||||||
|
value: chr
|
||||||
|
};
|
||||||
|
this.list.push(this.node);
|
||||||
|
this.node = null;
|
||||||
|
this.operatorExpecting = '';
|
||||||
|
this.escaped = false;
|
||||||
|
return;
|
||||||
|
} else if (!this.operatorExpecting && chr in OPERATORS) {
|
||||||
|
this.node = {
|
||||||
|
type: 'operator',
|
||||||
|
value: chr
|
||||||
|
};
|
||||||
|
this.list.push(this.node);
|
||||||
|
this.node = null;
|
||||||
|
this.operatorExpecting = OPERATORS[chr];
|
||||||
|
this.escaped = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.escaped && chr === '\\') {
|
||||||
|
this.escaped = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.node) {
|
||||||
|
this.node = {
|
||||||
|
type: 'text',
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
this.list.push(this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.escaped && chr !== '\\') {
|
||||||
|
this.node.value += '\\';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.node.value += chr;
|
||||||
|
this.escaped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class EmailModel {
|
class EmailModel {
|
||||||
email = '';
|
email = '';
|
||||||
name = '';
|
name = '';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import ko from 'ko';
|
import ko from 'ko';
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
import { MessagePriority, SignedVerifyStatus } from 'Common/Enums';
|
import { MessagePriority, SignedVerifyStatus } from 'Common/Enums';
|
||||||
import { i18n } from 'Common/Translator';
|
import { i18n } from 'Common/Translator';
|
||||||
|
|
@ -456,7 +455,8 @@ class MessageModel extends AbstractModel {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
lineAsCss() {
|
lineAsCss() {
|
||||||
return classnames({
|
let classes = [];
|
||||||
|
Object.entries({
|
||||||
'deleted': this.deleted(),
|
'deleted': this.deleted(),
|
||||||
'deleted-mark': this.deletedMark(),
|
'deleted-mark': this.deletedMark(),
|
||||||
'selected': this.selected(),
|
'selected': this.selected(),
|
||||||
|
|
@ -473,7 +473,8 @@ class MessageModel extends AbstractModel {
|
||||||
// 'hasChildrenMessage': 1 < this.threadsLen(),
|
// 'hasChildrenMessage': 1 < this.threadsLen(),
|
||||||
'hasUnseenSubMessage': this.hasUnseenSubMessage(),
|
'hasUnseenSubMessage': this.hasUnseenSubMessage(),
|
||||||
'hasFlaggedSubMessage': this.hasFlaggedSubMessage()
|
'hasFlaggedSubMessage': this.hasFlaggedSubMessage()
|
||||||
});
|
}).forEach(([key, value]) => value && classes.push(key));
|
||||||
|
return classes.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,8 @@
|
||||||
"autolinker": "3.13.0",
|
"autolinker": "3.13.0",
|
||||||
"babel-eslint": "10.1.0",
|
"babel-eslint": "10.1.0",
|
||||||
"babel-loader": "8.0.6",
|
"babel-loader": "8.0.6",
|
||||||
"classnames": "2.2.6",
|
|
||||||
"copy-webpack-plugin": "5.1.1",
|
"copy-webpack-plugin": "5.1.1",
|
||||||
"css-loader": "3.4.2",
|
"css-loader": "3.4.2",
|
||||||
"emailjs-addressparser": "2.0.2",
|
|
||||||
"eslint": "6.8.0",
|
"eslint": "6.8.0",
|
||||||
"eslint-config-prettier": "6.10.0",
|
"eslint-config-prettier": "6.10.0",
|
||||||
"eslint-plugin-prettier": "3.1.2",
|
"eslint-plugin-prettier": "3.1.2",
|
||||||
|
|
@ -89,7 +87,6 @@
|
||||||
"gulp-zip": "5.0.1",
|
"gulp-zip": "5.0.1",
|
||||||
"ifvisible.js": "1.0.6",
|
"ifvisible.js": "1.0.6",
|
||||||
"jquery": "^3.5.1",
|
"jquery": "^3.5.1",
|
||||||
"js-cookie": "2.2.1",
|
|
||||||
"knockout": "3.4.2",
|
"knockout": "3.4.2",
|
||||||
"knockout-sortable": "1.2.0",
|
"knockout-sortable": "1.2.0",
|
||||||
"moment": "2.24.0",
|
"moment": "2.24.0",
|
||||||
|
|
|
||||||
|
|
@ -1856,10 +1856,6 @@ class-utils@^0.3.5:
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
static-extend "^0.1.1"
|
static-extend "^0.1.1"
|
||||||
|
|
||||||
classnames@2.2.6:
|
|
||||||
version "2.2.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
|
||||||
|
|
||||||
clean-css@4.2.3:
|
clean-css@4.2.3:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
|
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue