ulogger-server/js/src/observe.js

152 lines
4.6 KiB
JavaScript
Raw Normal View History

2019-11-14 16:12:39 +01:00
/*
* μlogger
*
* Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net)
*
* This 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 <http://www.gnu.org/licenses/>.
*/
/* eslint-disable no-underscore-dangle */
export default class uObserve {
/**
* Observe object's property or all properties if not specified.
* On change call observer function.
* observe(obj, prop, observer) observes given property prop;
* observe(obj, observer) observes all properties of object obj.
* @param {Object} obj
2019-11-19 21:40:31 +01:00
* @param {(string|ObserveCallback)} p1
* @param {ObserveCallback=} p2
2019-11-14 16:12:39 +01:00
*/
static observe(obj, p1, p2) {
if (typeof p2 === 'function') {
this.observeProperty(obj, p1, p2);
} else if (typeof p1 === 'function') {
if (Array.isArray(obj)) {
this.observeArray(obj, p1);
} else {
this.observeRecursive(obj, p1);
}
} else {
throw new Error('Invalid arguments');
}
}
/**
* Observe object's proporty. On change call observer
* @param {Object} obj
* @param {?string} property
2019-11-19 21:40:31 +01:00
* @param {ObserveCallback} observer
2019-11-14 16:12:39 +01:00
*/
static observeProperty(obj, property, observer) {
this.addObserver(obj, observer, property);
if (!obj.hasOwnProperty('_values')) {
Object.defineProperty(obj, '_values', { enumerable: false, configurable: false, value: [] });
}
obj._values[property] = obj[property];
Object.defineProperty(obj, property, {
get: () => obj._values[property],
set: (newValue) => {
if (obj._values[property] !== newValue) {
obj._values[property] = newValue;
2019-11-19 21:40:31 +01:00
console.log(`${property} = ` + (Array.isArray(newValue) && newValue.length ? `[${newValue[0]}, …](${newValue.length})` : newValue));
2019-11-14 16:12:39 +01:00
uObserve.notify(obj._observers[property], newValue);
}
if (Array.isArray(obj[property])) {
this.observeArray(obj[property], observer);
}
}
});
if (Array.isArray(obj[property])) {
this.observeArray(obj[property], observer);
}
}
/**
* Recursively add observer to all properties
* @param {Object} obj
2019-11-19 21:40:31 +01:00
* @param {ObserveCallback} observer
2019-11-14 16:12:39 +01:00
*/
static observeRecursive(obj, observer) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
uObserve.observeProperty(obj, prop, observer);
}
}
}
/**
* Observe array
* @param {Object} arr
2019-11-19 21:40:31 +01:00
* @param {ObserveCallback} observer
2019-11-14 16:12:39 +01:00
*/
static observeArray(arr, observer) {
this.addObserver(arr, observer);
[ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach(
(operation) => {
const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation);
descriptor.value = function () {
const result = Array.prototype[operation].apply(arr, arguments);
2019-11-19 21:40:31 +01:00
console.log(`[${operation}] ` + arr.length ? `[${arr[0]}, …](${arr.length})` : arr);
2019-11-14 16:12:39 +01:00
uObserve.notify(arr._observers, arr);
return result;
};
Object.defineProperty(arr, operation, descriptor);
});
}
/**
* Store observer in object
* @param {Object} obj Object
2019-11-19 21:40:31 +01:00
* @param {ObserveCallback} observer Observer
2019-11-14 16:12:39 +01:00
* @param {string=} property Optional property
*/
static addObserver(obj, observer, property) {
if (!obj.hasOwnProperty('_observers')) {
Object.defineProperty(obj, '_observers', {
enumerable: false,
configurable: false,
value: (arguments.length === 3) ? [] : new Set()
});
}
if (arguments.length === 3) {
if (!obj._observers[property]) {
obj._observers[property] = new Set();
}
obj._observers[property].add(observer);
} else {
obj._observers.add(observer);
}
}
/**
* Notify observers
2019-11-19 21:40:31 +01:00
* @param {Set<ObserveCallback>} observers
2019-11-14 16:12:39 +01:00
* @param {*} value
*/
static notify(observers, value) {
for (const observer of observers) {
(async () => {
await observer(value);
})();
}
}
2019-11-19 21:40:31 +01:00
/**
* Notify callback
* @callback ObserveCallback
* @param {*} value
*/
2019-11-14 16:12:39 +01:00
}