mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Replaced dev/Common/ClientStorageDriver/* with webstorage polyfill
Cleanup some other code
This commit is contained in:
parent
0e8bf13d5d
commit
b837013cfb
13 changed files with 201 additions and 594 deletions
23
README.md
23
README.md
|
|
@ -69,6 +69,7 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill
|
||||||
* Replaced momentToNode with proper HTML5 <time>
|
* Replaced momentToNode with proper HTML5 <time>
|
||||||
* Replaced resize listeners with ResizeObserver
|
* Replaced resize listeners with ResizeObserver
|
||||||
* Replaced bootstrap.js with native drop-in replacement
|
* Replaced bootstrap.js with native drop-in replacement
|
||||||
|
* Replaced dev/Common/ClientStorageDriver/* with Web Storage Objects polyfill
|
||||||
* Removed pikaday
|
* Removed pikaday
|
||||||
* Removed underscore
|
* Removed underscore
|
||||||
* Removed polyfills
|
* Removed polyfills
|
||||||
|
|
@ -84,23 +85,23 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill
|
||||||
|
|
||||||
|js/* |1.14.0 |native |
|
|js/* |1.14.0 |native |
|
||||||
|----------- |--------: |--------: |
|
|----------- |--------: |--------: |
|
||||||
|admin.js |2.130.942 | 961.538 |
|
|admin.js |2.130.942 | 958.580 |
|
||||||
|app.js |4.184.455 |2.624.797 |
|
|app.js |4.184.455 |2.591.395 |
|
||||||
|boot.js | 671.522 | 43.742 |
|
|boot.js | 671.522 | 37.334 |
|
||||||
|libs.js | 647.614 | 312.276 |
|
|libs.js | 647.614 | 313.217 |
|
||||||
|polyfills.js | 325.834 | 0 |
|
|polyfills.js | 325.834 | 0 |
|
||||||
|TOTAL |7.960.367 |3.942.353 |
|
|TOTAL |7.960.367 |3.900.526 |
|
||||||
|
|
||||||
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
|js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli |
|
||||||
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
|--------------- |--------: |--------: |--------: |--------: |--------: |
|
||||||
|admin.min.js | 252.147 | 130.359 | 73.657 | 37.825 | 32.456 |
|
|admin.min.js | 252.147 | 130.139 | 73.657 | 37.785 | 32.445 |
|
||||||
|app.min.js | 511.202 | 354.068 |140.462 | 93.278 | 74.852 |
|
|app.min.js | 511.202 | 351.142 |140.462 | 92.245 | 74.050 |
|
||||||
|boot.min.js | 66.007 | 5.534 | 22.567 | 2.319 | 1.988 |
|
|boot.min.js | 66.007 | 4.938 | 22.567 | 2.097 | 1.767 |
|
||||||
|libs.min.js | 572.545 | 295.771 |176.720 | 91.520 | 80.851 |
|
|libs.min.js | 572.545 | 296.365 |176.720 | 91.813 | 80.999 |
|
||||||
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 |
|
|polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 |
|
||||||
|TOTAL |1.434.353 | 785.732 |424.718 |224.942 |190.147 |
|
|TOTAL |1.434.353 | 782.584 |424.718 |223.940 |189.261 |
|
||||||
|
|
||||||
648.621 bytes (199.776 gzip) is not much, but it feels faster.
|
651.769 bytes (200.778 gzip) is not much, but it feels faster.
|
||||||
|
|
||||||
### CSS changes
|
### CSS changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
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 {
|
|
||||||
/**
|
|
||||||
* @param {string} key
|
|
||||||
* @param {*} data
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
set(key, data) {
|
|
||||||
let result = false,
|
|
||||||
storageResult = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
storageResult = Cookies.getJSON(CLIENT_SIDE_STORAGE_INDEX_NAME);
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
(storageResult || (storageResult = {}))[key] = data;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Cookies.set(CLIENT_SIDE_STORAGE_INDEX_NAME, storageResult, {
|
|
||||||
expires: 30
|
|
||||||
});
|
|
||||||
|
|
||||||
result = true;
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} key
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
get(key) {
|
|
||||||
let result = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const storageResult = Cookies.getJSON(CLIENT_SIDE_STORAGE_INDEX_NAME);
|
|
||||||
result = storageResult && null != storageResult[key] ? storageResult[key] : null;
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
static supported() {
|
|
||||||
return !!(navigator && navigator.cookieEnabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { CookieDriver, CookieDriver as default };
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
import { isStorageSupported } from 'Storage/RainLoop';
|
|
||||||
import { CLIENT_SIDE_STORAGE_INDEX_NAME } from 'Common/Consts';
|
|
||||||
|
|
||||||
class LocalStorageDriver {
|
|
||||||
s = null;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.s = window.localStorage || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} key
|
|
||||||
* @param {*} data
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
set(key, data) {
|
|
||||||
if (!this.s) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let storageResult = null;
|
|
||||||
try {
|
|
||||||
const storageValue = this.s.getItem(CLIENT_SIDE_STORAGE_INDEX_NAME) || null;
|
|
||||||
storageResult = null === storageValue ? null : JSON.parse(storageValue);
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
(storageResult || (storageResult = {}))[key] = data;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.s.setItem(CLIENT_SIDE_STORAGE_INDEX_NAME, JSON.stringify(storageResult));
|
|
||||||
return true;
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} key
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
|
||||||
get(key) {
|
|
||||||
if (!this.s) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const storageValue = this.s.getItem(CLIENT_SIDE_STORAGE_INDEX_NAME) || null,
|
|
||||||
storageResult = null === storageValue ? null : JSON.parse(storageValue);
|
|
||||||
|
|
||||||
return storageResult && null != storageResult[key] ? storageResult[key] : null;
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
static supported() {
|
|
||||||
return isStorageSupported('localStorage');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { LocalStorageDriver, LocalStorageDriver as default };
|
|
||||||
|
|
@ -90,7 +90,6 @@ class HtmlEditor {
|
||||||
onModeChange = null;
|
onModeChange = null;
|
||||||
|
|
||||||
element;
|
element;
|
||||||
$element;
|
|
||||||
|
|
||||||
resize;
|
resize;
|
||||||
|
|
||||||
|
|
@ -113,24 +112,18 @@ class HtmlEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
runOnBlur() {
|
runOnBlur() {
|
||||||
if (this.onBlur) {
|
this.onBlur && this.onBlur();
|
||||||
this.onBlur();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blurTrigger() {
|
blurTrigger() {
|
||||||
if (this.onBlur) {
|
if (this.onBlur) {
|
||||||
clearTimeout(this.blurTimer);
|
clearTimeout(this.blurTimer);
|
||||||
this.blurTimer = setTimeout(() => {
|
this.blurTimer = setTimeout(() => this.runOnBlur(), 200);
|
||||||
this.runOnBlur();
|
|
||||||
}, 200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focusTrigger() {
|
focusTrigger() {
|
||||||
if (this.onBlur) {
|
this.onBlur && clearTimeout(this.blurTimer);
|
||||||
clearTimeout(this.blurTimer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -175,9 +168,7 @@ class HtmlEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDirty() {
|
resetDirty() {
|
||||||
if (this.editor) {
|
this.editor && this.editor.resetDirty();
|
||||||
this.editor.resetDirty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -245,9 +236,7 @@ class HtmlEditor {
|
||||||
this.editor.setData(html);
|
this.editor.setData(html);
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
if (focus) {
|
focus && this.focus();
|
||||||
this.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,9 +261,7 @@ class HtmlEditor {
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focus) {
|
focus && this.focus();
|
||||||
this.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,28 +295,16 @@ class HtmlEditor {
|
||||||
|
|
||||||
this.editor = window.CKEDITOR.appendTo(this.element, config);
|
this.editor = window.CKEDITOR.appendTo(this.element, config);
|
||||||
|
|
||||||
this.editor.on('key', (event) => {
|
this.editor.on('key', event => !(event && event.data && EventKeyCode.Tab === event.data.keyCode));
|
||||||
if (event && event.data && EventKeyCode.Tab === event.data.keyCode) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
this.editor.on('blur', () => this.blurTrigger());
|
||||||
});
|
|
||||||
|
|
||||||
this.editor.on('blur', () => {
|
|
||||||
this.blurTrigger();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.editor.on('mode', () => {
|
this.editor.on('mode', () => {
|
||||||
this.blurTrigger();
|
this.blurTrigger();
|
||||||
if (this.onModeChange) {
|
this.onModeChange && this.onModeChange('plain' !== this.editor.mode);
|
||||||
this.onModeChange('plain' !== this.editor.mode);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.on('focus', () => {
|
this.editor.on('focus', () => this.focusTrigger());
|
||||||
this.focusTrigger();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (window.FileReader) {
|
if (window.FileReader) {
|
||||||
this.editor.on('drop', (event) => {
|
this.editor.on('drop', (event) => {
|
||||||
|
|
@ -366,9 +341,7 @@ class HtmlEditor {
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
|
|
||||||
if (this.onReady) {
|
this.onReady && this.onReady();
|
||||||
this.onReady();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -381,46 +354,36 @@ class HtmlEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
if (this.editor) {
|
|
||||||
try {
|
try {
|
||||||
this.editor.focus();
|
this.editor && this.editor.focus();
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
hasFocus() {
|
hasFocus() {
|
||||||
if (this.editor) {
|
|
||||||
try {
|
try {
|
||||||
return !!this.editor.focusManager.hasFocus;
|
return this.editor && !!this.editor.focusManager.hasFocus;
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
blur() {
|
blur() {
|
||||||
if (this.editor) {
|
|
||||||
try {
|
try {
|
||||||
this.editor.focusManager.blur(true);
|
this.editor && this.editor.focusManager.blur(true);
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
resizeEditor() {
|
resizeEditor() {
|
||||||
if (this.editor && this.__resizable) {
|
|
||||||
try {
|
try {
|
||||||
this.editor.resize(this.element.clientWidth, this.element.clientHeight);
|
this.editor && this.__resizable && this.editor.resize(this.element.clientWidth, this.element.clientHeight);
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setReadOnly(value) {
|
setReadOnly(value) {
|
||||||
if (this.editor) {
|
|
||||||
try {
|
try {
|
||||||
this.editor.setReadOnly(!!value);
|
this.editor && this.editor.setReadOnly(!!value);
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
clear(focus) {
|
clear(focus) {
|
||||||
this.setHtml('', focus);
|
this.setHtml('', focus);
|
||||||
|
|
|
||||||
|
|
@ -305,7 +305,7 @@ export function openPgpWorkerPath() {
|
||||||
export function themePreviewLink(theme) {
|
export function themePreviewLink(theme) {
|
||||||
let prefix = VERSION_PREFIX;
|
let prefix = VERSION_PREFIX;
|
||||||
if ('@custom' === theme.substr(-7)) {
|
if ('@custom' === theme.substr(-7)) {
|
||||||
theme = theme.substring(0, theme.length - 7).trim();
|
theme = theme.substr(0, theme.length - 7).trim();
|
||||||
prefix = WEB_PREFIX;
|
prefix = WEB_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ class Selector {
|
||||||
focusedItem;
|
focusedItem;
|
||||||
selectedItem;
|
selectedItem;
|
||||||
|
|
||||||
itemSelectedThrottle;
|
|
||||||
|
|
||||||
selectedItemUseCallback = true;
|
selectedItemUseCallback = true;
|
||||||
|
|
||||||
iSelectNextHelper = 0;
|
iSelectNextHelper = 0;
|
||||||
|
|
@ -51,7 +49,7 @@ class Selector {
|
||||||
this.focusedItem = koFocusedItem || ko.observable(null);
|
this.focusedItem = koFocusedItem || ko.observable(null);
|
||||||
this.selectedItem = koSelectedItem || ko.observable(null);
|
this.selectedItem = koSelectedItem || ko.observable(null);
|
||||||
|
|
||||||
this.itemSelectedThrottle = (item=>this.itemSelected(item)).debounce(300);
|
const itemSelectedThrottle = (item => this.itemSelected(item)).debounce(300);
|
||||||
|
|
||||||
this.listChecked.subscribe((items) => {
|
this.listChecked.subscribe((items) => {
|
||||||
if (items.length) {
|
if (items.length) {
|
||||||
|
|
@ -70,13 +68,11 @@ class Selector {
|
||||||
this.selectedItem.subscribe((item) => {
|
this.selectedItem.subscribe((item) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
if (this.isListChecked()) {
|
if (this.isListChecked()) {
|
||||||
this.listChecked().forEach(subItem => {
|
this.listChecked().forEach(subItem => subItem.checked(false));
|
||||||
subItem.checked(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selectedItemUseCallback) {
|
if (this.selectedItemUseCallback) {
|
||||||
this.itemSelectedThrottle(item);
|
itemSelectedThrottle(item);
|
||||||
}
|
}
|
||||||
} else if (this.selectedItemUseCallback) {
|
} else if (this.selectedItemUseCallback) {
|
||||||
this.itemSelected(null);
|
this.itemSelected(null);
|
||||||
|
|
@ -91,11 +87,7 @@ class Selector {
|
||||||
this.sItemCheckedSelector = sItemCheckedSelector;
|
this.sItemCheckedSelector = sItemCheckedSelector;
|
||||||
this.sItemFocusedSelector = sItemFocusedSelector;
|
this.sItemFocusedSelector = sItemFocusedSelector;
|
||||||
|
|
||||||
this.focusedItem.subscribe((item) => {
|
this.focusedItem.subscribe(item => item && (this.sLastUid = this.getItemUid(item)), this);
|
||||||
if (item) {
|
|
||||||
this.sLastUid = this.getItemUid(item);
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
let aCache = [],
|
let aCache = [],
|
||||||
aCheckedCache = [],
|
aCheckedCache = [],
|
||||||
|
|
@ -110,9 +102,7 @@ class Selector {
|
||||||
const uid = this.getItemUid(item);
|
const uid = this.getItemUid(item);
|
||||||
|
|
||||||
aCache.push(uid);
|
aCache.push(uid);
|
||||||
if (item.checked()) {
|
item.checked() && aCheckedCache.push(uid);
|
||||||
aCheckedCache.push(uid);
|
|
||||||
}
|
|
||||||
if (null === mFocused && item.focused()) {
|
if (null === mFocused && item.focused()) {
|
||||||
mFocused = uid;
|
mFocused = uid;
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +168,8 @@ class Selector {
|
||||||
isNextFocused = aCache.find(sUid => {
|
isNextFocused = aCache.find(sUid => {
|
||||||
if (getNext && uids.includes(sUid)) {
|
if (getNext && uids.includes(sUid)) {
|
||||||
return sUid;
|
return sUid;
|
||||||
} else if (isNextFocused === sUid) {
|
}
|
||||||
|
if (isNextFocused === sUid) {
|
||||||
getNext = true;
|
getNext = true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -346,13 +337,6 @@ class Selector {
|
||||||
return !!(this.oCallbacks.onAutoSelect || (()=>true))();
|
return !!(this.oCallbacks.onAutoSelect || (()=>true))();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} up
|
|
||||||
*/
|
|
||||||
doUpUpOrDownDown(up) {
|
|
||||||
(this.oCallbacks.onUpUpOrDownDown || (()=>true))(!!up);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} oItem
|
* @param {Object} oItem
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
|
@ -433,7 +417,7 @@ class Selector {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result && (EventKeyCode.Down === iEventKeyCode || EventKeyCode.Up === iEventKeyCode)) {
|
if (!result && (EventKeyCode.Down === iEventKeyCode || EventKeyCode.Up === iEventKeyCode)) {
|
||||||
this.doUpUpOrDownDown(EventKeyCode.Up === iEventKeyCode);
|
(this.oCallbacks.onUpUpOrDownDown || (()=>true))(EventKeyCode.Up === iEventKeyCode);
|
||||||
}
|
}
|
||||||
} else if (EventKeyCode.Home === iEventKeyCode || EventKeyCode.End === iEventKeyCode) {
|
} else if (EventKeyCode.Home === iEventKeyCode || EventKeyCode.End === iEventKeyCode) {
|
||||||
if (EventKeyCode.Home === iEventKeyCode) {
|
if (EventKeyCode.Home === iEventKeyCode) {
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ export function splitPlainText(text, len = 100) {
|
||||||
newLinePos = 0;
|
newLinePos = 0;
|
||||||
|
|
||||||
while (result.length > len) {
|
while (result.length > len) {
|
||||||
subText = result.substring(0, len);
|
subText = result.substr(0, len);
|
||||||
spacePos = subText.lastIndexOf(' ');
|
spacePos = subText.lastIndexOf(' ');
|
||||||
newLinePos = subText.lastIndexOf('\n');
|
newLinePos = subText.lastIndexOf('\n');
|
||||||
|
|
||||||
|
|
@ -114,8 +114,8 @@ export function splitPlainText(text, len = 100) {
|
||||||
spacePos = len;
|
spacePos = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix += subText.substring(0, spacePos) + '\n';
|
prefix += subText.substr(0, spacePos) + '\n';
|
||||||
result = result.substring(spacePos + 1);
|
result = result.substr(spacePos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefix + result;
|
return prefix + result;
|
||||||
|
|
@ -214,33 +214,14 @@ export function replySubjectAdd(prefix, subject) {
|
||||||
return ((prefixIsRe ? 'Re: ' : 'Fwd: ') + (re ? 'Re: ' : '') + (fwd ? 'Fwd: ' : '') + parts.join(':').trim()).trim();
|
return ((prefixIsRe ? 'Re: ' : 'Fwd: ') + (re ? 'Re: ' : '') + (fwd ? 'Fwd: ' : '') + parts.join(':').trim()).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} num
|
|
||||||
* @param {number} dec
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
export function roundNumber(num, dec) {
|
|
||||||
return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {(number|string)} sizeInBytes
|
* @param {(number|string)} sizeInBytes
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function friendlySize(sizeInBytes) {
|
export function friendlySize(sizeInBytes) {
|
||||||
sizeInBytes = pInt(sizeInBytes);
|
sizeInBytes = pInt(sizeInBytes);
|
||||||
|
const sizes = ['B', 'KiB', 'MiB', 'GiB', 'TiB'], i = pInt(Math.floor(Math.log(sizeInBytes) / Math.log(1024)));
|
||||||
switch (true) {
|
return (sizeInBytes / Math.pow(1024, i)).toFixed(2>i ? 0 : 1) + sizes[i];
|
||||||
case 1073741824 <= sizeInBytes:
|
|
||||||
return roundNumber(sizeInBytes / 1073741824, 1) + 'GB';
|
|
||||||
case 1048576 <= sizeInBytes:
|
|
||||||
return roundNumber(sizeInBytes / 1048576, 1) + 'MB';
|
|
||||||
case 1024 <= sizeInBytes:
|
|
||||||
return roundNumber(sizeInBytes / 1024, 0) + 'KB';
|
|
||||||
// no default
|
|
||||||
}
|
|
||||||
|
|
||||||
return sizeInBytes + 'B';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -249,7 +230,7 @@ export function friendlySize(sizeInBytes) {
|
||||||
*/
|
*/
|
||||||
export const convertThemeName = theme => {
|
export const convertThemeName = theme => {
|
||||||
if ('@custom' === theme.substr(-7)) {
|
if ('@custom' === theme.substr(-7)) {
|
||||||
theme = theme.substring(0, theme.length - 7).trim();
|
theme = theme.substr(0, theme.length - 7).trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
return theme
|
return theme
|
||||||
|
|
@ -452,7 +433,7 @@ export function htmlToPlain(html) {
|
||||||
iP3 = text.indexOf('__bq__end__', iP1 + 5);
|
iP3 = text.indexOf('__bq__end__', iP1 + 5);
|
||||||
|
|
||||||
if ((-1 === iP2 || iP3 < iP2) && iP1 < iP3) {
|
if ((-1 === iP2 || iP3 < iP2) && iP1 < iP3) {
|
||||||
text = text.substring(0, iP1) + convertBlockquote(text.substring(iP1 + 13, iP3)) + text.substring(iP3 + 11);
|
text = text.substr(0, iP1) + convertBlockquote(text.substring(iP1 + 13, iP3)) + text.substr(iP3 + 11);
|
||||||
pos = 0;
|
pos = 0;
|
||||||
} else if (-1 < iP2 && iP2 < iP3) {
|
} else if (-1 < iP2 && iP2 < iP3) {
|
||||||
pos = iP2 - 1;
|
pos = iP2 - 1;
|
||||||
|
|
@ -722,7 +703,7 @@ export function changeTheme(value, themeTrigger = ()=>{}) {
|
||||||
.replace(/\/Css\/[^/]+\/User\//, '/Css/0/User/')
|
.replace(/\/Css\/[^/]+\/User\//, '/Css/0/User/')
|
||||||
.replace(/\/Hash\/[^/]+\//, '/Hash/-/');
|
.replace(/\/Hash\/[^/]+\//, '/Hash/-/');
|
||||||
|
|
||||||
if ('Json/' !== url.substring(url.length - 5, url.length)) {
|
if ('Json/' !== url.substr(-5)) {
|
||||||
url += 'Json/';
|
url += 'Json/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,14 @@ export function hideLoading() {
|
||||||
*/
|
*/
|
||||||
export function createCommand(fExecute, fCanExecute = true) {
|
export function createCommand(fExecute, fCanExecute = true) {
|
||||||
let fResult = null;
|
let fResult = null;
|
||||||
const fNonEmpty = (...args) => {
|
|
||||||
|
fResult = fExecute
|
||||||
|
? (...args) => {
|
||||||
if (fResult && fResult.canExecute && fResult.canExecute()) {
|
if (fResult && fResult.canExecute && fResult.canExecute()) {
|
||||||
fExecute.apply(null, args);
|
fExecute.apply(null, args);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
} : ()=>{};
|
||||||
|
|
||||||
fResult = fExecute ? fNonEmpty : ()=>{};
|
|
||||||
fResult.enabled = ko.observable(true);
|
fResult.enabled = ko.observable(true);
|
||||||
fResult.isCommand = true;
|
fResult.isCommand = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import { CookieDriver } from 'Common/ClientStorageDriver/Cookie';
|
import { CLIENT_SIDE_STORAGE_INDEX_NAME } from 'Common/Consts';
|
||||||
import { LocalStorageDriver } from 'Common/ClientStorageDriver/LocalStorage';
|
|
||||||
|
|
||||||
const SupportedStorageDriver = [LocalStorageDriver, CookieDriver].find(
|
const storage = window.localStorage;
|
||||||
StorageDriver => StorageDriver && StorageDriver.supported()
|
|
||||||
);
|
|
||||||
|
|
||||||
const driver = SupportedStorageDriver ? new SupportedStorageDriver() : null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} key
|
* @param {number} key
|
||||||
|
|
@ -13,7 +8,20 @@ const driver = SupportedStorageDriver ? new SupportedStorageDriver() : null;
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export function set(key, data) {
|
export function set(key, data) {
|
||||||
return driver ? driver.set('p' + key, data) : false;
|
let storageResult = null;
|
||||||
|
try {
|
||||||
|
const storageValue = storage.getItem(CLIENT_SIDE_STORAGE_INDEX_NAME) || null;
|
||||||
|
storageResult = null === storageValue ? null : JSON.parse(storageValue);
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
(storageResult || (storageResult = {}))['p' + key] = data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
storage.setItem(CLIENT_SIDE_STORAGE_INDEX_NAME, JSON.stringify(storageResult));
|
||||||
|
return true;
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -21,5 +29,13 @@ export function set(key, data) {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export function get(key) {
|
export function get(key) {
|
||||||
return driver ? driver.get('p' + key) : null;
|
try {
|
||||||
|
key = 'p' + key;
|
||||||
|
const storageValue = storage.getItem(CLIENT_SIDE_STORAGE_INDEX_NAME) || null,
|
||||||
|
storageResult = null === storageValue ? null : JSON.parse(storageValue);
|
||||||
|
|
||||||
|
return storageResult && null != storageResult[key] ? storageResult[key] : null;
|
||||||
|
} catch (e) {} // eslint-disable-line no-empty
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,17 @@
|
||||||
const STORAGE_KEY = '__rlA';
|
const STORAGE_KEY = '__rlA';
|
||||||
const TIME_KEY = '__rlT';
|
const TIME_KEY = '__rlT';
|
||||||
|
|
||||||
/**
|
const storage = ()=>window.sessionStorage;
|
||||||
* @param {string} storageName
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export function isStorageSupported(storageName) {
|
|
||||||
let storageIsAvailable = false;
|
|
||||||
try {
|
|
||||||
// at: window[storageName] firefox throws SecurityError: The operation is insecure. when in iframe
|
|
||||||
storageIsAvailable = storageName in window && window[storageName] && window[storageName].setItem;
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
|
|
||||||
if (storageIsAvailable) {
|
|
||||||
const s = window[storageName],
|
|
||||||
key = 'testLocalStorage_' + Math.random();
|
|
||||||
|
|
||||||
try {
|
|
||||||
s.setItem(key, key);
|
|
||||||
if (key === s.getItem(key)) {
|
|
||||||
s.removeItem(key);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (e) {} // eslint-disable-line no-empty
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SESS_STORAGE = isStorageSupported('sessionStorage') ? window.sessionStorage || null : null;
|
|
||||||
const WIN_STORAGE = window.top || window || null;
|
|
||||||
|
|
||||||
const __get = (key) => {
|
|
||||||
let result = null;
|
|
||||||
if (SESS_STORAGE) {
|
|
||||||
result = SESS_STORAGE.getItem(key) || null;
|
|
||||||
} else if (WIN_STORAGE) {
|
|
||||||
const data =
|
|
||||||
WIN_STORAGE.name && '{' === WIN_STORAGE.name.toString()[0]
|
|
||||||
? JSON.parse(WIN_STORAGE.name.toString())
|
|
||||||
: null;
|
|
||||||
result = data ? data[key] || null : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const __set = (key, value) => {
|
|
||||||
if (SESS_STORAGE) {
|
|
||||||
SESS_STORAGE.setItem(key, value);
|
|
||||||
} else if (WIN_STORAGE) {
|
|
||||||
let data =
|
|
||||||
WIN_STORAGE.name && '{' === WIN_STORAGE.name.toString()[0]
|
|
||||||
? JSON.parse(WIN_STORAGE.name.toString())
|
|
||||||
: null;
|
|
||||||
data = data || {};
|
|
||||||
data[key] = value;
|
|
||||||
|
|
||||||
WIN_STORAGE.name = JSON.stringify(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const timestamp = () => Math.round(Date.now() / 1000);
|
const timestamp = () => Math.round(Date.now() / 1000);
|
||||||
|
|
||||||
const setTimestamp = () => __set(TIME_KEY, timestamp());
|
const setTimestamp = () => storage().setItem(TIME_KEY, timestamp());
|
||||||
|
|
||||||
const getTimestamp = () => {
|
|
||||||
const time = __get(TIME_KEY, 0);
|
|
||||||
return time ? parseInt(time, 10) || 0 : 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
export function getHash() {
|
export function getHash() {
|
||||||
return __get(STORAGE_KEY);
|
return storage().getItem(STORAGE_KEY) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,7 +21,7 @@ export function setHash() {
|
||||||
const key = 'AuthAccountHash',
|
const key = 'AuthAccountHash',
|
||||||
appData = window.__rlah_data();
|
appData = window.__rlah_data();
|
||||||
|
|
||||||
__set(STORAGE_KEY, appData && appData[key] ? appData[key] : '');
|
storage().setItem(STORAGE_KEY, appData && appData[key] ? appData[key] : '');
|
||||||
setTimestamp();
|
setTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +29,7 @@ export function setHash() {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export function clearHash() {
|
export function clearHash() {
|
||||||
__set(STORAGE_KEY, '');
|
storage().setItem(STORAGE_KEY, '');
|
||||||
setTimestamp();
|
setTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +37,7 @@ export function clearHash() {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export function checkTimestamp() {
|
export function checkTimestamp() {
|
||||||
if (timestamp() > getTimestamp() + 3600000) {
|
if (timestamp() > (parseInt(storage().getItem(TIME_KEY) || 0, 10) || 0) + 3600000) {
|
||||||
// 60m
|
// 60m
|
||||||
clearHash();
|
clearHash();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
this.messageListCompleteLoadingThrottle = MessageStore.messageListCompleteLoadingThrottle;
|
this.messageListCompleteLoadingThrottle = MessageStore.messageListCompleteLoadingThrottle;
|
||||||
this.messageListCompleteLoadingThrottleForAnimation = MessageStore.messageListCompleteLoadingThrottleForAnimation;
|
this.messageListCompleteLoadingThrottleForAnimation = MessageStore.messageListCompleteLoadingThrottleForAnimation;
|
||||||
|
|
||||||
initOnStartOrLangChange(() => {
|
initOnStartOrLangChange(() => this.emptySubjectValue = i18n('MESSAGE_LIST/EMPTY_SUBJECT_TEXT'));
|
||||||
this.emptySubjectValue = i18n('MESSAGE_LIST/EMPTY_SUBJECT_TEXT');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.userQuota = QuotaStore.quota;
|
this.userQuota = QuotaStore.quota;
|
||||||
this.userUsageSize = QuotaStore.usage;
|
this.userUsageSize = QuotaStore.usage;
|
||||||
|
|
@ -140,9 +138,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
read: () => 0 < MessageStore.messageListChecked().length,
|
read: () => 0 < MessageStore.messageListChecked().length,
|
||||||
write: (value) => {
|
write: (value) => {
|
||||||
value = !!value;
|
value = !!value;
|
||||||
MessageStore.messageList().forEach(message => {
|
MessageStore.messageList().forEach(message => message.checked(value));
|
||||||
message.checked(value);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -151,9 +147,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
this.sLastSearchValue = '';
|
this.sLastSearchValue = '';
|
||||||
this.inputProxyMessageListSearch = ko.computed({
|
this.inputProxyMessageListSearch = ko.computed({
|
||||||
read: this.mainMessageListSearch,
|
read: this.mainMessageListSearch,
|
||||||
write: (value) => {
|
write: value => this.sLastSearchValue = value
|
||||||
this.sLastSearchValue = value;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.isIncompleteChecked = ko.computed(() => {
|
this.isIncompleteChecked = ko.computed(() => {
|
||||||
|
|
@ -205,9 +199,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
return this.mobile ? 0 < this.messageListChecked().length : true;
|
return this.mobile ? 0 < this.messageListChecked().length : true;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mobileCheckedStateHide = ko.computed(() => {
|
this.mobileCheckedStateHide = ko.computed(() => this.mobile ? !this.messageListChecked().length : true);
|
||||||
return this.mobile ? !this.messageListChecked().length : true;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.messageListFocused = ko.computed(() => Focused.MessageList === AppStore.focusedState());
|
this.messageListFocused = ko.computed(() => Focused.MessageList === AppStore.focusedState());
|
||||||
|
|
||||||
|
|
@ -225,25 +217,17 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
'.messageListItem.focused'
|
'.messageListItem.focused'
|
||||||
);
|
);
|
||||||
|
|
||||||
this.selector.on('onItemSelect', (message) => {
|
this.selector.on('onItemSelect', message => MessageStore.selectMessage(message));
|
||||||
MessageStore.selectMessage(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.selector.on('onItemGetUid', (message) => (message ? message.generateUid() : ''));
|
this.selector.on('onItemGetUid', message => (message ? message.generateUid() : ''));
|
||||||
|
|
||||||
this.selector.on('onAutoSelect', () => this.useAutoSelect());
|
this.selector.on('onAutoSelect', () => this.useAutoSelect());
|
||||||
|
|
||||||
this.selector.on('onUpUpOrDownDown', (v) => {
|
this.selector.on('onUpUpOrDownDown', v => this.goToUpUpOrDownDown(v));
|
||||||
this.goToUpUpOrDownDown(v);
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('mailbox.message-list.selector.go-down', e => {
|
addEventListener('mailbox.message-list.selector.go-down', e => this.selector.goDown(e.detail));
|
||||||
this.selector.goDown(e.detail);
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('mailbox.message-list.selector.go-up', e => {
|
addEventListener('mailbox.message-list.selector.go-up', e => this.selector.goUp(e.detail));
|
||||||
this.selector.goUp(e.detail);
|
|
||||||
});
|
|
||||||
|
|
||||||
addEventListener('mailbox.message.show', e => {
|
addEventListener('mailbox.message.show', e => {
|
||||||
const sFolder = e.detail.Folder, sUid = e.detail.Uid;
|
const sFolder = e.detail.Folder, sUid = e.detail.Uid;
|
||||||
|
|
@ -708,8 +692,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
gotoPage(page) {
|
gotoPage(page) {
|
||||||
if (page) {
|
page && setHash(
|
||||||
setHash(
|
|
||||||
mailBox(
|
mailBox(
|
||||||
FolderStore.currentFolderFullNameHash(),
|
FolderStore.currentFolderFullNameHash(),
|
||||||
page.value,
|
page.value,
|
||||||
|
|
@ -718,7 +701,6 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
gotoThread(message) {
|
gotoThread(message) {
|
||||||
if (message && 0 < message.threadsLen()) {
|
if (message && 0 < message.threadsLen()) {
|
||||||
|
|
@ -938,9 +920,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
const next = !!(StorageResultType.Success === result && data && data.Result);
|
const next = !!(StorageResultType.Success === result && data && data.Result);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.bPrefetch = false;
|
this.bPrefetch = false;
|
||||||
if (next) {
|
next && this.prefetchNextTick();
|
||||||
this.prefetchNextTick();
|
|
||||||
}
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
message.folderFullNameRaw,
|
message.folderFullNameRaw,
|
||||||
|
|
@ -951,9 +931,8 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
advancedSearchClick() {
|
advancedSearchClick() {
|
||||||
if (Settings.capa(Capa.SearchAdv)) {
|
Settings.capa(Capa.SearchAdv)
|
||||||
showScreenPopup(require('View/Popup/AdvancedSearch'), [this.mainMessageListSearch()]);
|
&& showScreenPopup(require('View/Popup/AdvancedSearch'), [this.mainMessageListSearch()]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quotaTooltip() {
|
quotaTooltip() {
|
||||||
|
|
@ -981,25 +960,13 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
dragAndDropBodyElement: this.dragOverBodyArea()
|
dragAndDropBodyElement: this.dragOverBodyArea()
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dragOver.subscribe((value) => {
|
this.dragOver.subscribe(value => value && this.selector.scrollToTop());
|
||||||
if (value) {
|
|
||||||
this.selector.scrollToTop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
oJua
|
oJua
|
||||||
.on('onDragEnter', () => {
|
.on('onDragEnter', () => this.dragOverEnter(true))
|
||||||
this.dragOverEnter(true);
|
.on('onDragLeave', () => this.dragOverEnter(false))
|
||||||
})
|
.on('onBodyDragEnter', () => this.dragOver(true))
|
||||||
.on('onDragLeave', () => {
|
.on('onBodyDragLeave', () => this.dragOver(false))
|
||||||
this.dragOverEnter(false);
|
|
||||||
})
|
|
||||||
.on('onBodyDragEnter', () => {
|
|
||||||
this.dragOver(true);
|
|
||||||
})
|
|
||||||
.on('onBodyDragLeave', () => {
|
|
||||||
this.dragOver(false);
|
|
||||||
})
|
|
||||||
.on('onSelect', (sUid, oData) => {
|
.on('onSelect', (sUid, oData) => {
|
||||||
if (sUid && oData && 'message/rfc822' === oData.Type) {
|
if (sUid && oData && 'message/rfc822' === oData.Type) {
|
||||||
MessageStore.messageListLoading(true);
|
MessageStore.messageListLoading(true);
|
||||||
|
|
@ -1008,9 +975,7 @@ class MessageListMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.on('onComplete', () => {
|
.on('onComplete', () => getApp().reloadMessageList(true, true));
|
||||||
getApp().reloadMessageList(true, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
return !!oJua;
|
return !!oJua;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
this.showAttachmnetControls = ko.observable(false);
|
this.showAttachmnetControls = ko.observable(false);
|
||||||
|
|
||||||
this.showAttachmnetControlsState = (v) => {
|
this.showAttachmnetControlsState = v => Local.set(ClientSideKeyName.MessageAttachmnetControls, !!v);
|
||||||
Local.set(ClientSideKeyName.MessageAttachmnetControls, !!v);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.allowAttachmnetControls = ko.computed(
|
this.allowAttachmnetControls = ko.computed(
|
||||||
() => this.attachmentsActions().length && Settings.capa(Capa.AttachmentsActions)
|
() => this.attachmentsActions().length && Settings.capa(Capa.AttachmentsActions)
|
||||||
|
|
@ -130,33 +128,23 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
this.downloadAsZipLoading = ko.observable(false);
|
this.downloadAsZipLoading = ko.observable(false);
|
||||||
this.downloadAsZipError = ko.observable(false).extend({ falseTimeout: 7000 });
|
this.downloadAsZipError = ko.observable(false).extend({ falseTimeout: 7000 });
|
||||||
|
|
||||||
this.showAttachmnetControls.subscribe((v) => {
|
this.showAttachmnetControls.subscribe(v => this.message()
|
||||||
if (this.message()) {
|
&& this.message().attachments().forEach(item => item && item.checked(!!v))
|
||||||
this.message().attachments().forEach(item => {
|
);
|
||||||
if (item) {
|
|
||||||
item.checked(!!v);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.lastReplyAction_ = ko.observable('');
|
this.lastReplyAction_ = ko.observable('');
|
||||||
this.lastReplyAction = ko.computed({
|
this.lastReplyAction = ko.computed({
|
||||||
read: this.lastReplyAction_,
|
read: this.lastReplyAction_,
|
||||||
write: (value) => {
|
write: value => this.lastReplyAction_(
|
||||||
this.lastReplyAction_(
|
|
||||||
[ComposeType.Reply, ComposeType.ReplyAll, ComposeType.Forward].includes(value)
|
[ComposeType.Reply, ComposeType.ReplyAll, ComposeType.Forward].includes(value)
|
||||||
? ComposeType.Reply
|
? ComposeType.Reply
|
||||||
: value
|
: value
|
||||||
);
|
)
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lastReplyAction(Local.get(ClientSideKeyName.LastReplyAction) || ComposeType.Reply);
|
this.lastReplyAction(Local.get(ClientSideKeyName.LastReplyAction) || ComposeType.Reply);
|
||||||
|
|
||||||
this.lastReplyAction_.subscribe((value) => {
|
this.lastReplyAction_.subscribe(value => Local.set(ClientSideKeyName.LastReplyAction, value));
|
||||||
Local.set(ClientSideKeyName.LastReplyAction, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.showFullInfo = ko.observable('1' === Local.get(ClientSideKeyName.MessageHeaderFullInfo));
|
this.showFullInfo = ko.observable('1' === Local.get(ClientSideKeyName.MessageHeaderFullInfo));
|
||||||
|
|
||||||
|
|
@ -165,16 +153,9 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
this.messageVisibility = ko.computed(() => !this.messageLoadingThrottle() && !!this.message());
|
this.messageVisibility = ko.computed(() => !this.messageLoadingThrottle() && !!this.message());
|
||||||
|
|
||||||
this.message.subscribe((message) => {
|
this.message.subscribe(message => (!message) && MessageStore.selectorMessageSelected(null));
|
||||||
if (!message) {
|
|
||||||
MessageStore.selectorMessageSelected(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canBeRepliedOrForwarded = ko.computed(() => {
|
this.canBeRepliedOrForwarded = ko.computed(() => !this.isDraftFolder() && this.messageVisibility());
|
||||||
const v = this.messageVisibility();
|
|
||||||
return !this.isDraftFolder() && v;
|
|
||||||
});
|
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
|
this.replyCommand = createCommandReplyHelper(ComposeType.Reply);
|
||||||
|
|
@ -242,9 +223,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
this.messageActiveDom.subscribe(dom => {
|
this.messageActiveDom.subscribe(dom => this.bodyBackgroundColor(this.detectDomBackgroundColor(dom)), this);
|
||||||
this.bodyBackgroundColor(this.detectDomBackgroundColor(dom));
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.message.subscribe((message) => {
|
this.message.subscribe((message) => {
|
||||||
this.messageActiveDom(null);
|
this.messageActiveDom(null);
|
||||||
|
|
@ -304,16 +283,10 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
|
|
||||||
this.message.viewTrigger.subscribe(() => {
|
this.message.viewTrigger.subscribe(() => {
|
||||||
const message = this.message();
|
const message = this.message();
|
||||||
if (message) {
|
message ? this.viewIsFlagged(message.flagged()) : this.viewIsFlagged(false);
|
||||||
this.viewIsFlagged(message.flagged());
|
|
||||||
} else {
|
|
||||||
this.viewIsFlagged(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.fullScreenMode.subscribe((value) => {
|
this.fullScreenMode.subscribe(value => $htmlCL.toggle('rl-message-fullscreen', value));
|
||||||
$htmlCL.toggle('rl-message-fullscreen', value);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.messageFocused = ko.computed(() => Focused.MessageView === AppStore.focusedState());
|
this.messageFocused = ko.computed(() => Focused.MessageView === AppStore.focusedState());
|
||||||
|
|
||||||
|
|
@ -321,9 +294,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
() => MessageStore.messageListCompleteLoadingThrottle() || MessageStore.messageLoadingThrottle()
|
() => MessageStore.messageListCompleteLoadingThrottle() || MessageStore.messageLoadingThrottle()
|
||||||
);
|
);
|
||||||
|
|
||||||
addEventListener('mailbox.message-view.toggle-full-screen', () => {
|
addEventListener('mailbox.message-view.toggle-full-screen', () => this.toggleFullScreen());
|
||||||
this.toggleFullScreen();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.attachmentPreview = this.attachmentPreview.bind(this);
|
this.attachmentPreview = this.attachmentPreview.bind(this);
|
||||||
}
|
}
|
||||||
|
|
@ -397,9 +368,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
replyOrforward(sType) {
|
replyOrforward(sType) {
|
||||||
if (Settings.capa(Capa.Composer)) {
|
Settings.capa(Capa.Composer) && showScreenPopup(require('View/Popup/Compose'), [sType, MessageStore.message()]);
|
||||||
showScreenPopup(require('View/Popup/Compose'), [sType, MessageStore.message()]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkHeaderHeight() {
|
checkHeaderHeight() {
|
||||||
|
|
@ -463,9 +432,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
removeInFocus(true);
|
removeInFocus(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
div.on('onCloseAfter.lg', () => {
|
div.on('onCloseAfter.lg', () => useKeyboardShortcuts(true));
|
||||||
useKeyboardShortcuts(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
div.lightGallery({
|
div.lightGallery({
|
||||||
dynamic: true,
|
dynamic: true,
|
||||||
|
|
@ -487,15 +454,9 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBuild(dom) {
|
onBuild(dom) {
|
||||||
this.fullScreenMode.subscribe((value) => {
|
this.fullScreenMode.subscribe(value => value && this.message() && AppStore.focusedState(Focused.MessageView));
|
||||||
if (value && this.message()) {
|
|
||||||
AppStore.focusedState(Focused.MessageView);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.showFullInfo.subscribe((value) => {
|
this.showFullInfo.subscribe(value => Local.set(ClientSideKeyName.MessageHeaderFullInfo, value ? '1' : '0'));
|
||||||
Local.set(ClientSideKeyName.MessageHeaderFullInfo, value ? '1' : '0');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.oHeaderDom = dom.querySelector('.messageItemHeader');
|
this.oHeaderDom = dom.querySelector('.messageItemHeader');
|
||||||
if (this.oHeaderDom) {
|
if (this.oHeaderDom) {
|
||||||
|
|
@ -556,26 +517,22 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
if (eqs(event, '.messageItemHeader .subjectParent .flagParent')) {
|
if (eqs(event, '.messageItemHeader .subjectParent .flagParent')) {
|
||||||
// eslint-disable-line prefer-arrow-callback
|
// eslint-disable-line prefer-arrow-callback
|
||||||
const message = this.message();
|
const message = this.message();
|
||||||
if (message) {
|
message && getApp().messageListAction(
|
||||||
getApp().messageListAction(
|
|
||||||
message.folderFullNameRaw,
|
message.folderFullNameRaw,
|
||||||
message.flagged() ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag,
|
message.flagged() ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag,
|
||||||
[message]
|
[message]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
el = eqs(event, '.thread-list .flagParent');
|
el = eqs(event, '.thread-list .flagParent');
|
||||||
if (el) {
|
if (el) {
|
||||||
// eslint-disable-line prefer-arrow-callback
|
// eslint-disable-line prefer-arrow-callback
|
||||||
const message = ko.dataFor(el); // eslint-disable-line no-invalid-this
|
const message = ko.dataFor(el); // eslint-disable-line no-invalid-this
|
||||||
if (message && message.folder && message.uid) {
|
message && message.folder && message.uid && getApp().messageListAction(
|
||||||
getApp().messageListAction(
|
|
||||||
message.folder,
|
message.folder,
|
||||||
message.flagged() ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag,
|
message.flagged() ? MessageSetAction.UnsetFlag : MessageSetAction.SetFlag,
|
||||||
[message]
|
[message]
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
this.threadsDropdownTrigger(true);
|
this.threadsDropdownTrigger(true);
|
||||||
|
|
||||||
|
|
@ -590,9 +547,7 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keyScopeReal.subscribe((value) => {
|
keyScopeReal.subscribe(value => this.messageDomFocused(KeyState.MessageView === value && !inFocus()));
|
||||||
this.messageDomFocused(KeyState.MessageView === value && !inFocus());
|
|
||||||
});
|
|
||||||
|
|
||||||
this.oMessageScrollerDom = dom.querySelector('.messageItem');
|
this.oMessageScrollerDom = dom.querySelector('.messageItem');
|
||||||
|
|
||||||
|
|
@ -604,13 +559,14 @@ class MessageViewMailBoxUserView extends AbstractViewNext {
|
||||||
*/
|
*/
|
||||||
escShortcuts() {
|
escShortcuts() {
|
||||||
if (this.viewModelVisibility() && this.message()) {
|
if (this.viewModelVisibility() && this.message()) {
|
||||||
|
const preview = Layout.NoPreview !== this.layout();
|
||||||
if (this.fullScreenMode()) {
|
if (this.fullScreenMode()) {
|
||||||
this.fullScreenMode(false);
|
this.fullScreenMode(false);
|
||||||
|
|
||||||
if (Layout.NoPreview !== this.layout()) {
|
if (preview) {
|
||||||
AppStore.focusedState(Focused.MessageList);
|
AppStore.focusedState(Focused.MessageList);
|
||||||
}
|
}
|
||||||
} else if (Layout.NoPreview === this.layout()) {
|
} else if (!preview) {
|
||||||
this.message(null);
|
this.message(null);
|
||||||
} else {
|
} else {
|
||||||
AppStore.focusedState(Focused.MessageList);
|
AppStore.focusedState(Focused.MessageList);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
(w=>{
|
||||||
|
|
||||||
Array.prototype.flat || Object.defineProperty(Array.prototype, 'flat', {
|
Array.prototype.flat || Object.defineProperty(Array.prototype, 'flat', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
|
@ -15,8 +16,7 @@ Array.prototype.flat || Object.defineProperty(Array.prototype, 'flat', {
|
||||||
writable: true
|
writable: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!window.ResizeObserver) {
|
w.ResizeObserver || (w.ResizeObserver = class {
|
||||||
window.ResizeObserver = class {
|
|
||||||
constructor(callback) {
|
constructor(callback) {
|
||||||
this.observer = new MutationObserver(callback.debounce(250));
|
this.observer = new MutationObserver(callback.debounce(250));
|
||||||
}
|
}
|
||||||
|
|
@ -28,5 +28,35 @@ if (!window.ResizeObserver) {
|
||||||
observe(target) {
|
observe(target) {
|
||||||
this.observer.observe(target, { attributes: true, subtree: true, attributeFilter: ['style'] });
|
this.observer.observe(target, { attributes: true, subtree: true, attributeFilter: ['style'] });
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Storage = type => {
|
||||||
|
let name = type+'Storage';
|
||||||
|
try {
|
||||||
|
w[name].setItem('storage', '');
|
||||||
|
w[name].getItem('storage');
|
||||||
|
w[name].removeItem('storage');
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
const cookieName = encodeURIComponent(name+('session' === type ? w.name || (w.name = Date.now()) : ''));
|
||||||
|
|
||||||
|
// initialise if there's already data
|
||||||
|
let data = document.cookie.match('(^|;) ?'+cookieName+'=([^;]*)(;|$)');
|
||||||
|
data = data ? decodeURIComponent(data[2]) : null;
|
||||||
|
data = data ? JSON.parse(data) : {};
|
||||||
|
|
||||||
|
w[name] = {
|
||||||
|
getItem: key => data[key] === undefined ? null : data[key],
|
||||||
|
setItem: function (key, value) {
|
||||||
|
data[key] = ''+value; // forces the value to a string
|
||||||
|
document.cookie = cookieName+'='+encodeURIComponent(JSON.stringify(data))
|
||||||
|
+"; expires="+('local' === type ? (new Date(Date.now()+(365*24*60*60*1000))).toGMTString() : '')
|
||||||
|
+"; path=/; samesite=strict";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
Storage('local');
|
||||||
|
Storage('session');
|
||||||
|
|
||||||
|
})(window);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue