diff options
Diffstat (limited to 'ext/mixed/js/dom-data-binder.js')
-rw-r--r-- | ext/mixed/js/dom-data-binder.js | 234 |
1 files changed, 0 insertions, 234 deletions
diff --git a/ext/mixed/js/dom-data-binder.js b/ext/mixed/js/dom-data-binder.js deleted file mode 100644 index 292b2f67..00000000 --- a/ext/mixed/js/dom-data-binder.js +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2020-2021 Yomichan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -/* global - * SelectorObserver - * TaskAccumulator - */ - -class DOMDataBinder { - constructor({selector, ignoreSelectors=[], createElementMetadata, compareElementMetadata, getValues, setValues, onError=null}) { - this._selector = selector; - this._ignoreSelectors = ignoreSelectors; - this._createElementMetadata = createElementMetadata; - this._compareElementMetadata = compareElementMetadata; - this._getValues = getValues; - this._setValues = setValues; - this._onError = onError; - this._updateTasks = new TaskAccumulator(this._onBulkUpdate.bind(this)); - this._assignTasks = new TaskAccumulator(this._onBulkAssign.bind(this)); - this._selectorObserver = new SelectorObserver({ - selector, - ignoreSelector: (ignoreSelectors.length > 0 ? ignoreSelectors.join(',') : null), - onAdded: this._createObserver.bind(this), - onRemoved: this._removeObserver.bind(this), - onChildrenUpdated: this._onObserverChildrenUpdated.bind(this), - isStale: this._isObserverStale.bind(this) - }); - } - - observe(element) { - this._selectorObserver.observe(element, true); - } - - disconnect() { - this._selectorObserver.disconnect(); - } - - async refresh() { - await this._updateTasks.enqueue(null, {all: true}); - } - - // Private - - async _onBulkUpdate(tasks) { - let all = false; - const targets = []; - for (const [observer, task] of tasks) { - if (observer === null) { - if (task.data.all) { - all = true; - break; - } - } else { - targets.push([observer, task]); - } - } - if (all) { - targets.length = 0; - for (const observer of this._selectorObserver.datas()) { - targets.push([observer, null]); - } - } - - const args = targets.map(([observer]) => ({ - element: observer.element, - metadata: observer.metadata - })); - const responses = await this._getValues(args); - this._applyValues(targets, responses, true); - } - - async _onBulkAssign(tasks) { - const targets = tasks; - const args = targets.map(([observer, task]) => ({ - element: observer.element, - metadata: observer.metadata, - value: task.data.value - })); - const responses = await this._setValues(args); - this._applyValues(targets, responses, false); - } - - _onElementChange(observer) { - const value = this._getElementValue(observer.element); - observer.value = value; - observer.hasValue = true; - this._assignTasks.enqueue(observer, {value}); - } - - _applyValues(targets, response, ignoreStale) { - if (!Array.isArray(response)) { return; } - - for (let i = 0, ii = targets.length; i < ii; ++i) { - const [observer, task] = targets[i]; - const {error, result} = response[i]; - const stale = (task !== null && task.stale); - - if (error) { - if (typeof this._onError === 'function') { - this._onError(error, stale, observer.element, observer.metadata); - } - continue; - } - - if (stale && !ignoreStale) { continue; } - - observer.value = result; - observer.hasValue = true; - this._setElementValue(observer.element, result); - } - } - - _createObserver(element) { - const metadata = this._createElementMetadata(element); - const nodeName = element.nodeName.toUpperCase(); - const observer = { - element, - type: (nodeName === 'INPUT' ? element.type : null), - value: null, - hasValue: false, - onChange: null, - metadata - }; - observer.onChange = this._onElementChange.bind(this, observer); - - element.addEventListener('change', observer.onChange, false); - - this._updateTasks.enqueue(observer); - - return observer; - } - - _removeObserver(element, observer) { - element.removeEventListener('change', observer.onChange, false); - observer.onChange = null; - } - - _onObserverChildrenUpdated(element, observer) { - if (observer.hasValue) { - this._setElementValue(element, observer.value); - } - } - - _isObserverStale(element, observer) { - const {type, metadata} = observer; - const nodeName = element.nodeName.toUpperCase(); - return !( - type === (nodeName === 'INPUT' ? element.type : null) && - this._compareElementMetadata(metadata, this._createElementMetadata(element)) - ); - } - - _setElementValue(element, value) { - switch (element.nodeName.toUpperCase()) { - case 'INPUT': - switch (element.type) { - case 'checkbox': - element.checked = value; - break; - case 'text': - case 'number': - element.value = value; - break; - } - break; - case 'TEXTAREA': - case 'SELECT': - element.value = value; - break; - } - - const event = new CustomEvent('settingChanged', {detail: {value}}); - element.dispatchEvent(event); - } - - _getElementValue(element) { - switch (element.nodeName.toUpperCase()) { - case 'INPUT': - switch (element.type) { - case 'checkbox': - return !!element.checked; - case 'text': - return `${element.value}`; - case 'number': - return DOMDataBinder.convertToNumber(element.value, element); - } - break; - case 'TEXTAREA': - case 'SELECT': - return element.value; - } - return null; - } - - // Utilities - - static convertToNumber(value, constraints) { - value = parseFloat(value); - if (!Number.isFinite(value)) { return 0; } - - let {min, max, step} = constraints; - min = DOMDataBinder.convertToNumberOrNull(min); - max = DOMDataBinder.convertToNumberOrNull(max); - step = DOMDataBinder.convertToNumberOrNull(step); - if (typeof min === 'number') { value = Math.max(value, min); } - if (typeof max === 'number') { value = Math.min(value, max); } - if (typeof step === 'number' && step !== 0) { value = Math.round(value / step) * step; } - return value; - } - - static convertToNumberOrNull(value) { - if (typeof value !== 'number') { - if (typeof value !== 'string' || value.length === 0) { - return null; - } - value = parseFloat(value); - } - return !Number.isNaN(value) ? value : null; - } -} |