diff --git a/README.md b/README.md index 27f66ab41..3512dacbd 100644 --- a/README.md +++ b/README.md @@ -113,25 +113,25 @@ RainLoop 1.14 vs SnappyMail |js/* |RainLoop |Snappy | |--------------- |--------: |--------: | -|admin.js |2.130.942 | 123.042 | -|app.js |4.184.455 | 534.334 | +|admin.js |2.130.942 | 120.785 | +|app.js |4.184.455 | 529.074 | |boot.js | 671.522 | 4.842 | |libs.js | 647.614 | 235.475 | |polyfills.js | 325.834 | 0 | |serviceworker.js | 0 | 285 | -|TOTAL |7.960.367 | 897.978 | +|TOTAL |7.960.367 | 890.449 | |js/min/* |RainLoop |Snappy |Rain gzip |gzip |brotli | |--------------- |--------: |--------: |--------: |--------: |--------: | -|admin.min.js | 252.147 | 63.913 | 73.657 | 18.158 | 16.135 | -|app.min.js | 511.202 | 262.917 |140.462 | 74.870 | 62.686 | +|admin.min.js | 252.147 | 62.863 | 73.657 | 18.009 | 15.992 | +|app.min.js | 511.202 | 259.534 |140.462 | 74.005 | 62.184 | |boot.min.js | 66.007 | 2.630 | 22.567 | 1.375 | 1.189 | |libs.min.js | 572.545 | 130.930 |176.720 | 47.397 | 42.116 | |polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 | -|TOTAL |1.434.353 | 460.390 |424.718 |141.800 |122.126 | -|TOTAL (no admin) |1.182.206 | 396.477 |351.061 |123.642 |105.991 | +|TOTAL |1.434.353 | 455.957 |424.718 |140.786 |121.481 | +|TOTAL (no admin) |1.182.206 | 393.094 |351.061 |122.777 |105.489 | -For a user its around 64% smaller and faster than traditional RainLoop. +For a user its around 65% smaller and faster than traditional RainLoop. ### CSS changes diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 24720b890..304ceffcb 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -1,9 +1,10 @@ import ko from 'ko'; import { + doc, $htmlCL, leftPanelDisabled, - leftPanelType + Settings } from 'Common/Globals'; import { KeyState } from 'Common/Enums'; @@ -11,7 +12,7 @@ import { rootAdmin, rootUser } from 'Common/Links'; import { initOnStartOrLangChange } from 'Common/Translator'; import LanguageStore from 'Stores/Language'; -import ThemeStore from 'Stores/Theme'; +import { ThemeStore } from 'Stores/Theme'; import SaveTriggerComponent from 'Component/SaveTrigger'; import InputComponent from 'Component/Input'; @@ -20,8 +21,6 @@ import TextAreaComponent from 'Component/TextArea'; import CheckboxMaterialDesignComponent from 'Component/MaterialDesign/Checkbox'; import CheckboxComponent from 'Component/Checkbox'; -const Settings = rl.settings, doc = document; - export class AbstractApp { /** * @param {RemoteStorage|AdminRemoteStorage} Remote @@ -45,16 +44,12 @@ export class AbstractApp { return null; } - data() { - return null; - } - /** * @param {string} link * @returns {boolean} */ download(link) { - if (rl.settings.app('mobile')) { + if (Settings.app('mobile')) { open(link, '_self'); focus(); } else { @@ -88,39 +83,27 @@ export class AbstractApp { } bootstart() { - const mobile = Settings.app('mobile'); + const mobile = Settings.app('mobile'), + register = (key, obj) => ko.components.register(key, obj); - ko.components.register('SaveTrigger', SaveTriggerComponent); - ko.components.register('Input', InputComponent); - ko.components.register('Select', SelectComponent); - ko.components.register('TextArea', TextAreaComponent); - ko.components.register('CheckboxSimple', CheckboxComponent); - if (Settings.app('materialDesign') && !rl.settings.app('mobile')) { - ko.components.register('Checkbox', CheckboxMaterialDesignComponent); - } else { - ko.components.register('Checkbox', CheckboxComponent); - } + register('SaveTrigger', SaveTriggerComponent); + register('Input', InputComponent); + register('Select', SelectComponent); + register('TextArea', TextAreaComponent); + register('CheckboxSimple', CheckboxComponent); + register('Checkbox', Settings.app('materialDesign') && !mobile + ? CheckboxMaterialDesignComponent + : CheckboxComponent); initOnStartOrLangChange(); - if (!mobile) { - // mobile - window.addEventListener('resize', () => leftPanelDisabled(767 >= window.innerWidth)); - } else { + if (mobile) { $htmlCL.add('rl-mobile'); leftPanelDisabled(true); + } else { + window.addEventListener('resize', () => leftPanelDisabled(767 >= window.innerWidth)); } - leftPanelDisabled.subscribe((bValue) => { - $htmlCL.toggle('rl-left-panel-disabled', bValue); - $htmlCL.toggle('rl-left-panel-enabled', !bValue); - }); - - leftPanelType.subscribe((sValue) => { - $htmlCL.toggle('rl-left-panel-none', 'none' === sValue); - $htmlCL.toggle('rl-left-panel-short', 'short' === sValue); - }); - leftPanelDisabled.valueHasMutated(); LanguageStore.populate(); diff --git a/dev/App/User.js b/dev/App/User.js index ae8355372..07050a1b7 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -17,7 +17,7 @@ import { ClientSideKeyName } from 'Common/EnumsUser'; -import { $htmlCL, leftPanelDisabled } from 'Common/Globals'; +import { doc, $htmlCL, Settings, leftPanelDisabled } from 'Common/Globals'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; @@ -74,9 +74,6 @@ import { FolderSystemPopupView } from 'View/Popup/FolderSystem'; import { AskPopupView } from 'View/Popup/Ask'; import { TwoFactorConfigurationPopupView } from 'View/Popup/TwoFactorConfiguration'; -const doc = document, - Settings = rl.settings; - class AppUser extends AbstractApp { constructor() { super(Remote); diff --git a/dev/Common/Audio.js b/dev/Common/Audio.js index 102d4390d..8c0b0df68 100644 --- a/dev/Common/Audio.js +++ b/dev/Common/Audio.js @@ -1,4 +1,5 @@ import * as Links from 'Common/Links'; +import { doc } from 'Common/Globals'; let notificator = null, player = null, @@ -48,7 +49,7 @@ let notificator = null, console.log('AudioContext ' + audioCtx.state); audioCtx.resume(); } - unlockEvents.forEach(type => document.removeEventListener(type, unlock, true)); + unlockEvents.forEach(type => doc.removeEventListener(type, unlock, true)); // setTimeout(()=>Audio.playNotification(1),1); }; @@ -56,27 +57,21 @@ if (audioCtx) { audioCtx = audioCtx ? new audioCtx : null; audioCtx.onstatechange = unlock; } -unlockEvents.forEach(type => document.addEventListener(type, unlock, true)); +unlockEvents.forEach(type => doc.addEventListener(type, unlock, true)); /** * Browsers can't play without user interaction */ const SMAudio = new class { - supported = false; - supportedMp3 = false; - supportedOgg = false; - supportedWav = false; - constructor() { player || (player = createNewObject()); this.supported = !!player; + this.supportedMp3 = canPlay('audio/mpeg;'); + this.supportedWav = canPlay('audio/wav; codecs="1"'); + this.supportedOgg = canPlay('audio/ogg; codecs="vorbis"'); if (player) { - this.supportedMp3 = !!canPlay('audio/mpeg;'); - this.supportedWav = !!canPlay('audio/wav; codecs="1"'); - this.supportedOgg = !!canPlay('audio/ogg; codecs="vorbis"'); - const stopFn = () => this.pause(); player.addEventListener('ended', stopFn); player.addEventListener('error', stopFn); diff --git a/dev/Common/File.js b/dev/Common/File.js index 4274553ac..d2673a0eb 100644 --- a/dev/Common/File.js +++ b/dev/Common/File.js @@ -175,11 +175,11 @@ export const File = { let result = FileType.Unknown; const mimeTypeParts = mimeType.split('/'), - type = mimeTypeParts[1], + type = mimeTypeParts[1].replace('x-','').replace('-compressed',''), match = str => type.includes(str); switch (true) { - case 'image' === mimeTypeParts[0] || ['png', 'jpg', 'jpeg', 'gif'].includes(ext): + case 'image' === mimeTypeParts[0] || ['png', 'jpg', 'jpeg', 'gif', 'webp'].includes(ext): result = FileType.Image; break; case 'audio' === mimeTypeParts[0] || ['mp3', 'ogg', 'oga', 'wav'].includes(ext): @@ -207,28 +207,18 @@ export const File = { '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' + 'bzip2' ].includes(type) || ['zip', '7z', 'tar', 'rar', 'gzip', 'bzip', 'bzip2'].includes(ext): result = FileType.Archive; break; - case ['pdf', 'x-pdf'].includes(type) || ['pdf'].includes(ext): + case 'pdf' == type || 'pdf' == 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): + case ['application/pkcs7-signature'].includes(mimeType) || 'p7s' == ext: result = FileType.CertificateBin; break; case match(msOffice+'.wordprocessingml') || match(openDoc+'.text') || match('vnd.ms-word') diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index 71c2157a9..9b8dccd8a 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -1,47 +1,55 @@ import ko from 'ko'; import { KeyState } from 'Common/Enums'; -export const $html = document.documentElement; -export const $htmlCL = $html.classList; +export const doc = document; + +export const $htmlCL = doc.documentElement.classList; + +export const Settings = rl.settings; -/** - * @type {?} - */ export const dropdownVisibility = ko.observable(false).extend({ rateLimit: 0 }); export const moveAction = ko.observable(false); export const leftPanelDisabled = ko.observable(false); export const leftPanelType = ko.observable(''); -export const leftPanelWidth = ko.observable(0); -leftPanelDisabled.subscribe(value => value && moveAction() && moveAction(false)); +leftPanelDisabled.subscribe(value => { + value && moveAction() && moveAction(false); + $htmlCL.toggle('rl-left-panel-disabled', value); +}); + +leftPanelType.subscribe(sValue => { + $htmlCL.toggle('rl-left-panel-none', 'none' === sValue); + $htmlCL.toggle('rl-left-panel-short', 'short' === sValue); +}); moveAction.subscribe(value => value && leftPanelDisabled() && leftPanelDisabled(false)); // keys export const keyScopeReal = ko.observable(KeyState.All); -export const keyScopeFake = ko.observable(KeyState.All); -export const keyScope = ko.computed({ - read: () => keyScopeFake(), - write: value => { - if (KeyState.Menu !== value) { - keyScopeFake(value); - if (dropdownVisibility()) { - value = KeyState.Menu; - } +export const keyScope = (()=>{ + let keyScopeFake = KeyState.All; + dropdownVisibility.subscribe(value => { + if (value) { + keyScope(KeyState.Menu); + } else if (KeyState.Menu === shortcuts.getScope()) { + keyScope(keyScopeFake); } + }); + return ko.computed({ + read: () => keyScopeFake, + write: value => { + if (KeyState.Menu !== value) { + keyScopeFake = value; + if (dropdownVisibility()) { + value = KeyState.Menu; + } + } - keyScopeReal(value); - } -}); + keyScopeReal(value); + } + }); +})(); keyScopeReal.subscribe(value => shortcuts.setScope(value)); - -dropdownVisibility.subscribe(value => { - if (value) { - keyScope(KeyState.Menu); - } else if (KeyState.Menu === shortcuts.getScope()) { - keyScope(keyScopeFake()); - } -}); diff --git a/dev/Common/Links.js b/dev/Common/Links.js index 980e14e88..d89a7b6ff 100644 --- a/dev/Common/Links.js +++ b/dev/Common/Links.js @@ -1,7 +1,7 @@ import { pString, pInt } from 'Common/Utils'; +import { Settings } from 'Common/Globals'; const - Settings = rl.settings, ROOT = './', HASH_PREFIX = '#/', SERVER_PREFIX = './?', @@ -42,25 +42,17 @@ export function rootUser() { /** * @param {string} type - * @param {string} download + * @param {string} hash * @param {string=} customSpecSuffix * @returns {string} */ -export function attachmentRaw(type, download, customSpecSuffix) { - customSpecSuffix = undefined === customSpecSuffix ? getHash() : customSpecSuffix; - return ( - SERVER_PREFIX + - '/Raw/' + - SUB_QUERY_PREFIX + - '/' + - customSpecSuffix + - '/' + - type + - '/' + - SUB_QUERY_PREFIX + - '/' + - download - ); +export function serverRequestRaw(type, hash, customSpecSuffix) { + return SERVER_PREFIX + '/Raw/' + SUB_QUERY_PREFIX + '/' + + (null == customSpecSuffix ? getHash() : customSpecSuffix) + '/' + + (type + ? type + '/' + (hash ? SUB_QUERY_PREFIX + '/' + hash : '') + : '') + ; } /** @@ -69,7 +61,7 @@ export function attachmentRaw(type, download, customSpecSuffix) { * @returns {string} */ export function attachmentDownload(download, customSpecSuffix) { - return attachmentRaw('Download', download, customSpecSuffix); + return serverRequestRaw('Download', download, customSpecSuffix); } /** @@ -78,7 +70,7 @@ export function attachmentDownload(download, customSpecSuffix) { * @returns {string} */ export function attachmentPreview(download, customSpecSuffix) { - return attachmentRaw('View', download, customSpecSuffix); + return serverRequestRaw('View', download, customSpecSuffix); } /** @@ -87,7 +79,7 @@ export function attachmentPreview(download, customSpecSuffix) { * @returns {string} */ export function attachmentThumbnailPreview(download, customSpecSuffix) { - return attachmentRaw('ViewThumbnail', download, customSpecSuffix); + return serverRequestRaw('ViewThumbnail', download, customSpecSuffix); } /** @@ -96,7 +88,7 @@ export function attachmentThumbnailPreview(download, customSpecSuffix) { * @returns {string} */ export function attachmentPreviewAsPlain(download, customSpecSuffix) { - return attachmentRaw('ViewAsPlain', download, customSpecSuffix); + return serverRequestRaw('ViewAsPlain', download, customSpecSuffix); } /** @@ -105,7 +97,7 @@ export function attachmentPreviewAsPlain(download, customSpecSuffix) { * @returns {string} */ export function attachmentFramed(download, customSpecSuffix) { - return attachmentRaw('FramedView', download, customSpecSuffix); + return serverRequestRaw('FramedView', download, customSpecSuffix); } /** @@ -157,17 +149,7 @@ export function change(email) { * @returns {string} */ export function messageViewLink(requestHash) { - return ( - SERVER_PREFIX + - '/Raw/' + - SUB_QUERY_PREFIX + - '/' + - getHash() + - '/ViewAsPlain/' + - SUB_QUERY_PREFIX + - '/' + - requestHash - ); + return serverRequestRaw('ViewAsPlain', requestHash); } /** @@ -175,17 +157,7 @@ export function messageViewLink(requestHash) { * @returns {string} */ export function messageDownloadLink(requestHash) { - return ( - SERVER_PREFIX + '/Raw/' + SUB_QUERY_PREFIX + '/' + getHash() + '/Download/' + SUB_QUERY_PREFIX + '/' + requestHash - ); -} - -/** - * @param {string} email - * @returns {string} - */ -export function avatarLink(email) { - return SERVER_PREFIX + '/Raw/0/Avatar/' + encodeURIComponent(email) + '/'; + return serverRequestRaw('Download', requestHash); } /** @@ -201,9 +173,7 @@ export function publicLink(hash) { * @returns {string} */ export function userBackground(hash) { - return ( - SERVER_PREFIX + '/Raw/' + SUB_QUERY_PREFIX + '/' + getHash() + '/UserBackground/' + SUB_QUERY_PREFIX + '/' + hash - ); + return serverRequestRaw('UserBackground', hash); } /** @@ -219,14 +189,14 @@ export function langLink(lang, isAdmin) { * @returns {string} */ export function exportContactsVcf() { - return SERVER_PREFIX + '/Raw/' + SUB_QUERY_PREFIX + '/' + getHash() + '/ContactsVcf/'; + return serverRequestRaw('ContactsVcf'); } /** * @returns {string} */ export function exportContactsCsv() { - return SERVER_PREFIX + '/Raw/' + SUB_QUERY_PREFIX + '/' + getHash() + '/ContactsCsv/'; + return serverRequestRaw('ContactsCsv'); } /** @@ -266,13 +236,6 @@ export function openPgpWorkerJs() { return staticPrefix('js/min/openpgp.worker.min.js'); } -/** - * @returns {string} - */ -export function openPgpWorkerPath() { - return staticPrefix('js/min/'); -} - /** * @param {string} theme * @returns {string} @@ -303,13 +266,6 @@ export function settings(screenName = '') { return HASH_PREFIX + 'settings' + (screenName ? '/' + screenName : ''); } -/** - * @returns {string} - */ -export function about() { - return HASH_PREFIX + 'about'; -} - /** * @param {string} screenName * @returns {string} diff --git a/dev/Common/Momentor.js b/dev/Common/Momentor.js index c9a1d65ae..6da0a88ce 100644 --- a/dev/Common/Momentor.js +++ b/dev/Common/Momentor.js @@ -1,6 +1,7 @@ +import { doc } from 'Common/Globals'; import { i18n } from 'Common/Translator'; -export function format(timeStampInUTC, formatStr) { +export function timestampToString(timeStampInUTC, formatStr) { const now = Date.now(), time = 0 < timeStampInUTC ? Math.min(now, timeStampInUTC * 1000) : (0 === timeStampInUTC ? now : 0); @@ -36,17 +37,15 @@ export function timeToNode(element, time) { try { time = time || (Date.parse(element.dateTime) / 1000); if (time) { - let key, m = new Date(time * 1000); - element.dateTime = m.format('Y-m-d\\TH:i:s'); + element.dateTime = (new Date(time * 1000)).format('Y-m-d\\TH:i:s'); - key = element.dataset.momentFormat; + let key = element.dataset.momentFormat; if (key) { - element.textContent = format(time, key); + element.textContent = timestampToString(time, key); } - key = element.dataset.momentFormatTitle; - if (key) { - element.title = format(time, key); + if ((key = element.dataset.momentFormatTitle)) { + element.title = timestampToString(time, key); } } } catch (e) { @@ -56,6 +55,6 @@ export function timeToNode(element, time) { } addEventListener('reload-time', () => setTimeout(() => - document.querySelectorAll('[data-bind*="moment:"]').forEach(element => timeToNode(element)) + doc.querySelectorAll('[data-bind*="moment:"]').forEach(element => timeToNode(element)) , 1) ); diff --git a/dev/Common/Plugins.js b/dev/Common/Plugins.js index 374c2cfd3..cf2858d13 100644 --- a/dev/Common/Plugins.js +++ b/dev/Common/Plugins.js @@ -3,14 +3,6 @@ import { settingsAddViewModel } from 'Screen/AbstractSettings'; const USER_VIEW_MODELS_HOOKS = [], ADMIN_VIEW_MODELS_HOOKS = []; -/** - * @param {string} name - * @returns {?} - */ -export function mainSettingsGet(name) { - return rl.settings.get(name); -} - /** * @param {Function} callback * @param {string} action diff --git a/dev/Common/Translator.js b/dev/Common/Translator.js index c08a59606..0fff7abf4 100644 --- a/dev/Common/Translator.js +++ b/dev/Common/Translator.js @@ -1,11 +1,10 @@ import ko from 'ko'; import { Notification, UploadErrorCode } from 'Common/Enums'; import { langLink } from 'Common/Links'; +import { doc } from 'Common/Globals'; let I18N_DATA = window.rainloopI18N || {}; -const doc = document; - export const trigger = ko.observable(false); /** @@ -54,8 +53,10 @@ const i18nToNode = element => { } } }, - getKeyByValue = (o, v) => Object.keys(o).find(key => o[key] === v); + i18nKey = key => key.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase(), + + getKeyByValue = (o, v) => Object.keys(o).find(key => o[key] === v); /** * @param {Object} elements @@ -80,9 +81,8 @@ export function initOnStartOrLangChange(startCallback, langCallback = null) { function getNotificationMessage(code) { let key = getKeyByValue(Notification, code); if (key) { - key = key.replace('CantCopyMessage', 'CantMoveMessage').replace('UnknownNotification', 'UnknownError'); - key = 'NOTIFICATIONS/' + key.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase(); - return I18N_DATA[key]; + key = i18nKey(key).replace('_NOTIFICATION', '_ERROR'); + return I18N_DATA['NOTIFICATIONS/' + key]; } } @@ -129,7 +129,7 @@ export function getUploadErrorDescByCode(code) { case UploadErrorCode.MissingTempFolder: case UploadErrorCode.OnSavingFile: case UploadErrorCode.FileType: - result = getKeyByValue(UploadErrorCode, code).replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase(); + result = i18nKey(getKeyByValue(UploadErrorCode, code)); break; } return i18n('UPLOAD/ERROR_' + result); diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 7cff2a83e..d6e03470c 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -1,7 +1,9 @@ import { SaveSettingsStep } from 'Common/Enums'; +import { doc } from 'Common/Globals'; -const - doc = document; +export const + isArray = Array.isArray, + isNonEmptyArray = Array.isNotEmpty; /** * @param {*} value diff --git a/dev/Common/UtilsUser.js b/dev/Common/UtilsUser.js index 6f9dacc1e..8a0d29b74 100644 --- a/dev/Common/UtilsUser.js +++ b/dev/Common/UtilsUser.js @@ -2,10 +2,8 @@ import { ComposeType, FolderType } from 'Common/EnumsUser'; import { EmailModel } from 'Model/Email'; import { showScreenPopup } from 'Knoin/Knoin'; import { encodeHtml } from 'Common/Html'; - -const - tpl = document.createElement('template'), - isArray = Array.isArray; +import { isArray } from 'Common/Utils'; +import { doc } from 'Common/Globals'; /** * @param {(string|number)} value @@ -81,7 +79,9 @@ export function htmlToPlain(html) { return ''; }; - const convertPre = (...args) => + const + tpl = doc.createElement('template'), + convertPre = (...args) => args && 1 < args.length ? args[1] .toString() diff --git a/dev/External/CKEditor.js b/dev/External/CKEditor.js index f3627c471..580309831 100644 --- a/dev/External/CKEditor.js +++ b/dev/External/CKEditor.js @@ -1,9 +1,9 @@ const config = { - 'title': false, - 'stylesSet': false, - 'customConfig': '', - 'contentsCss': '', - 'toolbarGroups': [ + title: false, + stylesSet: false, + customConfig: '', + contentsCss: '', + toolbarGroups: [ { name: 'spec' }, { name: 'styles' }, { name: 'basicstyles', groups: ['basicstyles', 'cleanup', 'bidi'] }, @@ -15,85 +15,56 @@ const config = { { name: 'others' } ], - 'removePlugins': 'liststyle', - 'removeButtons': 'Format,Undo,Redo,Cut,Copy,Paste,Anchor,Strike,Subscript,Superscript,Image,SelectAll,Source', - 'removeDialogTabs': 'link:advanced;link:target;image:advanced;images:advanced', + removePlugins: 'liststyle', + removeButtons: 'Format,Undo,Redo,Cut,Copy,Paste,Anchor,Strike,Subscript,Superscript,Image,SelectAll', + removeDialogTabs: 'link:advanced;link:target;image:advanced;images:advanced', - 'extraPlugins': 'plain,signature', + extraPlugins: 'plain,signature', - 'allowedContent': true, - 'extraAllowedContent': true, + allowedContent: true, + extraAllowedContent: true, - 'fillEmptyBlocks': false, - 'ignoreEmptyParagraph': true, - 'disableNativeSpellChecker': false, + fillEmptyBlocks: false, + ignoreEmptyParagraph: true, + disableNativeSpellChecker: false, - 'colorButton_enableAutomatic': false, - 'colorButton_enableMore': true, + colorButton_enableAutomatic: false, + colorButton_enableMore: true, - 'font_defaultLabel': 'Arial', - 'fontSize_defaultLabel': '13', - 'fontSize_sizes': '10/10px;12/12px;13/13px;14/14px;16/16px;18/18px;20/20px;24/24px;28/28px;36/36px;48/48px' + font_defaultLabel: 'Arial', + fontSize_defaultLabel: '13', + fontSize_sizes: '10/10px;12/12px;13/13px;14/14px;16/16px;18/18px;20/20px;24/24px;28/28px;36/36px;48/48px' }, -/** - * @type {Object} - */ htmlEditorLangsMap = { 'ar_sa': 'ar-sa', - 'bg_bg': 'bg', - 'cs_CZ': 'cs', - 'de_de': 'de', - 'el_gr': 'el', - 'es_es': 'es', - 'et_ee': 'et', - 'fr_fr': 'fr', - 'hu_hu': 'hu', - 'is_is': 'is', - 'it_it': 'it', - 'ja_jp': 'ja', - 'ko_kr': 'ko', - 'lt_lt': 'lt', - 'lv_lv': 'lv', - 'fa_ir': 'fa', - 'nb_no': 'nb', - 'nl_nl': 'nl', - 'pl_pl': 'pl', 'pt_br': 'pt-br', - 'pt_pt': 'pt', - 'ro_ro': 'ro', - 'ru_ru': 'ru', - 'sk_sk': 'sk', - 'sl_si': 'sl', - 'sv_se': 'sv', - 'tr_tr': 'tr', - 'uk_ua': 'uk', 'zh_cn': 'zh-cn', - 'zh_tw': 'zh' }; export function createCKEditor(element) { - const language = rl.settings.get('Language'), - allowSource = !!rl.settings.app('allowHtmlEditorSourceButton'), - biti = !!rl.settings.app('allowHtmlEditorBitiButtons'); + const language = (rl.settings.get('Language') || 'en').toLowerCase(), + noSource = !rl.settings.app('allowHtmlEditorSourceButton'), + noBidi = !rl.settings.app('allowHtmlEditorBitiButtons'); - if ((allowSource || !biti) && !config.toolbarGroups.__cfgInited) { - config.toolbarGroups.__cfgInited = true; + if (!config.__cfgInited) { + config.__cfgInited = true; - if (allowSource) { - config.removeButtons = config.removeButtons.replace(',Source', ''); + if (noSource) { + config.removeButtons += ',Source'; } - if (!biti) { - config.removePlugins += (config.removePlugins ? ',' : '') + 'bidi'; + if (noBidi) { + config.removePlugins += ',bidi'; } } config.enterMode = CKEDITOR.ENTER_BR; config.shiftEnterMode = CKEDITOR.ENTER_P; - config.language = htmlEditorLangsMap[(language || 'en').toLowerCase()] || 'en'; + config.language = htmlEditorLangsMap[language] || language.substr(0,2); + if (CKEDITOR.env) { CKEDITOR.env.isCompatible = true; } @@ -112,12 +83,10 @@ export function createCKEditor(element) reader = new FileReader(); reader.onloadend = () => { - if (reader.result) { - if ('wysiwyg' === editor.mode) { - try { - editor.setData(editor.getData().replace(imageId, ``)); - } catch (e) {} // eslint-disable-line no-empty - } + if (reader.result && 'wysiwyg' === editor.mode) { + try { + editor.setData(editor.getData().replace(imageId, ``)); + } catch (e) {} // eslint-disable-line no-empty } }; diff --git a/dev/External/User/ko.js b/dev/External/User/ko.js index e70095f37..869fca0ad 100644 --- a/dev/External/User/ko.js +++ b/dev/External/User/ko.js @@ -3,6 +3,7 @@ import ko from 'ko'; import { HtmlEditor } from 'Common/Html'; import { timeToNode } from 'Common/Momentor'; import { EmailModel } from 'Model/Email'; +import { doc } from 'Common/Globals'; const rlContentType = 'snappymail/action', @@ -111,7 +112,7 @@ ko.bindingHandlers.dragmessages = { if (!rl.settings.app('mobile')) { element.addEventListener("dragstart", e => { let data = fValueAccessor()(e); - dragImage || (dragImage = document.getElementById('messagesDragImage')); + dragImage || (dragImage = doc.getElementById('messagesDragImage')); if (data && dragImage) { dragImage.querySelector('.text').textContent = data.uids.length; let img = dragImage.querySelector('.icon-white'); diff --git a/dev/External/ko.js b/dev/External/ko.js index 395b5f7b0..e2bb9e875 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -1,8 +1,7 @@ import { i18n, i18nToNodes, trigger } from 'Common/Translator'; -import { dropdownVisibility } from 'Common/Globals'; +import { doc, dropdownVisibility } from 'Common/Globals'; const - doc = document, isFunction = v => typeof v === 'function', koValue = value => !ko.isObservable(value) && isFunction(value) ? value() : ko.unwrap(value); diff --git a/dev/Knoin/AbstractScreen.js b/dev/Knoin/AbstractScreen.js index 10eeffc26..a4311bcf5 100644 --- a/dev/Knoin/AbstractScreen.js +++ b/dev/Knoin/AbstractScreen.js @@ -39,15 +39,18 @@ export class AbstractScreen { /** * @returns {void} */ - __start() { - const routes = this.routes(); - if (Array.isNotEmpty(routes)) { - let route = new Crossroads(), - fMatcher = (this.onRoute || (()=>{})).bind(this); + onStart() { + if (!this.__started) { + this.__started = true; + const routes = this.routes(); + if (Array.isNotEmpty(routes)) { + let route = new Crossroads(), + fMatcher = (this.onRoute || (()=>{})).bind(this); - routes.forEach(item => item && route && (route.addRoute(item[0], fMatcher).rules = item[1])); + routes.forEach(item => item && route && (route.addRoute(item[0], fMatcher).rules = item[1])); - this.oCross = route; + this.oCross = route; + } } } } diff --git a/dev/Knoin/Knoin.js b/dev/Knoin/Knoin.js index 0dc37c2b6..99111cc15 100644 --- a/dev/Knoin/Knoin.js +++ b/dev/Knoin/Knoin.js @@ -1,13 +1,13 @@ import ko from 'ko'; -import { $htmlCL } from 'Common/Globals'; +import { doc, $htmlCL } from 'Common/Globals'; +import { isNonEmptyArray } from 'Common/Utils'; let currentScreen = null, defaultScreenName = '', popupVisibilityNames = []; const SCREENS = {}, - isNonEmptyArray = Array.isNotEmpty, autofocus = dom => { // if (!rl.settings.app('mobile')) { const af = dom.querySelector('[autofocus]'); @@ -54,7 +54,7 @@ export function createCommand(fExecute, fCanExecute = true) { * @param {string} screenName * @returns {?Object} */ -export function screen(screenName) { +function screen(screenName) { return screenName && null != SCREENS[screenName] ? SCREENS[screenName] : null; } @@ -78,7 +78,7 @@ function buildViewModel(ViewModelClass, vmScreen) { let vmDom = null; const vm = new ViewModelClass(vmScreen), position = vm.viewModelPosition || '', - vmPlace = position ? document.querySelector('#rl-content #rl-' + position.toLowerCase()) : null; + vmPlace = position ? doc.querySelector('#rl-content #rl-' + position.toLowerCase()) : null; ViewModelClass.__builded = true; ViewModelClass.__vm = vm; @@ -223,11 +223,9 @@ function screenOnRoute(screenName, subPart) { if (!vmScreen.__builded) { vmScreen.__builded = true; - if (vmScreen.viewModels.length) { - vmScreen.viewModels.forEach(ViewModelClass => { - buildViewModel(ViewModelClass, vmScreen); - }); - } + vmScreen.viewModels.forEach(ViewModelClass => + buildViewModel(ViewModelClass, vmScreen) + ); vmScreen.onBuild && vmScreen.onBuild(); } @@ -301,23 +299,17 @@ export function startScreens(screensClasses) { const vmScreen = new CScreen(), screenName = vmScreen && vmScreen.screenName(); - if (vmScreen && screenName) { - if (!defaultScreenName) { - defaultScreenName = screenName; - } + if (screenName) { + defaultScreenName || (defaultScreenName = screenName); SCREENS[screenName] = vmScreen; } } }); - Object.values(SCREENS).forEach(vmScreen => { - if (vmScreen && !vmScreen.__started && vmScreen.__start) { - vmScreen.__started = true; - vmScreen.__start(); - vmScreen.onStart && vmScreen.onStart(); - } - }); + Object.values(SCREENS).forEach(vmScreen => + vmScreen && vmScreen.onStart && vmScreen.onStart() + ); const cross = new Crossroads(); cross.addRoute(/^([a-zA-Z0-9-]*)\/?(.*)$/, screenOnRoute); @@ -326,10 +318,7 @@ export function startScreens(screensClasses) { hasher.changed.add(cross.parse, cross); hasher.init(); - setTimeout(() => { - $htmlCL.remove('rl-started-trigger'); - $htmlCL.add('rl-started'); - }, 100); + setTimeout(() => $htmlCL.remove('rl-started-trigger'), 100); setTimeout(() => $htmlCL.add('rl-started-delay'), 200); } diff --git a/dev/Model/FolderCollection.js b/dev/Model/FolderCollection.js index e1b293360..0c07101f2 100644 --- a/dev/Model/FolderCollection.js +++ b/dev/Model/FolderCollection.js @@ -4,6 +4,7 @@ import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { pInt } from 'Common/Utils'; import { ClientSideKeyName, FolderType } from 'Common/EnumsUser'; import * as Cache from 'Common/Cache'; +import { Settings } from 'Common/Globals'; import * as Local from 'Storage/Client'; @@ -17,8 +18,7 @@ import { i18n, trigger as translatorTrigger } from 'Common/Translator'; import { AbstractModel } from 'Knoin/AbstractModel'; -const Settings = rl.settings, - +const ServerFolderType = { USER: 0, INBOX: 1, diff --git a/dev/Model/Message.js b/dev/Model/Message.js index aff189b75..cc21cc184 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -4,6 +4,7 @@ import { MessagePriority } from 'Common/EnumsUser'; import { i18n } from 'Common/Translator'; import { encodeHtml } from 'Common/Html'; +import { isArray } from 'Common/Utils'; import { messageViewLink, messageDownloadLink } from 'Common/Links'; @@ -16,8 +17,7 @@ import { AbstractModel } from 'Knoin/AbstractModel'; import PreviewHTML from 'Html/PreviewMessage.html'; -const isArray = Array.isArray, - +const SignedVerifyStatus = { UnknownPublicKeys: -4, UnknownPrivateKey: -3, diff --git a/dev/Screen/AbstractSettings.js b/dev/Screen/AbstractSettings.js index fb433d7cd..510a55ee4 100644 --- a/dev/Screen/AbstractSettings.js +++ b/dev/Screen/AbstractSettings.js @@ -2,6 +2,7 @@ import ko from 'ko'; import { pString } from 'Common/Utils'; import { settings } from 'Common/Links'; +import { doc } from 'Common/Globals'; import { AbstractScreen } from 'Knoin/AbstractScreen'; @@ -44,7 +45,7 @@ export class AbstractSettingsScreen extends AbstractScreen { if (RoutedSettingsViewModel.__builded && RoutedSettingsViewModel.__vm) { settingsScreen = RoutedSettingsViewModel.__vm; } else { - const vmPlace = document.getElementById('rl-settings-subscreen'); + const vmPlace = doc.getElementById('rl-settings-subscreen'); if (vmPlace) { settingsScreen = new RoutedSettingsViewModel(); @@ -101,7 +102,7 @@ export class AbstractSettingsScreen extends AbstractScreen { ); }); - document.querySelector('#rl-content .b-settings .b-content').scrollTop = 0; + doc.querySelector('#rl-content .b-settings .b-content').scrollTop = 0; } // -- }, 1); diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index 192e589d2..111a3ff7b 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -1,6 +1,6 @@ import { Capa } from 'Common/Enums'; import { Focused, ClientSideKeyName } from 'Common/EnumsUser'; -import { leftPanelDisabled, leftPanelType, moveAction } from 'Common/Globals'; +import { doc, leftPanelDisabled, leftPanelType, moveAction, Settings } from 'Common/Globals'; import { pString, pInt } from 'Common/Utils'; import { getFolderFromCacheList, getFolderFullNameRaw, getFolderInboxName } from 'Common/Cache'; import { i18n } from 'Common/Translator'; @@ -22,8 +22,6 @@ import { AbstractScreen } from 'Knoin/AbstractScreen'; import { ComposePopupView } from 'View/Popup/Compose'; -const Settings = rl.settings; - export class MailBoxUserScreen extends AbstractScreen { constructor() { super('mailbox', [ @@ -38,12 +36,8 @@ export class MailBoxUserScreen extends AbstractScreen { * @returns {void} */ updateWindowTitle() { - let foldersInboxUnreadCount = FolderStore.foldersInboxUnreadCount(); - const email = AccountStore.email(); - - if (Settings.app('listPermanentFiltered')) { - foldersInboxUnreadCount = 0; - } + const foldersInboxUnreadCount = Settings.app('listPermanentFiltered') ? 0 : FolderStore.foldersInboxUnreadCount(), + email = AccountStore.email(); rl.setWindowTitle( (email @@ -66,10 +60,10 @@ export class MailBoxUserScreen extends AbstractScreen { leftPanelDisabled(true); } - if (!Settings.capa(Capa.Folders)) { - leftPanelType(Settings.capa(Capa.Composer) || Settings.capa(Capa.Contacts) ? 'short' : 'none'); - } else { + if (Settings.capa(Capa.Folders)) { leftPanelType(''); + } else { + leftPanelType(Settings.capa(Capa.Composer) || Settings.capa(Capa.Contacts) ? 'short' : 'none'); } } @@ -80,10 +74,9 @@ export class MailBoxUserScreen extends AbstractScreen { * @returns {void} */ onRoute(folderHash, page, search) { - let threadUid = folderHash.replace(/^(.+)~([\d]+)$/, '$2'); const folder = getFolderFromCacheList(getFolderFullNameRaw(folderHash.replace(/~([\d]+)$/, ''))); - if (folder) { + let threadUid = folderHash.replace(/^(.+)~([\d]+)$/, '$2'); if (folderHash === threadUid) { threadUid = ''; } @@ -102,21 +95,22 @@ export class MailBoxUserScreen extends AbstractScreen { * @returns {void} */ onStart() { - setTimeout(() => SettingsStore.layout.valueHasMutated(), 50); - setTimeout(() => warmUpScreenPopup(ComposePopupView), 500); + if (!this.__started) { + super.onStart(); + setTimeout(() => SettingsStore.layout.valueHasMutated(), 50); + setTimeout(() => warmUpScreenPopup(ComposePopupView), 500); - addEventListener('mailbox.inbox-unread-count', e => { - FolderStore.foldersInboxUnreadCount(e.detail); + addEventListener('mailbox.inbox-unread-count', e => { + FolderStore.foldersInboxUnreadCount(e.detail); - const email = AccountStore.email(); - AccountStore.accounts.forEach(item => { - if (item && email === item.email) { - item.count(e.detail); - } + const email = AccountStore.email(); + AccountStore.accounts.forEach(item => + item && email === item.email && item.count(e.detail) + ); + + this.updateWindowTitle(); }); - - this.updateWindowTitle(); - }); + } } /** @@ -129,7 +123,7 @@ export class MailBoxUserScreen extends AbstractScreen { , 1); } - document.addEventListener('click', event => + doc.addEventListener('click', event => event.target.closest('#rl-right') && moveAction(false) ); } @@ -138,30 +132,19 @@ export class MailBoxUserScreen extends AbstractScreen { * @returns {Array} */ routes() { - const inboxFolderName = getFolderInboxName(), + const fNormS = (request, vals) => { - vals[0] = pString(vals[0]); - vals[1] = pInt(vals[1]); - vals[1] = 0 >= vals[1] ? 1 : vals[1]; - vals[2] = pString(vals[2]); - - if (!request) { - vals[0] = inboxFolderName; + if (request) { + vals[0] = pString(vals[0]); + vals[1] = pInt(vals[1]); + } else { + vals[0] = getFolderInboxName(); vals[1] = 1; } - - return [decodeURI(vals[0]), vals[1], decodeURI(vals[2])]; + return [decodeURI(vals[0]), 1 > vals[1] ? 1 : vals[1], decodeURI(pString(vals[2]))]; }, - fNormD = (request, vals) => { - vals[0] = pString(vals[0]); - vals[1] = pString(vals[1]); - - if (!request) { - vals[0] = inboxFolderName; - } - - return [decodeURI(vals[0]), 1, decodeURI(vals[1])]; - }; + fNormD = (request, vals) => + [decodeURI(request ? pString(vals[0]) : getFolderInboxName()), 1, decodeURI(pString(vals[1]))]; return [ [/^([a-zA-Z0-9~]+)\/p([1-9][0-9]*)\/(.+)\/?$/, { 'normalize_': fNormS }], diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index 6e13fca50..197305721 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -1,5 +1,5 @@ import { Capa, KeyState } from 'Common/Enums'; -import { keyScope, leftPanelType, leftPanelDisabled } from 'Common/Globals'; +import { keyScope, leftPanelType, leftPanelDisabled, Settings } from 'Common/Globals'; import { runSettingsViewModelHooks } from 'Common/Plugins'; import { initOnStartOrLangChange, i18n } from 'Common/Translator'; @@ -22,8 +22,6 @@ import { SystemDropDownSettingsUserView } from 'View/User/Settings/SystemDropDow import { MenuSettingsUserView } from 'View/User/Settings/Menu'; import { PaneSettingsUserView } from 'View/User/Settings/Pane'; -const Settings = rl.settings; - export class SettingsUserScreen extends AbstractSettingsScreen { constructor() { super([SystemDropDownSettingsUserView, MenuSettingsUserView, PaneSettingsUserView]); diff --git a/dev/Settings/Admin/General.js b/dev/Settings/Admin/General.js index 30f10e4dc..fade40f7d 100644 --- a/dev/Settings/Admin/General.js +++ b/dev/Settings/Admin/General.js @@ -14,7 +14,7 @@ import { showScreenPopup } from 'Knoin/Knoin'; import Remote from 'Remote/Admin/Fetch'; -import ThemeStore from 'Stores/Theme'; +import { ThemeStore } from 'Stores/Theme'; import LanguageStore from 'Stores/Language'; import AppAdminStore from 'Stores/Admin/App'; import CapaAdminStore from 'Stores/Admin/Capa'; diff --git a/dev/Settings/User/Themes.js b/dev/Settings/User/Themes.js index 552d7fe20..d0b1fab05 100644 --- a/dev/Settings/User/Themes.js +++ b/dev/Settings/User/Themes.js @@ -4,8 +4,9 @@ import { SaveSettingsStep, UploadErrorCode, Capa } from 'Common/Enums'; import { changeTheme, convertThemeName } from 'Common/Utils'; import { userBackground, themePreviewLink, uploadBackground } from 'Common/Links'; import { i18n } from 'Common/Translator'; +import { doc, $htmlCL } from 'Common/Globals'; -import ThemeStore from 'Stores/Theme'; +import { ThemeStore } from 'Stores/Theme'; import Remote from 'Remote/User/Fetch'; @@ -39,13 +40,12 @@ export class ThemesUserSettings { }); this.background.hash.subscribe((value) => { - const b = document.body, cl = document.documentElement.classList; if (!value) { - cl.remove('UserBackground'); - b.removeAttribute('style'); + $htmlCL.remove('UserBackground'); + doc.body.removeAttribute('style'); } else { - cl.add('UserBackground'); - b.style.backgroundImage = "url("+userBackground(value)+")"; + $htmlCL.add('UserBackground'); + doc.body.style.backgroundImage = "url("+userBackground(value)+")"; } }); } diff --git a/dev/Stores/Theme.js b/dev/Stores/Theme.js index 6198c87e9..037c14f46 100644 --- a/dev/Stores/Theme.js +++ b/dev/Stores/Theme.js @@ -1,15 +1,11 @@ import ko from 'ko'; -class ThemeStore { - constructor() { - this.themes = ko.observableArray(); - this.themeBackgroundName = ko.observable(''); - this.themeBackgroundHash = ko.observable(''); +export const ThemeStore = { + themes: ko.observableArray(), + themeBackgroundName: ko.observable(''), + themeBackgroundHash: ko.observable(''), - this.theme = ko.observable('').extend({ limitedList: this.themes }); - } - - populate() { + populate: function(){ const Settings = rl.settings, themes = Settings.app('themes'); @@ -18,6 +14,6 @@ class ThemeStore { this.themeBackgroundName(Settings.get('UserBackgroundName')); this.themeBackgroundHash(Settings.get('UserBackgroundHash')); } -} +}; -export default new ThemeStore(); +ThemeStore.theme = ko.observable('').extend({ limitedList: ThemeStore.themes }); diff --git a/dev/Stores/User/App.js b/dev/Stores/User/App.js index abc312303..5c84e4a3f 100644 --- a/dev/Stores/User/App.js +++ b/dev/Stores/User/App.js @@ -2,9 +2,7 @@ import ko from 'ko'; import { KeyState } from 'Common/Enums'; import { Focused } from 'Common/EnumsUser'; -import { keyScope, leftPanelDisabled } from 'Common/Globals'; - -const Settings = rl.settings; +import { keyScope, leftPanelDisabled, Settings } from 'Common/Globals'; class AppUserStore { constructor() { diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index 17f5dea2b..b128bab15 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -2,7 +2,7 @@ import ko from 'ko'; import { StorageResultType, Notification } from 'Common/Enums'; import { Layout, Focused, MessageSetAction } from 'Common/EnumsUser'; - +import { doc } from 'Common/Globals'; import { pInt, pString } from 'Common/Utils'; import { plainToHtml } from 'Common/UtilsUser'; @@ -52,7 +52,7 @@ const let iMessageBodyCacheCount = 0; -document.body.append(hcont); +doc.body.append(hcont); class MessageUserStore { constructor() { @@ -485,7 +485,7 @@ class MessageUserStore { if (messagesDom) { id = 'rl-mgs-' + message.hash.replace(/[^a-zA-Z0-9]/g, ''); - const textBody = document.getElementById(id); + const textBody = doc.getElementById(id); if (textBody) { textBody.rlCacheCount = ++iMessageBodyCacheCount; message.body = textBody; @@ -508,7 +508,7 @@ class MessageUserStore { /-----BEGIN PGP SIGNED MESSAGE-----/.test(plain) && /-----BEGIN PGP SIGNATURE-----/.test(plain); } - const pre = document.createElement('pre'); + const pre = doc.createElement('pre'); if (pgpSigned && message.isPgpSigned()) { pre.className = 'b-plain-openpgp signed'; pre.textContent = plain; diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js index be60a5546..31cbcc875 100644 --- a/dev/Stores/User/Pgp.js +++ b/dev/Stores/User/Pgp.js @@ -2,6 +2,7 @@ import ko from 'ko'; import { i18n } from 'Common/Translator'; import { pString } from 'Common/Utils'; +import { doc } from 'Common/Globals'; import AccountStore from 'Stores/User/Account'; @@ -360,7 +361,7 @@ class PgpUserStore { verControl.addEventHandler('click', domControlSignedClickHelper(this, dom, domText)); } - dom.before(verControl, document.createElement('div')); + dom.before(verControl, doc.createElement('div')); } } } diff --git a/dev/Stores/User/Settings.js b/dev/Stores/User/Settings.js index 4792b18c8..446222457 100644 --- a/dev/Stores/User/Settings.js +++ b/dev/Stores/User/Settings.js @@ -3,6 +3,7 @@ import ko from 'ko'; import { MESSAGES_PER_PAGE_VALUES } from 'Common/Consts'; import { Layout, EditorDefaultType } from 'Common/EnumsUser'; import { pInt } from 'Common/Utils'; +import { doc } from 'Common/Globals'; class SettingsUserStore { constructor() { @@ -39,7 +40,7 @@ class SettingsUserStore { } subscribers() { - const htmlCL = document.documentElement.classList; + const htmlCL = doc.documentElement.classList; this.layout.subscribe(value => { htmlCL.toggle('rl-no-preview-pane', Layout.NoPreview === value); htmlCL.toggle('rl-side-preview-pane', Layout.SidePreview === value); diff --git a/dev/Styles/Layout.less b/dev/Styles/Layout.less index e4176424b..4d521e771 100644 --- a/dev/Styles/Layout.less +++ b/dev/Styles/Layout.less @@ -24,7 +24,7 @@ html.rl-mobile { min-height: 250px; } - &.rl-left-panel-enabled #rl-right { + &:not(.rl-left-panel-disabled) #rl-right { right: -150px; } diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index a85c4f925..13167021d 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -14,17 +14,16 @@ import { SetSystemFoldersNotification } from 'Common/EnumsUser'; -import { inFocus, pInt } from 'Common/Utils'; +import { inFocus, pInt, isArray, isNonEmptyArray } from 'Common/Utils'; import { delegateRunOnDestroy } from 'Common/UtilsUser'; -import { encodeHtml } from 'Common/Html'; +import { encodeHtml, HtmlEditor } from 'Common/Html'; import { UNUSED_OPTION_VALUE } from 'Common/Consts'; import { upload } from 'Common/Links'; import { i18n, getNotification, getUploadErrorDescByCode } from 'Common/Translator'; -import { format as momentorFormat } from 'Common/Momentor'; +import { timestampToString } from 'Common/Momentor'; import { MessageFlagsCache, setFolderHash } from 'Common/Cache'; - -import { HtmlEditor } from 'Common/Html'; +import { Settings } from 'Common/Globals'; import AppStore from 'Stores/User/App'; import SettingsStore from 'Stores/User/Settings'; @@ -46,7 +45,7 @@ import { AskPopupView } from 'View/Popup/Ask'; import { ContactsPopupView } from 'View/Popup/Contacts'; import { ComposeOpenPgpPopupView } from 'View/Popup/ComposeOpenPgp'; -const Settings = rl.settings, +const /** * @param {string} prefix * @param {string} subject @@ -324,7 +323,7 @@ class ComposePopupView extends AbstractViewPopup { }, attachmentsInProcess: value => { - if (this.attachmentsInProcessError() && Array.isNotEmpty(value)) { + if (this.attachmentsInProcessError() && isNonEmptyArray(value)) { this.attachmentsInProcessError(false); } } @@ -395,7 +394,7 @@ class ComposePopupView extends AbstractViewPopup { if (!this.emptyToError() && !this.attachmentsInErrorError() && !this.attachmentsInProcessError()) { if (SettingsStore.replySameFolder()) { if ( - Array.isArray(this.aDraftInfo) && + isArray(this.aDraftInfo) && 3 === this.aDraftInfo.length && null != this.aDraftInfo[2] && this.aDraftInfo[2].length @@ -414,7 +413,7 @@ class ComposePopupView extends AbstractViewPopup { this.sendError(false); this.sending(true); - if (Array.isArray(this.aDraftInfo) && 3 === this.aDraftInfo.length) { + if (isArray(this.aDraftInfo) && 3 === this.aDraftInfo.length) { const flagsCache = MessageFlagsCache.getFor(this.aDraftInfo[2], this.aDraftInfo[1]); if (flagsCache) { if ('forward' === this.aDraftInfo[0]) { @@ -820,7 +819,7 @@ class ComposePopupView extends AbstractViewPopup { * @param {Array} emails */ addEmailsTo(fKoValue, emails) { - if (Array.isNotEmpty(emails)) { + if (isNonEmptyArray(emails)) { const value = fKoValue().trim(), values = emails.map(item => item ? item.toLine(false) : null) .validUnique(); @@ -870,9 +869,9 @@ class ComposePopupView extends AbstractViewPopup { oMessageOrArray = oMessageOrArray || null; if (oMessageOrArray) { message = - Array.isArray(oMessageOrArray) && 1 === oMessageOrArray.length + isArray(oMessageOrArray) && 1 === oMessageOrArray.length ? oMessageOrArray[0] - : !Array.isArray(oMessageOrArray) + : !isArray(oMessageOrArray) ? oMessageOrArray : null; } @@ -890,20 +889,20 @@ class ComposePopupView extends AbstractViewPopup { excludeEmail[identity.email()] = true; } - if (Array.isNotEmpty(aToEmails)) { + if (isNonEmptyArray(aToEmails)) { this.to(this.emailArrayToStringLineHelper(aToEmails)); } - if (Array.isNotEmpty(aCcEmails)) { + if (isNonEmptyArray(aCcEmails)) { this.cc(this.emailArrayToStringLineHelper(aCcEmails)); } - if (Array.isNotEmpty(aBccEmails)) { + if (isNonEmptyArray(aBccEmails)) { this.bcc(this.emailArrayToStringLineHelper(aBccEmails)); } if (lineComposeType && message) { - sDate = momentorFormat(message.dateTimeStampInUTC(), 'FULL'); + sDate = timestampToString(message.dateTimeStampInUTC(), 'FULL'); sSubject = message.subject(); aDraftInfo = message.aDraftInfo; sText = message.bodyAsHTML(); @@ -963,7 +962,7 @@ class ComposePopupView extends AbstractViewPopup { this.subject(sSubject); this.prepareMessageAttachments(message, lineComposeType); - this.aDraftInfo = Array.isNotEmpty(aDraftInfo) && 3 === aDraftInfo.length ? aDraftInfo : null; + this.aDraftInfo = isNonEmptyArray(aDraftInfo) && 3 === aDraftInfo.length ? aDraftInfo : null; this.sInReplyTo = message.sInReplyTo; this.sReferences = message.sReferences; break; @@ -977,7 +976,7 @@ class ComposePopupView extends AbstractViewPopup { this.subject(sSubject); this.prepareMessageAttachments(message, lineComposeType); - this.aDraftInfo = Array.isNotEmpty(aDraftInfo) && 3 === aDraftInfo.length ? aDraftInfo : null; + this.aDraftInfo = isNonEmptyArray(aDraftInfo) && 3 === aDraftInfo.length ? aDraftInfo : null; this.sInReplyTo = message.sInReplyTo; this.sReferences = message.sReferences; break; @@ -1070,7 +1069,7 @@ class ComposePopupView extends AbstractViewPopup { this.setFocusInPopup(); }); - } else if (Array.isNotEmpty(oMessageOrArray)) { + } else if (isNonEmptyArray(oMessageOrArray)) { oMessageOrArray.forEach(item => { this.addMessageAsAttachment(item); }); @@ -1096,7 +1095,7 @@ class ComposePopupView extends AbstractViewPopup { } const downloads = this.getAttachmentsDownloadsForUpload(); - if (Array.isNotEmpty(downloads)) { + if (isNonEmptyArray(downloads)) { Remote.messageUploadAttachments((sResult, oData) => { if (StorageResultType.Success === sResult && oData && oData.Result) { Object.entries(oData.Result).forEach(([tempName, id]) => { diff --git a/dev/View/Popup/ViewOpenPgpKey.js b/dev/View/Popup/ViewOpenPgpKey.js index cf55ad8b8..053e78386 100644 --- a/dev/View/Popup/ViewOpenPgpKey.js +++ b/dev/View/Popup/ViewOpenPgpKey.js @@ -1,5 +1,5 @@ import { KeyState } from 'Common/Enums'; - +import { doc } from 'Common/Globals'; import { AbstractViewPopup } from 'Knoin/AbstractViews'; class ViewOpenPgpKeyPopupView extends AbstractViewPopup { @@ -22,7 +22,7 @@ class ViewOpenPgpKeyPopupView extends AbstractViewPopup { const el = this.keyDom(); if (el) { let sel = getSelection(), - range = document.createRange(); + range = doc.createRange(); sel.removeAllRanges(); range.selectNodeContents(el); sel.addRange(range); diff --git a/dev/View/User/AbstractSystemDropDown.js b/dev/View/User/AbstractSystemDropDown.js index 1bdd49969..44db97f6e 100644 --- a/dev/View/User/AbstractSystemDropDown.js +++ b/dev/View/User/AbstractSystemDropDown.js @@ -11,7 +11,7 @@ import { AbstractViewRight } from 'Knoin/AbstractViews'; import { KeyboardShortcutsHelpPopupView } from 'View/Popup/KeyboardShortcutsHelp'; import { AccountPopupView } from 'View/Popup/Account'; -const Settings = rl.settings; +import { Settings } from 'Common/Globals'; export class AbstractSystemDropDownUserView extends AbstractViewRight { constructor(name) { diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index ea1a290df..2ea37d9c0 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -19,12 +19,12 @@ import Remote from 'Remote/User/Fetch'; import { command, showScreenPopup } from 'Knoin/Knoin'; import { AbstractViewCenter } from 'Knoin/AbstractViews'; +import { Settings } from 'Common/Globals'; import { rootAdmin } from 'Common/Links'; import { LanguagesPopupView } from 'View/Popup/Languages'; -const Settings = rl.settings, - +const LoginSignMeType = { DefaultOff: 0, DefaultOn: 1, diff --git a/dev/View/User/MailBox/FolderList.js b/dev/View/User/MailBox/FolderList.js index 01a19f418..35f3558e4 100644 --- a/dev/View/User/MailBox/FolderList.js +++ b/dev/View/User/MailBox/FolderList.js @@ -2,7 +2,7 @@ import ko from 'ko'; import { Capa, KeyState } from 'Common/Enums'; import { Focused, Layout } from 'Common/EnumsUser'; -import { leftPanelDisabled, moveAction } from 'Common/Globals'; +import { leftPanelDisabled, moveAction, Settings } from 'Common/Globals'; import { mailBox, settings } from 'Common/Links'; import { setFolderHash } from 'Common/Cache'; @@ -18,8 +18,6 @@ import { ComposePopupView } from 'View/Popup/Compose'; import { FolderCreatePopupView } from 'View/Popup/FolderCreate'; import { ContactsPopupView } from 'View/Popup/Contacts'; -const Settings = rl.settings; - class FolderListMailBoxUserView extends AbstractViewLeft { constructor() { super('User/MailBox/FolderList', 'MailFolderList'); diff --git a/dev/View/User/MailBox/MessageList.js b/dev/View/User/MailBox/MessageList.js index fddf5f425..1accd7a7b 100644 --- a/dev/View/User/MailBox/MessageList.js +++ b/dev/View/User/MailBox/MessageList.js @@ -16,7 +16,7 @@ import { import { UNUSED_OPTION_VALUE } from 'Common/Consts'; -import { leftPanelDisabled, moveAction } from 'Common/Globals'; +import { doc, leftPanelDisabled, moveAction, Settings } from 'Common/Globals'; import { computedPaginatorHelper } from 'Common/UtilsUser'; import { File } from 'Common/File'; @@ -49,7 +49,6 @@ import { ComposePopupView } from 'View/Popup/Compose'; import { AdvancedSearchPopupView } from 'View/Popup/AdvancedSearch'; const - Settings = rl.settings, canBeMovedHelper = (self) => self.canBeMoved(), ifvisible = window.ifvisible; @@ -456,7 +455,7 @@ class MessageListMailBoxUserView extends AbstractViewRight { } getDragData(event) { - const item = ko.dataFor(document.elementFromPoint(event.clientX, event.clientY)); + const item = ko.dataFor(doc.elementFromPoint(event.clientX, event.clientY)); item && item.checked && item.checked(true); const uids = MessageStore.messageListCheckedOrSelectedUidsWithSubMails(); item && !uids.includes(item.uid) && uids.push(item.uid); diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 1c4e2a3e1..2f9a2d8b3 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -16,7 +16,7 @@ import { MessageSetAction } from 'Common/EnumsUser'; -import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction } from 'Common/Globals'; +import { $htmlCL, leftPanelDisabled, keyScopeReal, moveAction, Settings } from 'Common/Globals'; import { inFocus } from 'Common/Utils'; import { mailToHelper } from 'Common/UtilsUser'; @@ -43,8 +43,6 @@ import { AbstractViewRight } from 'Knoin/AbstractViews'; import { ComposePopupView } from 'View/Popup/Compose'; -const Settings = rl.settings; - function isTransparent(color) { return 'rgba(0, 0, 0, 0)' === color || 'transparent' === color; } diff --git a/dev/bootstrap.js b/dev/bootstrap.js index 31b8a3145..87ec39f2f 100644 --- a/dev/bootstrap.js +++ b/dev/bootstrap.js @@ -1,4 +1,4 @@ -import { dropdownVisibility } from 'Common/Globals'; +import { doc, dropdownVisibility } from 'Common/Globals'; import * as Enums from 'Common/Enums'; import * as Plugins from 'Common/Plugins'; import { i18n } from 'Common/Translator'; @@ -38,7 +38,6 @@ export default (App) => { rl.addSettingsViewModel = Plugins.addSettingsViewModel; rl.addSettingsViewModelForAdmin = Plugins.addSettingsViewModelForAdmin; - rl.settingsGet = Plugins.mainSettingsGet; rl.pluginSettingsGet = Plugins.settingsGet; rl.pluginRemoteRequest = Plugins.remoteRequest; @@ -140,8 +139,7 @@ export default (App) => { }; window.__APP_BOOT = fErrorCallback => { - const doc = document, - cb = () => setTimeout(() => { + const cb = () => setTimeout(() => { if (rl.TEMPLATES) { doc.getElementById('rl-templates').innerHTML = rl.TEMPLATES; setTimeout(() => App.bootstart(), 10); diff --git a/plugins/snowfall-on-login-screen/js/include.js b/plugins/snowfall-on-login-screen/js/include.js index 7598b9820..5a461d625 100644 --- a/plugins/snowfall-on-login-screen/js/include.js +++ b/plugins/snowfall-on-login-screen/js/include.js @@ -1,18 +1,12 @@ -$(function () { - if (window.snowFall && window.rl && !window.rl.settingsGet('Auth')) - { - var - sUserAgent = (navigator.userAgent || '').toLowerCase(), - bIsiOSDevice = -1 < sUserAgent.indexOf('iphone') || -1 < sUserAgent.indexOf('ipod') || -1 < sUserAgent.indexOf('ipad'), - bIsAndroidDevice = -1 < sUserAgent.indexOf('android') - ; - - if (!bIsiOSDevice && !bIsAndroidDevice) +if (!/iphone|ipod|ipad|android/i.test(navigator.userAgent)) +{ + document.addEventListener('DOMContentLoaded', () => { + if (window.snowFall && window.rl && !rl.settings.get('Auth')) { window.snowFall.snow(document.getElementsByTagName('body'), { shadow: true, round: true, minSize: 2, maxSize: 5 }); } - } -}); \ No newline at end of file + }); +} diff --git a/snappymail/v/0.0.0/app/localization/admin/_source.en.yml b/snappymail/v/0.0.0/app/localization/admin/_source.en.yml index a58621228..b108fda69 100644 --- a/snappymail/v/0.0.0/app/localization/admin/_source.en.yml +++ b/snappymail/v/0.0.0/app/localization/admin/_source.en.yml @@ -179,6 +179,7 @@ en: CANT_GET_MESSAGE_LIST: "Can't get message list" CANT_GET_MESSAGE: "Can't get message" CANT_DELETE_MESSAGE: "Can't delete message" + CANT_COPY_MESSAGE: "Can't copy message" CANT_MOVE_MESSAGE: "Can't move message" CANT_SAVE_MESSAGE: "Can't save message" CANT_SEND_MESSAGE: "Can't send message" diff --git a/snappymail/v/0.0.0/app/localization/admin/de_DE.yml b/snappymail/v/0.0.0/app/localization/admin/de_DE.yml index 71ee58fab..c399cb3eb 100644 --- a/snappymail/v/0.0.0/app/localization/admin/de_DE.yml +++ b/snappymail/v/0.0.0/app/localization/admin/de_DE.yml @@ -175,6 +175,7 @@ de_DE: CANT_GET_MESSAGE_LIST: "Die Nachrichtenliste ist nicht verfügbar" CANT_GET_MESSAGE: "Diese Nachricht ist nicht verfügbar" CANT_DELETE_MESSAGE: "Diese Nachricht kann nicht gelöscht werden" + CANT_COPY_MESSAGE: "Diese Nachricht kann nicht kopiert werden" CANT_MOVE_MESSAGE: "Diese Nachricht kann nicht verschoben werden" CANT_SAVE_MESSAGE: "Diese Nachricht kann nicht gespeichert werden" CANT_SEND_MESSAGE: "Diese Nachricht kann nicht gesendet werden" diff --git a/snappymail/v/0.0.0/app/localization/admin/en_US.yml b/snappymail/v/0.0.0/app/localization/admin/en_US.yml index df84e5ae2..4311f9faa 100644 --- a/snappymail/v/0.0.0/app/localization/admin/en_US.yml +++ b/snappymail/v/0.0.0/app/localization/admin/en_US.yml @@ -176,6 +176,7 @@ en_US: CANT_GET_MESSAGE_LIST: "Can't get message list" CANT_GET_MESSAGE: "Can't get message" CANT_DELETE_MESSAGE: "Can't delete message" + CANT_COPY_MESSAGE: "Can't copy message" CANT_MOVE_MESSAGE: "Can't move message" CANT_SAVE_MESSAGE: "Can't save message" CANT_SEND_MESSAGE: "Can't send message" diff --git a/snappymail/v/0.0.0/app/localization/admin/es_ES.yml b/snappymail/v/0.0.0/app/localization/admin/es_ES.yml index 19fce23a7..444e327ea 100644 --- a/snappymail/v/0.0.0/app/localization/admin/es_ES.yml +++ b/snappymail/v/0.0.0/app/localization/admin/es_ES.yml @@ -176,6 +176,7 @@ es_ES: CANT_GET_MESSAGE_LIST: "No se puede obtener la lista de mensajes" CANT_GET_MESSAGE: "No se puede obtener el mensaje" CANT_DELETE_MESSAGE: "No se puede eliminar el mensaje" + CANT_COPY_MESSAGE: "No se puede copiar el mensaje" CANT_MOVE_MESSAGE: "No se puede mover el mensaje" CANT_SAVE_MESSAGE: "No se puede guardar el mensaje" CANT_SEND_MESSAGE: "No se puede enviar el mensaje" diff --git a/snappymail/v/0.0.0/app/localization/admin/fr_FR.yml b/snappymail/v/0.0.0/app/localization/admin/fr_FR.yml index 0c2727e5e..08f2ef7c1 100644 --- a/snappymail/v/0.0.0/app/localization/admin/fr_FR.yml +++ b/snappymail/v/0.0.0/app/localization/admin/fr_FR.yml @@ -176,6 +176,7 @@ fr_FR: CANT_GET_MESSAGE_LIST: "Impossible d'obtenir la liste des messages" CANT_GET_MESSAGE: "Impossible d'obtenir le message" CANT_DELETE_MESSAGE: "Impossible de supprimer le message" + CANT_COPY_MESSAGE: "Impossible de copier le message" CANT_MOVE_MESSAGE: "Impossible de déplacer le message" CANT_SAVE_MESSAGE: "Impossible d'enregistrer le message" CANT_SEND_MESSAGE: "Impossible d'envoyer le message" diff --git a/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml b/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml index 46f955de8..cc727f7c8 100644 --- a/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml +++ b/snappymail/v/0.0.0/app/localization/admin/nl_NL.yml @@ -175,6 +175,7 @@ nl_NL: CANT_GET_MESSAGE_LIST: "Kan berichtenlijst niet ophalen" CANT_GET_MESSAGE: "Kan bericht niet ophalen" CANT_DELETE_MESSAGE: "Kan bericht niet verwijderen" + CANT_COPY_MESSAGE: "Kan bericht niet kopiëren" CANT_MOVE_MESSAGE: "Kan bericht niet verplaatsen" CANT_SAVE_MESSAGE: "Kan bericht niet opslaan" CANT_SEND_MESSAGE: "Kan bericht niet verzenden" diff --git a/snappymail/v/0.0.0/app/localization/admin/zh_CN.yml b/snappymail/v/0.0.0/app/localization/admin/zh_CN.yml index 449759c93..96fe6178f 100644 --- a/snappymail/v/0.0.0/app/localization/admin/zh_CN.yml +++ b/snappymail/v/0.0.0/app/localization/admin/zh_CN.yml @@ -176,6 +176,7 @@ zh_CN: CANT_GET_MESSAGE_LIST: "无法获取邮件列表" CANT_GET_MESSAGE: "无法获取邮件" CANT_DELETE_MESSAGE: "无法删除邮件" + CANT_COPY_MESSAGE: "无法复制邮件" CANT_MOVE_MESSAGE: "无法移动邮件" CANT_SAVE_MESSAGE: "无法保存邮件" CANT_SEND_MESSAGE: "无法发送邮件" diff --git a/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml b/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml index 0c386ab1d..fe25a4a7d 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/_source.en.yml @@ -523,6 +523,7 @@ en: CANT_GET_MESSAGE_LIST: "Can't get message list" CANT_GET_MESSAGE: "Can't get message" CANT_DELETE_MESSAGE: "Can't delete message" + CANT_COPY_MESSAGE: "Can't copy message" CANT_MOVE_MESSAGE: "Can't move message" CANT_SAVE_MESSAGE: "Can't save message" CANT_SEND_MESSAGE: "Can't send message" diff --git a/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml b/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml index b90148225..8430bbf52 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/de_DE.yml @@ -524,6 +524,7 @@ de_DE: CANT_GET_MESSAGE_LIST: "Die Nachrichtenliste ist nicht verfügbar" CANT_GET_MESSAGE: "Diese Nachricht ist nicht verfügbar" CANT_DELETE_MESSAGE: "Diese Nachricht kann nicht gelöscht werden" + CANT_COPY_MESSAGE: "Diese Nachricht kann nicht kopiert werden" CANT_MOVE_MESSAGE: "Diese Nachricht kann nicht verschoben werden" CANT_SAVE_MESSAGE: "Diese Nachricht kann nicht gespeichert werden" CANT_SEND_MESSAGE: "Diese Nachricht kann nicht gesendet werden" diff --git a/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml b/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml index 98d310644..f3edc2d0c 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/en_GB.yml @@ -523,6 +523,7 @@ en_GB: CANT_GET_MESSAGE_LIST: "Can't get message list" CANT_GET_MESSAGE: "Can't get message" CANT_DELETE_MESSAGE: "Can't delete message" + CANT_COPY_MESSAGE: "Can't copy message" CANT_MOVE_MESSAGE: "Can't move message" CANT_SAVE_MESSAGE: "Can't save message" CANT_SEND_MESSAGE: "Can't send message" diff --git a/snappymail/v/0.0.0/app/localization/webmail/en_US.yml b/snappymail/v/0.0.0/app/localization/webmail/en_US.yml index 16ab1fea4..7b0ce4658 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/en_US.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/en_US.yml @@ -523,6 +523,7 @@ en_US: CANT_GET_MESSAGE_LIST: "Can't get message list" CANT_GET_MESSAGE: "Can't get message" CANT_DELETE_MESSAGE: "Can't delete message" + CANT_COPY_MESSAGE: "Can't copy message" CANT_MOVE_MESSAGE: "Can't move message" CANT_SAVE_MESSAGE: "Can't save message" CANT_SEND_MESSAGE: "Can't send message" diff --git a/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml b/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml index 50cfd1648..d655f55ad 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/es_ES.yml @@ -525,6 +525,7 @@ es_ES: CANT_GET_MESSAGE_LIST: "No se puede obtener la lista de mensajes" CANT_GET_MESSAGE: "No se puede obtener el mensaje" CANT_DELETE_MESSAGE: "No se puede eliminar el mensaje" + CANT_COPY_MESSAGE: "No se puede copiar el mensaje" CANT_MOVE_MESSAGE: "No se puede mover el mensaje" CANT_SAVE_MESSAGE: "No se puede guardar el mensaje" CANT_SEND_MESSAGE: "No se puede enviar el mensaje" diff --git a/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml b/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml index 45c77f4c7..7ccf851a1 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/fr_FR.yml @@ -524,6 +524,7 @@ fr_FR: CANT_GET_MESSAGE_LIST: "Impossible d'obtenir la liste des messages" CANT_GET_MESSAGE: "Impossible d'obtenir le message" CANT_DELETE_MESSAGE: "Impossible de supprimer le message" + CANT_COPY_MESSAGE: "Impossible de copier le message" CANT_MOVE_MESSAGE: "Impossible de déplacer le message" CANT_SAVE_MESSAGE: "Impossible d'enregistrer le message" CANT_SEND_MESSAGE: "Impossible d'envoyer le message" diff --git a/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml b/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml index de7cbc72f..208f3fbf0 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/nl_NL.yml @@ -523,6 +523,7 @@ nl_NL: CANT_GET_MESSAGE_LIST: "Berichtenlijst kan niet worden opgehaald" CANT_GET_MESSAGE: "Bericht kan niet worden opgehaald" CANT_DELETE_MESSAGE: "Kan bericht niet verwijderen" + CANT_COPY_MESSAGE: "Kan bericht niet kopiëren" CANT_MOVE_MESSAGE: "Kan bericht niet verplaatsen" CANT_SAVE_MESSAGE: "Kan bericht niet opslaan" CANT_SEND_MESSAGE: "Kan bericht niet verzenden" diff --git a/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml b/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml index b5a3bab15..b924c3ffb 100644 --- a/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml +++ b/snappymail/v/0.0.0/app/localization/webmail/zh_CN.yml @@ -521,6 +521,7 @@ zh_CN: CANT_GET_MESSAGE_LIST: "无法获取邮件列表" CANT_GET_MESSAGE: "无法获取邮件" CANT_DELETE_MESSAGE: "无法删除邮件" + CANT_COPY_MESSAGE: "无法复制邮件" CANT_MOVE_MESSAGE: "无法移动邮件" CANT_SAVE_MESSAGE: "无法保存邮件" CANT_SEND_MESSAGE: "无法发送邮件"