diff --git a/README.md b/README.md index 1a1ad0a0d..5ee2fb1c5 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ This fork has the following changes: * Embed boot.js and boot.css into index.html * Ongoing removal of old JavaScript code (things are native these days) * Added modified [Squire](https://github.com/neilj/Squire) HTML editor as replacement for CKEditor +* Split Admin specific JavaScript code from User code ### Removal of old JavaScript @@ -89,23 +90,23 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill |js/* |1.14.0 |native | |----------- |--------: |--------: | -|admin.js |2.130.942 | 935.611 | -|app.js |4.184.455 |2.517.449 | +|admin.js |2.130.942 | 857.772 | +|app.js |4.184.455 |2.507.407 | |boot.js | 671.522 | 5.777 | |libs.js | 647.614 | 327.257 | |polyfills.js | 325.834 | 0 | -|TOTAL |7.960.367 |3.786.094 | +|TOTAL |7.960.367 |3.698.213 | |js/min/* |1.14.0 |native |gzip 1.14 |gzip |brotli | |--------------- |--------: |--------: |--------: |--------: |--------: | -|admin.min.js | 252.147 | 128.146 | 73.657 | 37.448 | 32.137 | -|app.min.js | 511.202 | 344.299 |140.462 | 90.265 | 73.006 | +|admin.min.js | 252.147 | 117.678 | 73.657 | 33.668 | 29.096 | +|app.min.js | 511.202 | 342.012 |140.462 | 89.893 | 72.807 | |boot.min.js | 66.007 | 3.101 | 22.567 | 1.576 | 1.346 | |libs.min.js | 572.545 | 304.000 |176.720 | 94.870 | 83.524 | |polyfills.min.js | 32.452 | 0 | 11.312 | 0 | 0 | -|TOTAL |1.434.353 | 779.546 |424.718 |224.159 |190.013 | +|TOTAL |1.434.353 | 766.791 |424.718 |220.007 |186.773 | -654.807 bytes (200.559 gzip) is not much, but it feels faster. +667.562 bytes (204.711 gzip) is not much, but it feels faster. ### CSS changes @@ -130,8 +131,8 @@ Things might work in Edge 18, Firefox 50-62 and Chrome 54-68 due to one polyfill |css/* |1.14.0 |native |gzip 1.14 |gzip |brotli | |-------------- |-------: |-------: |------: |------: |------: | -|app.css | 340.334 | 253.691 | 46,959 | 36.574 | 30.777 | -|app.min.css | 274.791 | 207.298 | 39.618 | 32.001 | 27.155 | +|app.css | 340.334 | 254.033 | 46,959 | 36.648 | 30.815 | +|app.min.css | 274.791 | 207.558 | 39.618 | 32.050 | 27.234 | |boot.css | | 2.538 | | 837 | 668 | |boot.min.css | | 2.055 | | 732 | 560 | diff --git a/dev/App/Admin.js b/dev/App/Admin.js index e3e185e68..5ae00477b 100644 --- a/dev/App/Admin.js +++ b/dev/App/Admin.js @@ -1,4 +1,4 @@ -import ko from 'ko'; +import ko from 'External/Admin/ko'; import { StorageResultType } from 'Common/Enums'; diff --git a/dev/App/User.js b/dev/App/User.js index 3f0ff3471..16f240bff 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -72,7 +72,10 @@ import { startScreens, showScreenPopup } from 'Knoin/Knoin'; import { AbstractApp } from 'App/Abstract'; -const doc = document, Settings = rl.settings; +require('External/User/ko'); + +const doc = document, + Settings = rl.settings; class AppUser extends AbstractApp { constructor() { diff --git a/dev/External/Admin/ko.js b/dev/External/Admin/ko.js new file mode 100644 index 000000000..b2ba46881 --- /dev/null +++ b/dev/External/Admin/ko.js @@ -0,0 +1,68 @@ +import { SaveSettingsStep } from 'Common/Enums'; + +const ko = window.ko; + +ko.bindingHandlers.saveTrigger = { + init: (element) => { + element.saveTriggerType = element.matches('input[type=text],input[type=email],input[type=password],select,textarea') + ? 'input' : 'custom'; + + if ('custom' === element.saveTriggerType) { + element.append( + '  ', + Element.fromHTML(''), + Element.fromHTML(''), + Element.fromHTML('') + ); + element.classList.add('settings-saved-trigger'); + } else { + element.classList.add('settings-saved-trigger-input'); + } + }, + update: (element, fValueAccessor) => { + const value = parseInt(ko.unwrap(fValueAccessor()),10); + if ('custom' === element.saveTriggerType) { + element.querySelector('.animated').hidden = value !== SaveSettingsStep.Animate; + element.querySelector('.success').hidden = value !== SaveSettingsStep.TrueResult; + element.querySelector('.error').hidden = value !== SaveSettingsStep.FalseResult; + } else if (value !== SaveSettingsStep.Animate) { + element.classList.toggle('success', value === SaveSettingsStep.TrueResult); + element.classList.toggle('error', value === SaveSettingsStep.FalseResult); + } + } +}; + +ko.extenders.idleTrigger = (target) => { + target.trigger = ko.observable(SaveSettingsStep.Idle); + return target; +}; + +ko.extenders.posInterer = (target, defaultVal) => { + const Utils = require('Common/Utils'), + result = ko.computed({ + read: target, + write: (newValue) => { + let val = Utils.pInt(newValue.toString(), defaultVal); + if (0 >= val) { + val = defaultVal; + } + + if (val === target() && '' + val !== '' + newValue) { + target(val + 1); + } + + target(val); + } + }); + + result(target()); + return result; +}; + +// functions + +ko.observable.fn.idleTrigger = function() { + return this.extend({ 'idleTrigger': true }); +}; + +export default ko; diff --git a/dev/External/User/ko.js b/dev/External/User/ko.js new file mode 100644 index 000000000..6da8566f8 --- /dev/null +++ b/dev/External/User/ko.js @@ -0,0 +1,263 @@ +import { bMobileDevice } from 'Common/Globals'; + +const ko = window.ko, + $ = jQuery; + +ko.bindingHandlers.editor = { + init: (element, fValueAccessor) => { + let editor = null; + + const fValue = fValueAccessor(), + HtmlEditor = require('Common/HtmlEditor').default, + fUpdateEditorValue = () => fValue && fValue.__editor && fValue.__editor.setHtmlOrPlain(fValue()), + fUpdateKoValue = () => fValue && fValue.__editor && fValue(fValue.__editor.getDataWithHtmlMark()), + fOnReady = () => { + fValue.__editor = editor; + fUpdateEditorValue(); + }; + + if (ko.isObservable(fValue) && HtmlEditor) { + editor = new HtmlEditor(element, fUpdateKoValue, fOnReady, fUpdateKoValue); + + fValue.__fetchEditorValue = fUpdateKoValue; + + fValue.subscribe(fUpdateEditorValue); + + // ko.utils.domNodeDisposal.addDisposeCallback(element, () => { + // }); + } + } +}; + +let ttn = (element, fValueAccessor) => require('Common/Momentor').timeToNode(element, ko.unwrap(fValueAccessor())); +ko.bindingHandlers.moment = { + init: ttn, + update: ttn +}; + +ko.bindingHandlers.emailsTags = { + init: (element, fValueAccessor, fAllBindingsAccessor) => { + const EmailModel = require('Model/Email').default, + $el = $(element), + fValue = fValueAccessor(), + fAllBindings = fAllBindingsAccessor(), + inputDelimiters = [',', ';', '\n']; + + $el.inputosaurus({ + parseOnBlur: true, + allowDragAndDrop: true, + focusCallback: value => { + if (fValue && fValue.focused) { + fValue.focused(!!value); + } + }, + inputDelimiters: inputDelimiters, + autoCompleteSource: fAllBindings.autoCompleteSource || null, + splitHook: value => { + const v = value.trim(); + if (v && inputDelimiters.includes(v.substr(-1))) { + return EmailModel.splitEmailLine(value); + } + return null; + }, + parseHook: input => + input.map(inputValue => { + const values = EmailModel.parseEmailLine(inputValue); + return values.length ? values : inputValue; + }).flat(Infinity).map( + item => (item.toLine ? [item.toLine(false), item] : [item, null]) + ), + change: event => { + element.EmailsTagsValue = event.target.value; + fValue(event.target.value); + } + }); + + if (fValue && fValue.focused && fValue.focused.subscribe) { + fValue.focused.subscribe((value) => { + $el.inputosaurus(value ? 'focus' : 'blur'); + }); + } + }, + update: (element, fValueAccessor) => { + const value = ko.unwrap(fValueAccessor()); + + if (element.EmailsTagsValue !== value) { + element.value = value; + element.EmailsTagsValue = value; + $(element).inputosaurus('refresh'); + } + } +}; + +ko.bindingHandlers.draggable = { + init: (element, fValueAccessor, fAllBindingsAccessor) => { + const Utils = require('Common/Utils'); + + if (!bMobileDevice) { + const triggerZone = 50, + scrollSpeed = 3, + fAllValueFunc = fAllBindingsAccessor(), + selector = fAllValueFunc ? fAllValueFunc.droppableSelector : '', + droppable = selector ? document.querySelector(selector) : null, + conf = { + distance: 20, + handle: '.dragHandle', + cursorAt: { top: 22, left: 3 }, + refreshPositions: true, + scroll: true, + drag: null, + stop: null, + helper: null + }; + let bcr; + + if (droppable) { + conf.drag = event => { + if (droppable.scrollTopMax) { + clearInterval(droppable.timerScroll); + if (droppable.scrollTop + && bcr.top < event.clientY + && bcr.top + triggerZone > event.clientY) + { + droppable.timerScroll = setInterval(() => droppable.scrollTop -= scrollSpeed, 10); + } + else if (droppable.scrollTop < droppable.scrollTopMax + && bcr.bottom > event.clientY + && bcr.bottom - triggerZone < event.clientY) + { + droppable.timerScroll = setInterval(() => droppable.scrollTop += scrollSpeed, 10); + } + else { + clearInterval(droppable.timerScroll); + } + } + }; + + conf.stop = () => clearInterval(droppable.timerScroll); + } + + conf.helper = event => fValueAccessor()(event && event.target ? ko.dataFor(event.target) : null); + + $(element) + .draggable(conf) + .on('mousedown.koDraggable', () => { + Utils.removeInFocus(); + bcr = droppable ? droppable.getBoundingClientRect() : null; + }); + + ko.utils.domNodeDisposal.addDisposeCallback(element, () => + $(element) + .off('mousedown.koDraggable') + .draggable('destroy') + ); + } + } +}; + +ko.bindingHandlers.droppable = { + init: (element, fValueAccessor, fAllBindingsAccessor) => { + if (!bMobileDevice) { + const fValueFunc = fValueAccessor(), + fAllValueFunc = fAllBindingsAccessor(), + fOverCallback = fAllValueFunc && fAllValueFunc.droppableOver ? fAllValueFunc.droppableOver : null, + fOutCallback = fAllValueFunc && fAllValueFunc.droppableOut ? fAllValueFunc.droppableOut : null, + conf = { + tolerance: 'pointer', + hoverClass: 'droppableHover', + drop: null, + over: null, + out: null + }; + + if (fValueFunc) { + conf.drop = (event, ui) => { + fValueFunc(event, ui); + }; + + if (fOverCallback) { + conf.over = (event, ui) => { + fOverCallback(event, ui); + }; + } + + if (fOutCallback) { + conf.out = (event, ui) => { + fOutCallback(event, ui); + }; + } + + $(element).droppable(conf); + + ko.utils.domNodeDisposal.addDisposeCallback(element, () => { + $(element).droppable('destroy'); + }); + } + } + } +}; + +ko.bindingHandlers.link = { + update: (element, fValueAccessor) => element.href = ko.unwrap(fValueAccessor()) +}; + +ko.bindingHandlers.initDom = { + init: (element, fValueAccessor) => fValueAccessor()(element) +}; + +ko.bindingHandlers.onEsc = { + init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => { + $(element).on('keyup.koOnEsc', (event) => { + if (event && 27 === parseInt(event.keyCode, 10)) { + $(element).trigger('change'); + fValueAccessor().call(viewModel); + } + }); + + ko.utils.domNodeDisposal.addDisposeCallback(element, () => { + $(element).off('keyup.koOnEsc'); + }); + } +}; + +// extenders + +ko.extenders.specialThrottle = (target, option) => { + target.iSpecialThrottleTimeoutValue = require('Common/Utils').pInt(option); + if (0 < target.iSpecialThrottleTimeoutValue) { + target.iSpecialThrottleTimeout = 0; + target.valueForRead = ko.observable(!!target()).extend({ throttle: 10 }); + + return ko.computed({ + read: target.valueForRead, + write: (bValue) => { + if (bValue) { + target.valueForRead(bValue); + } else if (target.valueForRead()) { + clearTimeout(target.iSpecialThrottleTimeout); + target.iSpecialThrottleTimeout = setTimeout(() => { + target.valueForRead(false); + target.iSpecialThrottleTimeout = 0; + }, target.iSpecialThrottleTimeoutValue); + } else { + target.valueForRead(bValue); + } + } + }); + } + + return target; +}; + +// functions + +ko.observable.fn.validateEmail = function() { + this.hasError = ko.observable(false); + + this.subscribe(value => this.hasError(value && !/^[^@\s]+@[^@\s]+$/.test(value))); + + this.valueHasMutated(); + return this; +}; + +export default ko; diff --git a/dev/External/ko.js b/dev/External/ko.js index 0758a7a7d..a10c905b1 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -1,44 +1,17 @@ -import { SaveSettingsStep } from 'Common/Enums'; - const $ = jQuery, doc = document, ko = window.ko, Translator = () => require('Common/Translator'), + Globals = () => require('Common/Globals'), isFunction = v => typeof v === 'function'; -ko.bindingHandlers.editor = { - init: (element, fValueAccessor) => { - let editor = null; - - const fValue = fValueAccessor(), - HtmlEditor = require('Common/HtmlEditor').default, - fUpdateEditorValue = () => fValue && fValue.__editor && fValue.__editor.setHtmlOrPlain(fValue()), - fUpdateKoValue = () => fValue && fValue.__editor && fValue(fValue.__editor.getDataWithHtmlMark()), - fOnReady = () => { - fValue.__editor = editor; - fUpdateEditorValue(); - }; - - if (ko.isObservable(fValue) && HtmlEditor) { - editor = new HtmlEditor(element, fUpdateKoValue, fOnReady, fUpdateKoValue); - - fValue.__fetchEditorValue = fUpdateKoValue; - - fValue.subscribe(fUpdateEditorValue); - - // ko.utils.domNodeDisposal.addDisposeCallback(element, () => { - // }); - } - } -}; - ko.bindingHandlers.tooltip = { init: (element, fValueAccessor) => { const fValue = fValueAccessor(), - Globals = require('Common/Globals'); + Global = Globals(); - if (!Globals.bMobileDevice || 'on' === element.dataset.tooltipMobile) { + if (!Global.bMobileDevice || 'on' === element.dataset.tooltipMobile) { const sValue = !ko.isObservable(fValue) && isFunction(fValue) ? fValue() : ko.unwrap(fValue); if ('off' === element.dataset.tooltipI18n) { @@ -48,17 +21,16 @@ ko.bindingHandlers.tooltip = { Translator().trigger.subscribe(() => element.title = Translator().i18n(sValue) ); - Globals.dropdownVisibility.subscribe(() => + Global.dropdownVisibility.subscribe(() => element.title = Translator().i18n(sValue) ); } } }, update: (element, fValueAccessor) => { - const fValue = fValueAccessor(), - Globals = require('Common/Globals'); + const fValue = fValueAccessor(); - if (!Globals.bMobileDevice || 'on' === element.dataset.tooltipMobile) { + if (!Globals().bMobileDevice || 'on' === element.dataset.tooltipMobile) { const sValue = !ko.isObservable(fValue) && isFunction(fValue) ? fValue() : ko.unwrap(fValue); if (sValue) { element.title = 'off' === element.dataset.tooltipI18n ? sValue : Translator().i18n(sValue); @@ -148,26 +120,9 @@ ko.bindingHandlers.onSpace = { } }; -ko.bindingHandlers.onEsc = { - init: (element, fValueAccessor, fAllBindingsAccessor, viewModel) => { - $(element).on('keyup.koOnEsc', (event) => { - if (event && 27 === parseInt(event.keyCode, 10)) { - $(element).trigger('change'); - fValueAccessor().call(viewModel); - } - }); - - ko.utils.domNodeDisposal.addDisposeCallback(element, () => { - $(element).off('keyup.koOnEsc'); - }); - } -}; - ko.bindingHandlers.modal = { init: (element, fValueAccessor) => { - const Globals = require('Common/Globals'); - - element.classList.toggle('fade', !Globals.bMobileDevice); + element.classList.toggle('fade', !Globals().bMobileDevice); const close = element.querySelector('.close'), click = () => fValueAccessor()(false); close && close.addEventListener('click.koModal', click); @@ -178,12 +133,6 @@ ko.bindingHandlers.modal = { } }; -let ttn = (element, fValueAccessor) => require('Common/Momentor').timeToNode(element, ko.unwrap(fValueAccessor())); -ko.bindingHandlers.moment = { - init: ttn, - update: ttn -}; - ko.bindingHandlers.i18nInit = { init: element => Translator().i18nToNodes(element) }; @@ -195,212 +144,10 @@ ko.bindingHandlers.i18nUpdate = { } }; -ko.bindingHandlers.link = { - update: (element, fValueAccessor) => element.href = ko.unwrap(fValueAccessor()) -}; - ko.bindingHandlers.title = { update: (element, fValueAccessor) => element.title = ko.unwrap(fValueAccessor()) }; -ko.bindingHandlers.initDom = { - init: (element, fValueAccessor) => fValueAccessor()(element) -}; - -ko.bindingHandlers.draggable = { - init: (element, fValueAccessor, fAllBindingsAccessor) => { - const Globals = require('Common/Globals'), - Utils = require('Common/Utils'); - - if (!Globals.bMobileDevice) { - const triggerZone = 50, - scrollSpeed = 3, - fAllValueFunc = fAllBindingsAccessor(), - selector = fAllValueFunc ? fAllValueFunc.droppableSelector : '', - droppable = selector ? doc.querySelector(selector) : null, - conf = { - distance: 20, - handle: '.dragHandle', - cursorAt: { top: 22, left: 3 }, - refreshPositions: true, - scroll: true, - drag: null, - stop: null, - helper: null - }; - let bcr; - - if (droppable) { - conf.drag = event => { - if (droppable.scrollTopMax) { - clearInterval(droppable.timerScroll); - if (droppable.scrollTop - && bcr.top < event.clientY - && bcr.top + triggerZone > event.clientY) - { - droppable.timerScroll = setInterval(() => droppable.scrollTop -= scrollSpeed, 10); - } - else if (droppable.scrollTop < droppable.scrollTopMax - && bcr.bottom > event.clientY - && bcr.bottom - triggerZone < event.clientY) - { - droppable.timerScroll = setInterval(() => droppable.scrollTop += scrollSpeed, 10); - } - else { - clearInterval(droppable.timerScroll); - } - } - }; - - conf.stop = () => clearInterval(droppable.timerScroll); - } - - conf.helper = event => fValueAccessor()(event && event.target ? ko.dataFor(event.target) : null); - - $(element) - .draggable(conf) - .on('mousedown.koDraggable', () => { - Utils.removeInFocus(); - bcr = droppable ? droppable.getBoundingClientRect() : null; - }); - - ko.utils.domNodeDisposal.addDisposeCallback(element, () => - $(element) - .off('mousedown.koDraggable') - .draggable('destroy') - ); - } - } -}; - -ko.bindingHandlers.droppable = { - init: (element, fValueAccessor, fAllBindingsAccessor) => { - const Globals = require('Common/Globals'); - if (!Globals.bMobileDevice) { - const fValueFunc = fValueAccessor(), - fAllValueFunc = fAllBindingsAccessor(), - fOverCallback = fAllValueFunc && fAllValueFunc.droppableOver ? fAllValueFunc.droppableOver : null, - fOutCallback = fAllValueFunc && fAllValueFunc.droppableOut ? fAllValueFunc.droppableOut : null, - conf = { - tolerance: 'pointer', - hoverClass: 'droppableHover', - drop: null, - over: null, - out: null - }; - - if (fValueFunc) { - conf.drop = (event, ui) => { - fValueFunc(event, ui); - }; - - if (fOverCallback) { - conf.over = (event, ui) => { - fOverCallback(event, ui); - }; - } - - if (fOutCallback) { - conf.out = (event, ui) => { - fOutCallback(event, ui); - }; - } - - $(element).droppable(conf); - - ko.utils.domNodeDisposal.addDisposeCallback(element, () => { - $(element).droppable('destroy'); - }); - } - } - } -}; - -ko.bindingHandlers.saveTrigger = { - init: (element) => { - element.saveTriggerType = element.matches('input[type=text],input[type=email],input[type=password],select,textarea') - ? 'input' : 'custom'; - - if ('custom' === element.saveTriggerType) { - element.append( - '  ', - Element.fromHTML(''), - Element.fromHTML(''), - Element.fromHTML('') - ); - element.classList.add('settings-saved-trigger'); - } else { - element.classList.add('settings-saved-trigger-input'); - } - }, - update: (element, fValueAccessor) => { - const value = parseInt(ko.unwrap(fValueAccessor()),10); - if ('custom' === element.saveTriggerType) { - element.querySelector('.animated').hidden = value !== SaveSettingsStep.Animate; - element.querySelector('.success').hidden = value !== SaveSettingsStep.TrueResult; - element.querySelector('.error').hidden = value !== SaveSettingsStep.FalseResult; - } else if (value !== SaveSettingsStep.Animate) { - element.classList.toggle('success', value === SaveSettingsStep.TrueResult); - element.classList.toggle('error', value === SaveSettingsStep.FalseResult); - } - } -}; - -ko.bindingHandlers.emailsTags = { - init: (element, fValueAccessor, fAllBindingsAccessor) => { - const EmailModel = require('Model/Email').default, - $el = $(element), - fValue = fValueAccessor(), - fAllBindings = fAllBindingsAccessor(), - inputDelimiters = [',', ';', '\n']; - - $el.inputosaurus({ - parseOnBlur: true, - allowDragAndDrop: true, - focusCallback: value => { - if (fValue && fValue.focused) { - fValue.focused(!!value); - } - }, - inputDelimiters: inputDelimiters, - autoCompleteSource: fAllBindings.autoCompleteSource || null, - splitHook: value => { - const v = value.trim(); - if (v && inputDelimiters.includes(v.substr(-1))) { - return EmailModel.splitEmailLine(value); - } - return null; - }, - parseHook: input => - input.map(inputValue => { - const values = EmailModel.parseEmailLine(inputValue); - return values.length ? values : inputValue; - }).flat(Infinity).map( - item => (item.toLine ? [item.toLine(false), item] : [item, null]) - ), - change: event => { - element.EmailsTagsValue = event.target.value; - fValue(event.target.value); - } - }); - - if (fValue && fValue.focused && fValue.focused.subscribe) { - fValue.focused.subscribe((value) => { - $el.inputosaurus(value ? 'focus' : 'blur'); - }); - } - }, - update: (element, fValueAccessor) => { - const value = ko.unwrap(fValueAccessor()); - - if (element.EmailsTagsValue !== value) { - element.value = value; - element.EmailsTagsValue = value; - $(element).inputosaurus('refresh'); - } - } -}; - ko.bindingHandlers.command = { init: (element, fValueAccessor, fAllBindingsAccessor, viewModel, bindingContext) => { const command = fValueAccessor(); @@ -455,28 +202,6 @@ ko.bindingHandlers.command = { // extenders -ko.extenders.posInterer = (target, defaultVal) => { - const Utils = require('Common/Utils'), - result = ko.computed({ - read: target, - write: (newValue) => { - let val = Utils.pInt(newValue.toString(), defaultVal); - if (0 >= val) { - val = defaultVal; - } - - if (val === target() && '' + val !== '' + newValue) { - target(val + 1); - } - - target(val); - } - }); - - result(target()); - return result; -}; - ko.extenders.limitedList = (target, limitedList) => { const result = ko .computed({ @@ -522,12 +247,6 @@ ko.extenders.reversible = (target) => { return target; }; -ko.extenders.toggleSubscribe = (target, options) => { - target.subscribe(options[1], options[0], 'beforeChange'); - target.subscribe(options[2], options[0]); - return target; -}; - ko.extenders.toggleSubscribeProperty = (target, options) => { const prop = options[1]; if (prop) { @@ -558,53 +277,8 @@ ko.extenders.falseTimeout = (target, option) => { return target; }; -ko.extenders.specialThrottle = (target, option) => { - target.iSpecialThrottleTimeoutValue = require('Common/Utils').pInt(option); - if (0 < target.iSpecialThrottleTimeoutValue) { - target.iSpecialThrottleTimeout = 0; - target.valueForRead = ko.observable(!!target()).extend({ throttle: 10 }); - - return ko.computed({ - read: target.valueForRead, - write: (bValue) => { - if (bValue) { - target.valueForRead(bValue); - } else if (target.valueForRead()) { - clearTimeout(target.iSpecialThrottleTimeout); - target.iSpecialThrottleTimeout = setTimeout(() => { - target.valueForRead(false); - target.iSpecialThrottleTimeout = 0; - }, target.iSpecialThrottleTimeoutValue); - } else { - target.valueForRead(bValue); - } - } - }); - } - - return target; -}; - -ko.extenders.idleTrigger = (target) => { - target.trigger = ko.observable(SaveSettingsStep.Idle); - return target; -}; - // functions -ko.observable.fn.idleTrigger = function() { - return this.extend({ 'idleTrigger': true }); -}; - -ko.observable.fn.validateEmail = function() { - this.hasError = ko.observable(false); - - this.subscribe(value => this.hasError(value && !/^[^@\s]+@[^@\s]+$/.test(value))); - - this.valueHasMutated(); - return this; -}; - ko.observable.fn.deleteAccessHelper = function() { this.extend({ falseTimeout: 3000 }).extend({ toggleSubscribeProperty: [this, 'deleteAccess'] }); return this; diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 9b98f4111..b916f0d7f 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -45,6 +45,12 @@ import { AbstractViewNext } from 'Knoin/AbstractViewNext'; const Settings = rl.settings; +ko.extenders.toggleSubscribe = (target, options) => { + target.subscribe(options[1], options[0], 'beforeChange'); + target.subscribe(options[2], options[0]); + return target; +}; + @popup({ name: 'View/Popup/Compose', templateID: 'PopupsCompose' diff --git a/dev/View/Popup/Identity.js b/dev/View/Popup/Identity.js index a2bfb3963..a4bc0921b 100644 --- a/dev/View/Popup/Identity.js +++ b/dev/View/Popup/Identity.js @@ -10,6 +10,15 @@ import Remote from 'Remote/User/Fetch'; import { popup, command } from 'Knoin/Knoin'; import { AbstractViewNext } from 'Knoin/AbstractViewNext'; +ko.observable.fn.validateEmail = function() { + this.hasError = ko.observable(false); + + this.subscribe(value => this.hasError(value && !/^[^@\s]+@[^@\s]+$/.test(value))); + + this.valueHasMutated(); + return this; +}; + @popup({ name: 'View/Popup/Identity', templateID: 'PopupsIdentity'