From 97a73c66399f4831240b0541afc8f458d2dbb43a Mon Sep 17 00:00:00 2001 From: djmaze Date: Tue, 18 Aug 2020 20:24:17 +0200 Subject: [PATCH] Replace timeOutAction() with debounce Replace delegateRun() Revert my throttle/debounce setTimeout() to Function.prototype[throttle/debounce] --- README.md | 24 +++--- dev/App/Abstract.js | 30 +++----- dev/App/User.js | 53 +++++-------- dev/Common/ClientStorageDriver/Cookie.js | 2 +- .../ClientStorageDriver/LocalStorage.js | 2 +- dev/Common/HtmlEditor.js | 11 +-- dev/Common/Plugins.js | 4 +- dev/Common/Selector.js | 7 +- dev/Common/Translator.js | 2 +- dev/Common/Utils.js | 57 +++----------- dev/Knoin/AbstractViewNext.js | 4 +- dev/Knoin/Knoin.js | 76 +++++++++---------- dev/Promises/AbstractAjax.js | 4 +- dev/Remote/AbstractAjax.js | 2 +- dev/Screen/AbstractSettings.js | 14 ++-- dev/Settings/User/General.js | 52 +++++-------- dev/Storage/Settings.js | 4 +- dev/Stores/User/Message.js | 36 +++------ dev/View/Popup/AddOpenPgpKey.js | 3 +- dev/View/Popup/Compose.js | 57 ++++---------- dev/View/Popup/Filter.js | 10 +-- dev/View/Popup/FolderSystem.js | 27 +++---- dev/View/Popup/KeyboardShortcutsHelp.js | 44 +++++------ dev/View/Popup/NewOpenPgpKey.js | 4 +- dev/View/Popup/Plugin.js | 16 +--- dev/View/User/MailBox/MessageView.js | 17 ++--- dev/prototype-function.js | 34 +++++++++ tasks/config.js | 1 + 28 files changed, 225 insertions(+), 372 deletions(-) create mode 100644 dev/prototype-function.js diff --git a/README.md b/README.md index 1349e8f8b..227b6c471 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ This fork uses downsized/simplified versions of scripts and has no support for I The result is faster and smaller download code (good for mobile networks). -Things might work in Edge 16-18, Firefox 50-62 and Chrome 54-68 due to one polyfill for array.flat(). +Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill for array.flat(). * Replaced jQuery with jQuery.slim * Replaced ProgressJS with simple native dropin @@ -79,23 +79,23 @@ Things might work in Edge 16-18, Firefox 50-62 and Chrome 54-68 due to one polyf |js/* |1.14.0 |native | |----------- |--------: |--------: | -|admin.js |2.130.942 |1.089.940 | -|app.js |4.184.455 |2.774.461 | -|boot.js | 671.522 | 44.029 | -|libs.js | 647.614 | 316.107 | +|admin.js |2.130.942 |1.082.985 | +|app.js |4.184.455 |2.751.154 | +|boot.js | 671.522 | 43.995 | +|libs.js | 647.614 | 316.876 | |polyfills.js | 325.834 | 0 | -|TOTAL |7.960.367 |4.224.537 | +|TOTAL |7.960.367 |4.195.010 | |js/min/* |1.14.0 |native |gzip 1.14 |gzip | |--------------- |--------: |--------: |--------: |--------: | -|admin.min.js | 252.147 | 148.234 | 73.657 | 42.376 | -|app.min.js | 511.202 | 371.731 |140.462 | 97.432 | -|boot.min.js | 66.007 | 5.589 | 22.567 | 2.332 | -|libs.min.js | 572.545 | 300.211 |176.720 | 92.699 | +|admin.min.js | 252.147 | 147.526 | 73.657 | 42.213 | +|app.min.js | 511.202 | 369.396 |140.462 | 97.130 | +|boot.min.js | 66.007 | 5.579 | 22.567 | 2.328 | +|libs.min.js | 572.545 | 300.520 |176.720 | 92.825 | |polyfills.min.js | 32.452 | 0 | 11.312 | 0 | -|TOTAL |1.434.353 | 825.765 |424.718 |234.839 | +|TOTAL |1.434.353 | 823.015 |424.718 |234.496 | -608.588 bytes (189.879 gzip) is not much, but it feels faster. +611.338 bytes (190.222 gzip) is not much, but it feels faster. |css/* |1.14.0 |native | diff --git a/dev/App/Abstract.js b/dev/App/Abstract.js index 2f530e033..bf2b8304f 100644 --- a/dev/App/Abstract.js +++ b/dev/App/Abstract.js @@ -31,25 +31,18 @@ class AbstractApp extends AbstractBoot { this.isLocalAutocomplete = true; this.lastErrorTime = 0; - var t; addEventListener( 'resize', - ()=>{ - // throttle - if (!t) { - t = setTimeout(()=>{ - const iH = $win.height(), - iW = $win.height(); + (()=>{ + const iH = $win.height(), + iW = $win.height(); - if ($win.__sizes[0] !== iH || $win.__sizes[1] !== iW) { - $win.__sizes[0] = iH; - $win.__sizes[1] = iW; - dispatchEvent(new CustomEvent('resize.real')); - } - t = 0; - }, 50); + if ($win.__sizes[0] !== iH || $win.__sizes[1] !== iW) { + $win.__sizes[0] = iH; + $win.__sizes[1] = iW; + dispatchEvent(new CustomEvent('resize.real')); } - } + }).throttle(50) ); const $doc = document; @@ -64,12 +57,7 @@ class AbstractApp extends AbstractBoot { } }); - var d; - const fn = ()=>{ - // debounce - clearTimeout(d); - d = setTimeout(()=>dispatchEvent(new CustomEvent('rl.auto-logout-refresh')), 5000); - } + const fn = (()=>dispatchEvent(new CustomEvent('rl.auto-logout-refresh'))).debounce(5000); $doc.addEventListener('mousemove', fn); $doc.addEventListener('keypress', fn); diff --git a/dev/App/User.js b/dev/App/User.js index 93adf2f8d..3a9fb177e 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -81,16 +81,14 @@ import { AbstractApp } from 'App/Abstract'; const doc = document; if (!window.ResizeObserver) { - let rot; window.ResizeObserver = class { constructor(callback) { + callback = callback.debounce(250); this.observer = new MutationObserver(mutations => { let i = mutations.length; while (i--) { if ('style' == mutations[i].attributeName) { - // debounce - clearTimeout(rot); - rot = setTimeout(callback, 250); + callback(); break; } } @@ -113,15 +111,11 @@ class AppUser extends AbstractApp { this.moveCache = {}; - let qd, o = this; - this.quotaDebounce = ()=>{ - // debounce - clearTimeout(qd); - qd = setTimeout(o.quota, 30000); - }; - + this.quotaDebounce = this.quota.debounce(30000); this.moveOrDeleteResponseHelper = this.moveOrDeleteResponseHelper.bind(this); + this.messagesMoveTrigger = this.messagesMoveTrigger.debounce(500); + // wakeUp const interval = 3600000; // 60m var lastTime = (new Date()).getTime(); @@ -262,30 +256,25 @@ class AppUser extends AbstractApp { } messagesMoveTrigger() { - // debounce - const o = this; - clearTimeout(o.mt); - o.mt = setTimeout(()=>{ - const sTrashFolder = FolderStore.trashFolder(), - sSpamFolder = FolderStore.spamFolder(); + const sTrashFolder = FolderStore.trashFolder(), + sSpamFolder = FolderStore.spamFolder(); - Object.values(o.moveCache).forEach(item => { - const isSpam = sSpamFolder === item.To, - isTrash = sTrashFolder === item.To, - isHam = !isSpam && sSpamFolder === item.From && getFolderInboxName() === item.To; + Object.values(this.moveCache).forEach(item => { + const isSpam = sSpamFolder === item.To, + isTrash = sTrashFolder === item.To, + isHam = !isSpam && sSpamFolder === item.From && getFolderInboxName() === item.To; - Remote.messagesMove( - o.moveOrDeleteResponseHelper, - item.From, - item.To, - item.Uid, - isSpam ? 'SPAM' : isHam ? 'HAM' : '', - isSpam || isTrash - ); - }); + Remote.messagesMove( + this.moveOrDeleteResponseHelper, + item.From, + item.To, + item.Uid, + isSpam ? 'SPAM' : isHam ? 'HAM' : '', + isSpam || isTrash + ); + }); - o.moveCache = {}; - }, 500); + this.moveCache = {}; } messagesMoveHelper(fromFolderFullNameRaw, toFolderFullNameRaw, uidsForMove) { diff --git a/dev/Common/ClientStorageDriver/Cookie.js b/dev/Common/ClientStorageDriver/Cookie.js index a90702c33..17ce9fac0 100644 --- a/dev/Common/ClientStorageDriver/Cookie.js +++ b/dev/Common/ClientStorageDriver/Cookie.js @@ -145,7 +145,7 @@ class CookieDriver { try { const storageResult = Cookies.getJSON(CLIENT_SIDE_STORAGE_INDEX_NAME); - result = storageResult && undefined !== storageResult[key] ? storageResult[key] : null; + result = storageResult && null != storageResult[key] ? storageResult[key] : null; } catch (e) {} // eslint-disable-line no-empty return result; diff --git a/dev/Common/ClientStorageDriver/LocalStorage.js b/dev/Common/ClientStorageDriver/LocalStorage.js index ddbbd0484..463634013 100644 --- a/dev/Common/ClientStorageDriver/LocalStorage.js +++ b/dev/Common/ClientStorageDriver/LocalStorage.js @@ -47,7 +47,7 @@ class LocalStorageDriver { const storageValue = this.s.getItem(CLIENT_SIDE_STORAGE_INDEX_NAME) || null, storageResult = null === storageValue ? null : JSON.parse(storageValue); - return storageResult && undefined !== storageResult[key] ? storageResult[key] : null; + return storageResult && null != storageResult[key] ? storageResult[key] : null; } catch (e) {} // eslint-disable-line no-empty return null; diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js index 425a47891..3e5af8492 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/HtmlEditor.js @@ -32,16 +32,7 @@ class HtmlEditor { this.element = element; this.$element = jQuery(element); - // throttle - var t, o = this; - this.resize = ()=>{ - if (!t) { - t = setTimeout(()=>{ - o.resizeEditor(); - t = 0; - }, 100); - } - }; + this.resize = this.resizeEditor.throttle(100); this.init(); } diff --git a/dev/Common/Plugins.js b/dev/Common/Plugins.js index 22b28128a..18f65bb67 100644 --- a/dev/Common/Plugins.js +++ b/dev/Common/Plugins.js @@ -88,6 +88,6 @@ export function runSettingsViewModelHooks(admin) { */ export function settingsGet(pluginSection, name) { let plugins = Settings.settingsGet('Plugins'); - plugins = plugins && undefined !== plugins[pluginSection] ? plugins[pluginSection] : null; - return plugins ? (undefined === plugins[name] ? null : plugins[name]) : null; + plugins = plugins && null != plugins[pluginSection] ? plugins[pluginSection] : null; + return plugins ? (null == plugins[name] ? null : plugins[name]) : null; } diff --git a/dev/Common/Selector.js b/dev/Common/Selector.js index 4c4570904..1e7139d16 100644 --- a/dev/Common/Selector.js +++ b/dev/Common/Selector.js @@ -52,12 +52,7 @@ class Selector { this.focusedItem = koFocusedItem || ko.observable(null); this.selectedItem = koSelectedItem || ko.observable(null); - var d, o = this; - this.itemSelectedThrottle = (item)=>{ - // debounce - clearTimeout(d); - d = setTimeout(()=>o.itemSelected(item), 300); - }; + this.itemSelectedThrottle = (item=>this.itemSelected(item)).debounce(300); this.listChecked.subscribe((items) => { if (items.length) { diff --git a/dev/Common/Translator.js b/dev/Common/Translator.js index 32e966771..238d81633 100644 --- a/dev/Common/Translator.js +++ b/dev/Common/Translator.js @@ -94,7 +94,7 @@ export function i18n(key, valueList, defaulValue) { result = undefined === defaulValue ? key : defaulValue; } - if (undefined !== valueList && null !== valueList) { + if (null != valueList) { for (valueName in valueList) { if (Object.prototype.hasOwnProperty.call(valueList, valueName)) { result = result.replace('%' + valueName + '%', valueList[valueName]); diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index bc3601172..fd0d25297 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -124,17 +124,6 @@ export function splitPlainText(text, len = 100) { return prefix + result; } -const timeOutAction = (() => { - const timeOuts = {}; - return (action, fFunction, timeOut) => { - timeOuts[action] = undefined === timeOuts[action] ? 0 : timeOuts[action]; - clearTimeout(timeOuts[action]); - timeOuts[action] = setTimeout(fFunction, timeOut); - }; -})(); - -export { timeOutAction }; - /** * @param {any} m * @returns {any} @@ -258,27 +247,6 @@ export function friendlySize(sizeInBytes) { return sizeInBytes + 'B'; } -/** - * @param {?} object - * @param {string} methodName - * @param {Array=} params - * @param {number=} delay = 0 - */ -export function delegateRun(object, methodName, params, delay = 0) { - if (object && object[methodName]) { - delay = pInt(delay); - params = isArray(params) ? params : []; - - if (0 >= delay) { - object[methodName](...params); - } else { - setTimeout(() => { - object[methodName](...params); - }, delay); - } - } -} - /** * @param {(Object|null|undefined)} context * @param {Function} fExecute @@ -830,14 +798,9 @@ export function selectElement(element) { sel.addRange(range); } -var dv; -export const detectDropdownVisibility = ()=>{ - // leading debounce - clearTimeout(dv); - dv = setTimeout(()=> - dropdownVisibility(!!GlobalsData.aBootstrapDropdowns.find(item => item.hasClass('open'))) - , 50); -}; +export const detectDropdownVisibility = (()=> + dropdownVisibility(!!GlobalsData.aBootstrapDropdowns.find(item => item.hasClass('open'))) +).debounce(50); /** * @param {boolean=} delay = false @@ -881,11 +844,9 @@ export function getConfigurationFromScriptTag(configuration) { export function delegateRunOnDestroy(objectOrObjects) { if (objectOrObjects) { if (isArray(objectOrObjects)) { - objectOrObjects.forEach(item => { - delegateRunOnDestroy(item); - }); - } else if (objectOrObjects && objectOrObjects.onDestroy) { - objectOrObjects.onDestroy(); + objectOrObjects.forEach(item => delegateRunOnDestroy(item)); + } else { + objectOrObjects.onDestroy && objectOrObjects.onDestroy(); } } } @@ -1199,8 +1160,8 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) { to, cc, bcc, - undefined === params.subject ? null : pString(decodeURIComponent(params.subject)), - undefined === params.body ? null : plainToHtml(pString(decodeURIComponent(params.body))) + null == params.subject ? null : pString(decodeURIComponent(params.subject)), + null == params.body ? null : plainToHtml(pString(decodeURIComponent(params.body))) ]); return true; @@ -1212,7 +1173,7 @@ export function mailToHelper(mailToUrl, PopupComposeViewModel) { var wr; export const windowResize = timeout => { clearTimeout(wr); - if (undefined === timeout || null === timeout) { + if (null == timeout) { $win.trigger('resize'); } else { wr = setTimeout(()=>$win.trigger('resize'), timeout); diff --git a/dev/Knoin/AbstractViewNext.js b/dev/Knoin/AbstractViewNext.js index 133bf29b0..3ca90f737 100644 --- a/dev/Knoin/AbstractViewNext.js +++ b/dev/Knoin/AbstractViewNext.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { delegateRun, inFocus } from 'Common/Utils'; +import { inFocus } from 'Common/Utils'; import { KeyState, EventKeyCode } from 'Common/Enums'; import { keyScope } from 'Common/Globals'; @@ -38,7 +38,7 @@ export class AbstractViewNext { addEventListener('keydown', (event) => { if (event && this.modalVisibility && this.modalVisibility()) { if (!this.bDisabeCloseOnEsc && EventKeyCode.Esc === event.keyCode) { - delegateRun(this, 'cancelCommand'); + this.cancelCommand && this.cancelCommand(); return false; } else if (EventKeyCode.Backspace === event.keyCode && !inFocus()) { return false; diff --git a/dev/Knoin/Knoin.js b/dev/Knoin/Knoin.js index ad6f2b69d..8587d6bde 100644 --- a/dev/Knoin/Knoin.js +++ b/dev/Knoin/Knoin.js @@ -3,7 +3,7 @@ import ko from 'ko'; import { runHook } from 'Common/Plugins'; import { $htmlCL, VIEW_MODELS, popupVisibilityNames } from 'Common/Globals'; -import { pString, createCommandLegacy, delegateRun, isNonEmptyArray } from 'Common/Utils'; +import { pString, createCommandLegacy, isNonEmptyArray } from 'Common/Utils'; let currentScreen = null, defaultScreenName = ''; @@ -90,7 +90,7 @@ export function routeOn() { * @returns {?Object} */ export function screen(screenName) { - return screenName && undefined !== SCREENS[screenName] ? SCREENS[screenName] : null; + return screenName && null != SCREENS[screenName] ? SCREENS[screenName] : null; } /** @@ -181,10 +181,10 @@ export function buildViewModel(ViewModelClass, vmScreen) { vm.onShowTrigger(!vm.onShowTrigger()); } - delegateRun(vm, 'onShowWithDelay', [], 500); + vm.onShowWithDelay && setTimeout(()=>vm.onShowWithDelay, 500); } else { - delegateRun(vm, 'onHide'); - delegateRun(vm, 'onHideWithDelay', [], 500); + vm.onHide && vm.onHide(); + vm.onHideWithDelay && setTimeout(()=>vm.onHideWithDelay, 500); if (vm.onHideTrigger) { vm.onHideTrigger(!vm.onHideTrigger()); @@ -213,7 +213,7 @@ export function buildViewModel(ViewModelClass, vmScreen) { vm ); - delegateRun(vm, 'onBuild', [vmDom]); + vm.onBuild && vm.onBuild(vmDom); if (vm && ViewType.Popup === position) { vm.registerPopupKeyDown(); } @@ -238,11 +238,13 @@ export function showScreenPopup(ViewModelClassToShow, params = []) { buildViewModel(ModalView); if (ModalView.__vm && ModalView.__dom) { - delegateRun(ModalView.__vm, 'onBeforeShow', params || []); + params = params || []; + + ModalView.__vm.onBeforeShow && ModalView.__vm.onBeforeShow(...params); ModalView.__vm.modalVisibility(true); - delegateRun(ModalView.__vm, 'onShow', params || []); + ModalView.__vm.onShow && ModalView.__vm.onShow(...params); vmRunHook('view-model-on-show', ModalView, params || []); } @@ -259,7 +261,7 @@ export function warmUpScreenPopup(ViewModelClassToShow) { buildViewModel(ModalView); if (ModalView.__vm && ModalView.__dom) { - delegateRun(ModalView.__vm, 'onWarmUp'); + ModalView.__vm.onWarmUp && ModalView.__vm.onWarmUp(); } } } @@ -309,14 +311,14 @@ export function screenOnRoute(screenName, subPart) { }); } - delegateRun(vmScreen, 'onBuild'); + vmScreen.onBuild && vmScreen.onBuild(); } setTimeout(() => { // hide screen if (currentScreen && !isSameScreen) { - delegateRun(currentScreen, 'onHide'); - delegateRun(currentScreen, 'onHideWithDelay', [], 500); + currentScreen.onHide && currentScreen.onHide(); + currentScreen.onHideWithDelay && setTimeout(()=>currentScreen.onHideWithDelay(), 500); if (currentScreen.onHideTrigger) { currentScreen.onHideTrigger(!currentScreen.onHideTrigger()); @@ -332,8 +334,8 @@ export function screenOnRoute(screenName, subPart) { ViewModelClass.__dom.hide(); ViewModelClass.__vm.viewModelVisibility(false); - delegateRun(ViewModelClass.__vm, 'onHide'); - delegateRun(ViewModelClass.__vm, 'onHideWithDelay', [], 500); + ViewModelClass.__vm.onHide && ViewModelClass.__vm.onHide(); + ViewModelClass.__vm.onHideWithDelay && setTimeout(()=>ViewModelClass.__vm.onHideWithDelay(), 500); if (ViewModelClass.__vm.onHideTrigger) { ViewModelClass.__vm.onHideTrigger(!ViewModelClass.__vm.onHideTrigger()); @@ -348,7 +350,7 @@ export function screenOnRoute(screenName, subPart) { // show screen if (currentScreen && !isSameScreen) { - delegateRun(currentScreen, 'onShow'); + currentScreen.onShow && currentScreen.onShow(); if (currentScreen.onShowTrigger) { currentScreen.onShowTrigger(!currentScreen.onShowTrigger()); } @@ -362,17 +364,18 @@ export function screenOnRoute(screenName, subPart) { ViewModelClass.__dom && ViewType.Popup !== ViewModelClass.__vm.viewModelPosition ) { - delegateRun(ViewModelClass.__vm, 'onBeforeShow'); + ViewModelClass.__vm.onBeforeShow && ViewModelClass.__vm.onBeforeShow(); ViewModelClass.__dom.show(); ViewModelClass.__vm.viewModelVisibility(true); - delegateRun(ViewModelClass.__vm, 'onShow'); + ViewModelClass.__vm.onShow && ViewModelClass.__vm.onShow(); if (ViewModelClass.__vm.onShowTrigger) { ViewModelClass.__vm.onShowTrigger(!ViewModelClass.__vm.onShowTrigger()); } - delegateRun(ViewModelClass.__vm, 'onShowWithDelay', [], 200); + ViewModelClass.__vm.onShowWithDelay && setTimeout(()=>ViewModelClass.__vm.onShowWithDelay, 200); + vmRunHook('view-model-on-show', ViewModelClass); } }); @@ -415,7 +418,7 @@ export function startScreens(screensClasses) { vmScreen.__start(); runHook('screen-pre-start', [vmScreen.screenName(), vmScreen]); - delegateRun(vmScreen, 'onStart'); + vmScreen.onStart && vmScreen.onStart(); runHook('screen-post-start', [vmScreen.screenName(), vmScreen]); } }); @@ -526,30 +529,23 @@ function commandDecorator(canExecute = true) { * @returns {Function} */ function settingsMenuKeysHandler($items) { - // throttle - var t; - return (event, handler)=>{ - if (!t) { - t = setTimeout(()=>{ - const up = handler && 'up' === handler.shortcut; + return ((event, handler)=>{ + const up = handler && 'up' === handler.shortcut; - if (event && $items.length) { - let index = $items.index($items.filter('.selected')); - if (up && 0 < index) { - index -= 1; - } else if (!up && index < $items.length - 1) { - index += 1; - } + if (event && $items.length) { + let index = $items.index($items.filter('.selected')); + if (up && 0 < index) { + index -= 1; + } else if (!up && index < $items.length - 1) { + index += 1; + } - const resultHash = $items.eq(index).attr('href'); - if (resultHash) { - setHash(resultHash, false, true); - } - } - t = 0; - }, 200); + const resultHash = $items.eq(index).attr('href'); + if (resultHash) { + setHash(resultHash, false, true); + } } - }; + }).throttle(200); } export { diff --git a/dev/Promises/AbstractAjax.js b/dev/Promises/AbstractAjax.js index 667a78896..87b87fa71 100644 --- a/dev/Promises/AbstractAjax.js +++ b/dev/Promises/AbstractAjax.js @@ -37,7 +37,7 @@ class AbstractAjaxPromises extends AbstractBasicPromises { ajaxRequest(action, isPost, timeOut, params, additionalGetString, fTrigger) { - additionalGetString = undefined === additionalGetString ? '' : pString(additionalGetString); + additionalGetString = pString(additionalGetString); let init = { mode: 'same-origin', @@ -65,7 +65,7 @@ class AbstractAjaxPromises extends AbstractBasicPromises { } }; buildFormData(formData, params); - init.body = (new URLSearchParams(formData)).toString(); + init.body = new URLSearchParams(formData); } runHook('ajax-default-request', [action, params, additionalGetString]); diff --git a/dev/Remote/AbstractAjax.js b/dev/Remote/AbstractAjax.js index d37926904..1c989949c 100644 --- a/dev/Remote/AbstractAjax.js +++ b/dev/Remote/AbstractAjax.js @@ -163,7 +163,7 @@ class AbstractAjaxRemote { } }; buildFormData(formData, params); - init.body = (new URLSearchParams(formData)).toString(); + init.body = new URLSearchParams(formData); } if (window.AbortController) { diff --git a/dev/Screen/AbstractSettings.js b/dev/Screen/AbstractSettings.js index 61b50df3c..27eb79da1 100644 --- a/dev/Screen/AbstractSettings.js +++ b/dev/Screen/AbstractSettings.js @@ -1,7 +1,7 @@ import ko from 'ko'; import { VIEW_MODELS } from 'Common/Globals'; -import { delegateRun, windowResize, pString } from 'Common/Utils'; +import { windowResize, pString } from 'Common/Utils'; import { settings } from 'Common/Links'; import { setHash } from 'Knoin/Knoin'; @@ -94,7 +94,7 @@ class AbstractSettingsScreen extends AbstractScreen { settingsScreen ); - delegateRun(settingsScreen, 'onBuild', [viewModelDom]); + settingsScreen.onBuild && settingsScreen.onBuild(viewModelDom); } else { console.log('Cannot find sub settings view model position: SettingsSubScreen'); } @@ -105,7 +105,7 @@ class AbstractSettingsScreen extends AbstractScreen { setTimeout(() => { // hide if (o.oCurrentSubScreen) { - delegateRun(o.oCurrentSubScreen, 'onHide'); + o.oCurrentSubScreen.onHide && o.oCurrentSubScreen.onHide(); o.oCurrentSubScreen.viewModelDom.hide(); } // -- @@ -114,10 +114,10 @@ class AbstractSettingsScreen extends AbstractScreen { // show if (o.oCurrentSubScreen) { - delegateRun(o.oCurrentSubScreen, 'onBeforeShow'); + o.oCurrentSubScreen.onBeforeShow && o.oCurrentSubScreen.onBeforeShow(); o.oCurrentSubScreen.viewModelDom.show(); - delegateRun(o.oCurrentSubScreen, 'onShow'); - delegateRun(o.oCurrentSubScreen, 'onShowWithDelay', [], 200); + o.oCurrentSubScreen.onShow && o.oCurrentSubScreen.onShow(); + o.oCurrentSubScreen.onShowWithDelay && setTimeout(() => o.oCurrentSubScreen.onShowWithDelay(), 200); o.menu().forEach(item => { item.selected( @@ -141,7 +141,7 @@ class AbstractSettingsScreen extends AbstractScreen { onHide() { if (this.oCurrentSubScreen && this.oCurrentSubScreen.viewModelDom) { - delegateRun(this.oCurrentSubScreen, 'onHide'); + this.oCurrentSubScreen.onHide && this.oCurrentSubScreen.onHide(); this.oCurrentSubScreen.viewModelDom.hide(); } } diff --git a/dev/Settings/User/General.js b/dev/Settings/User/General.js index 1e14fa027..c209b4557 100644 --- a/dev/Settings/User/General.js +++ b/dev/Settings/User/General.js @@ -4,7 +4,7 @@ import { MESSAGES_PER_PAGE_VALUES } from 'Common/Consts'; import { SaveSettingsStep, EditorDefaultType, Layout } from 'Common/Enums'; -import { settingsSaveHelperSimpleFunction, convertLangName, timeOutAction } from 'Common/Utils'; +import { settingsSaveHelperSimpleFunction, convertLangName } from 'Common/Utils'; import { i18n, trigger as translatorTrigger, reload as translatorReload } from 'Common/Translator'; @@ -121,46 +121,28 @@ class GeneralUserSettings { this.useCheckboxesInList.subscribe(Remote.saveSettingsHelper('UseCheckboxesInList', v=>v?'1':'0')); - this.enableDesktopNotification.subscribe((value) => { - timeOutAction( - 'SaveDesktopNotifications', - () => { - Remote.saveSettings(null, { - 'DesktopNotifications': value ? '1' : '0' - }); - }, - 3000 - ); - }); + this.enableDesktopNotification.subscribe((value => + Remote.saveSettings(null, { + 'DesktopNotifications': value ? 1 : 0 + }) + ).debounce(3000)); - this.enableSoundNotification.subscribe((value) => { - timeOutAction( - 'SaveSoundNotification', - () => { - Remote.saveSettings(null, { - 'SoundNotification': value ? '1' : '0' - }); - }, - 3000 - ); - }); + this.enableSoundNotification.subscribe((value => + Remote.saveSettings(null, { + 'SoundNotification': value ? 1 : 0 + }) + ).debounce(3000)); - this.replySameFolder.subscribe((value) => { - timeOutAction( - 'SaveReplySameFolder', - () => { - Remote.saveSettings(null, { - 'ReplySameFolder': value ? '1' : '0' - }); - }, - 3000 - ); - }); + this.replySameFolder.subscribe((value => + Remote.saveSettings(null, { + 'ReplySameFolder': value ? 1 : 0 + }) + ).debounce(3000)); this.useThreads.subscribe((value) => { MessageStore.messageList([]); Remote.saveSettings(null, { - 'UseThreads': value ? '1' : '0' + 'UseThreads': value ? 1 : 0 }); }); diff --git a/dev/Storage/Settings.js b/dev/Storage/Settings.js index 321b1d7f7..0bd59133f 100644 --- a/dev/Storage/Settings.js +++ b/dev/Storage/Settings.js @@ -9,7 +9,7 @@ APP_SETTINGS = null != APP_SETTINGS ? APP_SETTINGS : {}; * @returns {*} */ export function settingsGet(name) { - return undefined === SETTINGS[name] ? null : SETTINGS[name]; + return null == SETTINGS[name] ? null : SETTINGS[name]; } /** @@ -25,7 +25,7 @@ export function settingsSet(name, value) { * @returns {*} */ export function appSettingsGet(name) { - return undefined === APP_SETTINGS[name] ? null : APP_SETTINGS[name]; + return null == APP_SETTINGS[name] ? null : APP_SETTINGS[name]; } /** diff --git a/dev/Stores/User/Message.js b/dev/Stores/User/Message.js index c3e372c1a..4b44c3077 100644 --- a/dev/Stores/User/Message.js +++ b/dev/Stores/User/Message.js @@ -112,16 +112,7 @@ class MessageUserStore { this.onMessageResponse = this.onMessageResponse.bind(this); - // throttle - var t, o = this; - this.purgeMessageBodyCacheThrottle = ()=>{ - if (!t) { - t = setTimeout(()=>{ - o.purgeMessageBodyCache(); - t = 0; - }, 30000); - } - }; + this.purgeMessageBodyCacheThrottle = this.purgeMessageBodyCache.throttle(30000); } computers() { @@ -203,20 +194,15 @@ class MessageUserStore { this.messageListCompleteLoadingThrottleForAnimation(value); }); - var d; this.messageList.subscribe( - (list)=>{ - // debounce - clearTimeout(d); - d = setTimeout(()=>list.forEach(item => { - if (item && item.newForAnimation()) { - item.newForAnimation(false); - } - }), 500); - } + (list=> { + list.forEach(item => + item && item.newForAnimation() && item.newForAnimation(false) + ) + }).debounce(500) ); - this.message.subscribe((message) => { + this.message.subscribe(message => { if (message) { if (Layout.NoPreview === SettingsStore.layout()) { AppStore.focusedState(Focused.MessageView); @@ -229,9 +215,7 @@ class MessageUserStore { } }); - this.messageLoading.subscribe((value) => { - this.messageLoadingThrottle(value); - }); + this.messageLoading.subscribe(value => this.messageLoadingThrottle(value)); this.messagesBodiesDom.subscribe((dom) => { if (dom && !(dom instanceof $)) { @@ -239,7 +223,7 @@ class MessageUserStore { } }); - this.messageListEndFolder.subscribe((folder) => { + this.messageListEndFolder.subscribe(folder => { const message = this.message(); if (message && folder && folder !== message.folderFullNameRaw) { this.message(null); @@ -745,7 +729,7 @@ class MessageUserStore { iCount = pInt(data.Result.MessageResultCount), iOffset = pInt(data.Result.Offset); - const folder = getFolderFromCacheList(null != data.Result.Folder ? data.Result.Folder : ''); + const folder = getFolderFromCacheList(data.Result.Folder); if (folder && !cached) { folder.interval = Date.now() / 1000; diff --git a/dev/View/Popup/AddOpenPgpKey.js b/dev/View/Popup/AddOpenPgpKey.js index f0f289f5a..87d2b4c7f 100644 --- a/dev/View/Popup/AddOpenPgpKey.js +++ b/dev/View/Popup/AddOpenPgpKey.js @@ -1,5 +1,4 @@ import ko from 'ko'; -import { delegateRun } from 'Common/Utils'; import PgpStore from 'Stores/User/Pgp'; @@ -83,7 +82,7 @@ class AddOpenPgpKeyPopupView extends AbstractViewNext { return false; } - delegateRun(this, 'cancelCommand'); + this.cancelCommand && this.cancelCommand(); return true; } diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 901082d56..8a33acada 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -12,7 +12,6 @@ import { } from 'Common/Enums'; import { - delegateRun, isNonEmptyArray, clearBqSwitcher, replySubjectAdd, @@ -85,7 +84,7 @@ class ComposePopupView extends AbstractViewNext { this.sLastFocusedField = 'to'; - this.resizerTrigger = this.resizerTrigger.bind(this); + this.resizerTrigger = this.resizerTrigger.debounce(50).bind(this); this.allowContacts = !!AppStore.contactsIsAllowed(); this.allowFolders = !!Settings.capa(Capa.Folders); @@ -148,17 +147,9 @@ class ComposePopupView extends AbstractViewNext { } }); - this.savedError.subscribe((value) => { - if (!value) { - this.savedErrorDesc(''); - } - }); + this.savedError.subscribe(value => !value && this.savedErrorDesc('')); - this.sendSuccessButSaveError.subscribe((value) => { - if (!value) { - this.savedErrorDesc(''); - } - }); + this.sendSuccessButSaveError.subscribe(value => !value && this.savedErrorDesc('')); this.savedTime = ko.observable(0); this.savedTimeText = ko.computed(() => @@ -297,9 +288,6 @@ class ComposePopupView extends AbstractViewNext { this.canBeSentOrSaved = ko.computed(() => !this.sending() && !this.saving()); - this.sendMessageResponse = this.sendMessageResponse.bind(this); - this.saveMessageResponse = this.saveMessageResponse.bind(this); - setInterval(() => { if ( this.modalVisibility() && @@ -318,20 +306,10 @@ class ComposePopupView extends AbstractViewNext { this.showBcc.subscribe(this.resizerTrigger); this.showReplyTo.subscribe(this.resizerTrigger); - this.onMessageUploadAttachments = this.onMessageUploadAttachments.bind(this); - this.bDisabeCloseOnEsc = true; this.sDefaultKeyScope = KeyState.Compose; - // debounce - var d, fn = this.tryToClosePopup.bind(this); - this.tryToClosePopup = ()=>{ - clearTimeout(d); - d = setTimeout(fn, 200); - }; - - this.emailsSource = this.emailsSource.bind(this); - this.autosaveFunction = this.autosaveFunction.bind(this); + this.tryToClosePopup = this.tryToClosePopup.debounce(200); this.iTimer = 0; } @@ -424,7 +402,7 @@ class ComposePopupView extends AbstractViewNext { setFolderHash(sSentFolder, ''); Remote.sendMessage( - this.sendMessageResponse, + this.sendMessageResponse.bind(this), this.getMessageRequestParams(sSentFolder) ); } @@ -448,7 +426,7 @@ class ComposePopupView extends AbstractViewNext { setFolderHash(FolderStore.draftFolder(), ''); Remote.saveMessage( - this.saveMessageResponse, + this.saveMessageResponse.bind(this), this.getMessageRequestParams(FolderStore.draftFolder()) ); } @@ -517,7 +495,7 @@ class ComposePopupView extends AbstractViewNext { autosaveStart() { clearTimeout(this.iTimer); - this.iTimer = setTimeout(this.autosaveFunction, 60000); + this.iTimer = setTimeout(()=>this.autosaveFunction(), 60000); } autosaveStop() { @@ -525,9 +503,7 @@ class ComposePopupView extends AbstractViewNext { } emailsSource(oData, fResponse) { - getApp().getAutocomplete(oData.term, (aData) => { - fResponse(aData.map(oEmailItem => oEmailItem.toLine(false))); - }); + getApp().getAutocomplete(oData.term, aData => fResponse(aData.map(oEmailItem => oEmailItem.toLine(false)))); } openOpenPgpPopup() { @@ -615,9 +591,7 @@ class ComposePopupView extends AbstractViewNext { if (StorageResultType.Success === statusResult && data && data.Result) { result = true; - if (this.modalVisibility()) { - delegateRun(this, 'closeCommand'); - } + this.modalVisibility() && this.closeCommand && this.closeCommand(); } if (this.modalVisibility() && !result) { @@ -1096,7 +1070,7 @@ class ComposePopupView extends AbstractViewNext { const downloads = this.getAttachmentsDownloadsForUpload(); if (isNonEmptyArray(downloads)) { - Remote.messageUploadAttachments(this.onMessageUploadAttachments, downloads); + Remote.messageUploadAttachments(()=>this.onMessageUploadAttachments(), downloads); } if (identity) { @@ -1147,13 +1121,13 @@ class ComposePopupView extends AbstractViewNext { const PopupsAskViewModel = require('View/Popup/Ask'); if (!isPopupVisible(PopupsAskViewModel) && this.modalVisibility()) { if (this.bSkipNextHide || (this.isEmptyForm() && !this.draftUid())) { - delegateRun(this, 'closeCommand'); + this.closeCommand && this.closeCommand(); } else { showScreenPopup(PopupsAskViewModel, [ i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), () => { if (this.modalVisibility()) { - delegateRun(this, 'closeCommand'); + this.closeCommand && this.closeCommand(); } } ]); @@ -1206,12 +1180,7 @@ class ComposePopupView extends AbstractViewNext { return false; }); - var d, o = this; - addEventListener('resize.real', ()=>{ - // debounce - clearTimeout(d); - d = setTimeout(o.resizerTrigger, 50); - }); + addEventListener('resize.real', this.resizerTrigger); setInterval(() => { if (this.modalVisibility() && this.oEditor) { diff --git a/dev/View/Popup/Filter.js b/dev/View/Popup/Filter.js index 8e9f933f3..fcb7f03d6 100644 --- a/dev/View/Popup/Filter.js +++ b/dev/View/Popup/Filter.js @@ -2,7 +2,7 @@ import ko from 'ko'; import { FiltersAction, FilterConditionField, FilterConditionType } from 'Common/Enums'; import { bMobileDevice } from 'Common/Globals'; -import { defautOptionsAfterRender, delegateRun } from 'Common/Utils'; +import { defautOptionsAfterRender } from 'Common/Utils'; import { i18n, initOnStartOrLangChange } from 'Common/Translator'; import FilterStore from 'Stores/User/Filter'; @@ -59,13 +59,9 @@ class FilterPopupView extends AbstractViewNext { return false; } - if (this.fTrueCallback) { - this.fTrueCallback(this.filter()); - } + this.fTrueCallback && this.fTrueCallback(this.filter()); - if (this.modalVisibility()) { - delegateRun(this, 'closeCommand'); - } + this.modalVisibility() && this.closeCommand && this.closeCommand(); } return true; diff --git a/dev/View/Popup/FolderSystem.js b/dev/View/Popup/FolderSystem.js index 787cd5e4b..9770b36b3 100644 --- a/dev/View/Popup/FolderSystem.js +++ b/dev/View/Popup/FolderSystem.js @@ -55,7 +55,6 @@ class FolderSystemPopupView extends AbstractViewNext { this.trashFolder = FolderStore.trashFolder; this.archiveFolder = FolderStore.archiveFolder; - var d; const fSetSystemFolders = () => { Settings.settingsSet('SentFolder', FolderStore.sentFolder()); Settings.settingsSet('DraftFolder', FolderStore.draftFolder()); @@ -63,21 +62,17 @@ class FolderSystemPopupView extends AbstractViewNext { Settings.settingsSet('TrashFolder', FolderStore.trashFolder()); Settings.settingsSet('ArchiveFolder', FolderStore.archiveFolder()); }, - fSaveSystemFolders = ()=>{ - // debounce - clearTimeout(d); - d = setTimeout(()=>{ - fSetSystemFolders(); - Remote.saveSystemFolders(()=>{}, { - SentFolder: FolderStore.sentFolder(), - DraftFolder: FolderStore.draftFolder(), - SpamFolder: FolderStore.spamFolder(), - TrashFolder: FolderStore.trashFolder(), - ArchiveFolder: FolderStore.archiveFolder(), - NullFolder: 'NullFolder' - }); - }, 1000); - }, + fSaveSystemFolders = (()=>{ + fSetSystemFolders(); + Remote.saveSystemFolders(()=>{}, { + SentFolder: FolderStore.sentFolder(), + DraftFolder: FolderStore.draftFolder(), + SpamFolder: FolderStore.spamFolder(), + TrashFolder: FolderStore.trashFolder(), + ArchiveFolder: FolderStore.archiveFolder(), + NullFolder: 'NullFolder' + }); + }).debounce(1000), fCallback = () => { fSetSystemFolders(); fSaveSystemFolders(); diff --git a/dev/View/Popup/KeyboardShortcutsHelp.js b/dev/View/Popup/KeyboardShortcutsHelp.js index 64d87de6b..ff4dd0a17 100644 --- a/dev/View/Popup/KeyboardShortcutsHelp.js +++ b/dev/View/Popup/KeyboardShortcutsHelp.js @@ -14,39 +14,29 @@ class KeyboardShortcutsHelpPopupView extends AbstractViewNext { } onBuild(dom) { - var t; key( 'tab, shift+tab, left, right', KeyState.PopupKeyboardShortcutsHelp, - (event, handler)=>{ - // throttle - if (!t) { - t = setTimeout(()=>{ - t = 0; - if (event && handler) { - const $tabs = dom.find('.nav.nav-tabs > li'), - isNext = handler && ('tab' === handler.shortcut || 'right' === handler.shortcut); + ((event, handler)=>{ + if (event && handler) { + const $tabs = dom.find('.nav.nav-tabs > li'), + isNext = handler && ('tab' === handler.shortcut || 'right' === handler.shortcut); - let index = $tabs.index($tabs.filter('.active')); - if (!isNext && 0 < index) { - index -= 1; - } else if (isNext && index < $tabs.length - 1) { - index += 1; - } else { - index = isNext ? 0 : $tabs.length - 1; - } + let index = $tabs.index($tabs.filter('.active')); + if (!isNext && 0 < index) { + index -= 1; + } else if (isNext && index < $tabs.length - 1) { + index += 1; + } else { + index = isNext ? 0 : $tabs.length - 1; + } - $tabs - .eq(index) - .find('a[data-toggle="tab"]') - .tab('show'); - return false; - } - - return true; - }, 100); + $tabs + .eq(index) + .find('a[data-toggle="tab"]') + .tab('show'); } - } + }).throttle(100) ); } } diff --git a/dev/View/Popup/NewOpenPgpKey.js b/dev/View/Popup/NewOpenPgpKey.js index 6333a8597..c166f8068 100644 --- a/dev/View/Popup/NewOpenPgpKey.js +++ b/dev/View/Popup/NewOpenPgpKey.js @@ -1,6 +1,6 @@ import ko from 'ko'; -import { delegateRun, pInt } from 'Common/Utils'; +import { pInt } from 'Common/Utils'; import PgpStore from 'Stores/User/Pgp'; @@ -69,7 +69,7 @@ class NewOpenPgpKeyPopupView extends AbstractViewNext { openpgpKeyring.store(); getApp().reloadOpenPgpKeys(); - delegateRun(this, 'cancelCommand'); + this.cancelCommand && this.cancelCommand(); } }) .catch((e) => { diff --git a/dev/View/Popup/Plugin.js b/dev/View/Popup/Plugin.js index b35544abf..8e34e56cd 100644 --- a/dev/View/Popup/Plugin.js +++ b/dev/View/Popup/Plugin.js @@ -1,7 +1,6 @@ import ko from 'ko'; import { KeyState, StorageResultType, Notification } from 'Common/Enums'; -import { isNonEmptyArray, delegateRun } from 'Common/Utils'; import { getNotification, i18n } from 'Common/Translator'; import Remote from 'Remote/Admin/Ajax'; @@ -41,12 +40,7 @@ class PluginPopupView extends AbstractViewNext { this.bDisabeCloseOnEsc = true; this.sDefaultKeyScope = KeyState.All; - var d, fn = this.tryToClosePopup.bind(this); - this.tryToClosePopup = ()=>{ - // debounce - clearTimeout(d); - d = setTimeout(fn, 200); - }; + this.tryToClosePopup = this.tryToClosePopup.debounce(200); } @command((self) => self.hasConfiguration()) @@ -89,7 +83,7 @@ class PluginPopupView extends AbstractViewNext { this.readme(oPlugin.Readme); const config = oPlugin.Config; - if (isNonEmptyArray(config)) { + if (Array.isArray(config) && config.length) { this.configures( config.map(item => ({ 'value': ko.observable(item[0]), @@ -110,11 +104,7 @@ class PluginPopupView extends AbstractViewNext { if (!isPopupVisible(PopupsAskViewModel)) { showScreenPopup(PopupsAskViewModel, [ i18n('POPUPS_ASK/DESC_WANT_CLOSE_THIS_WINDOW'), - () => { - if (this.modalVisibility()) { - delegateRun(this, 'cancelCommand'); - } - } + () => this.modalVisibility() && this.cancelCommand && this.cancelCommand() ]); } } diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 954e5501d..de200b66e 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -539,20 +539,13 @@ class MessageViewMailBoxUserView extends AbstractViewNext { this.showFullInfo.subscribe(fCheckHeaderHeight); this.message.subscribe(fCheckHeaderHeight); - var t; addEventListener( 'resize', - ()=>{ - // throttle - if (!t) { - t = setTimeout(()=>{ - setTimeout(fCheckHeaderHeight, 1); - setTimeout(fCheckHeaderHeight, 200); - setTimeout(fCheckHeaderHeight, 500); - t = 0; - }, 50); - } - } + (()=>{ + setTimeout(fCheckHeaderHeight, 1); + setTimeout(fCheckHeaderHeight, 200); + setTimeout(fCheckHeaderHeight, 500); + }).throttle(50) ); this.showFullInfo.subscribe((value) => { diff --git a/dev/prototype-function.js b/dev/prototype-function.js new file mode 100644 index 000000000..5dfd80000 --- /dev/null +++ b/dev/prototype-function.js @@ -0,0 +1,34 @@ +/** + * Every time the function is executed, + * it will delay the execution with the given amount of milliseconds. + */ +if (!Function.prototype.debounce) { + Function.prototype.debounce = function(ms) { + let func = this, timer; + return function(...args) { + timer && clearTimeout(timer); + timer = setTimeout(()=>{ + func.apply(this, args) + timer = 0; + }, ms); + }; + }; +} + +/** + * No matter how many times the event is executed, + * the function will be executed only once, after the given amount of milliseconds. + */ +if (!Function.prototype.throttle) { + Function.prototype.throttle = function(ms) { + let func = this, timer; + return function(...args) { + if (!timer) { + timer = setTimeout(()=>{ + func.apply(this, args) + timer = 0; + }, ms); + } + }; + }; +} diff --git a/tasks/config.js b/tasks/config.js index 1c9618dc2..4fda86960 100644 --- a/tasks/config.js +++ b/tasks/config.js @@ -81,6 +81,7 @@ config.paths.js = { 'vendors/qr.js/qr.min.js', // fixed (license) 'vendors/bootstrap/js/bootstrap.min.js', // fixed 'dev/prototype-date.js', + 'dev/prototype-function.js', 'node_modules/knockout/build/output/knockout-latest.js', 'node_modules/knockout-sortable/build/knockout-sortable.min.js ', 'node_modules/simplestatemanager/dist/ssm.min.js',