jodit
Version:
Jodit is an awesome and useful wysiwyg editor with filebrowser
145 lines (144 loc) • 4.55 kB
JavaScript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { getPropertyDescriptor } from "../decorators/watch/watch.js";
import { isArray } from "../helpers/checker/is-array.js";
import { isFastEqual } from "../helpers/checker/is-equal.js";
import { isPlainObject } from "../helpers/checker/is-plain-object.js";
const OBSERVABLE_OBJECT = Symbol('observable-object');
function isObservableObject(obj) {
return obj[OBSERVABLE_OBJECT] !== undefined;
}
/**
* Makes any object an observable object
* @example
* ```js
* const obj = {
* a: 1,
* b: {
* c: 5
* }
* }
*
* const obsObj = Jodit.modules.observable(obj);
* console.log(obj === obsObj); // true
* obsObj.on('change', () => {
* console.log('Object changed');
* });
* obsObj.on('change.a', () => {
* console.log('Key a changed');
* });
* obsObj.on('change.b.c', () => {
* console.log('Key b.c changed');
* });
*
* obj.a = 6;
* // Object changed
* // Key a changed
*
* obj.b = {c: 6}
* // Object changed
*
* obj.b.c = 8
* // Object changed
* // Key b.c changed
* ```
*/
export function observable(obj) {
if (isObservableObject(obj)) {
return obj;
}
const __lockEvent = {};
const __onEvents = {};
const on = (event, callback) => {
if (isArray(event)) {
event.map(e => on(e, callback));
return obj;
}
if (!__onEvents[event]) {
__onEvents[event] = [];
}
__onEvents[event].push(callback);
return obj;
};
const fire = (event, ...attr) => {
if (isArray(event)) {
event.map(e => fire(e, ...attr));
return;
}
try {
if (!__lockEvent[event] && __onEvents[event]) {
__lockEvent[event] = true;
__onEvents[event].forEach(clb => clb.call(obj, ...attr));
}
}
finally {
__lockEvent[event] = false;
}
};
const initAccessors = (dict, prefixes = []) => {
const store = {};
if (isObservableObject(dict)) {
return;
}
Object.defineProperty(dict, OBSERVABLE_OBJECT, {
enumerable: false,
value: true
});
Object.keys(dict).forEach(_key => {
const key = _key;
const prefix = prefixes.concat(key).filter(a => a.length);
store[key] = dict[key];
const descriptor = getPropertyDescriptor(dict, key);
Object.defineProperty(dict, key, {
set: (value) => {
const oldValue = store[key];
if (!isFastEqual(store[key], value)) {
fire([
'beforeChange',
`beforeChange.${prefix.join('.')}`
], key, value);
if (isPlainObject(value)) {
initAccessors(value, prefix);
}
if (descriptor && descriptor.set) {
descriptor.set.call(obj, value);
}
else {
store[key] = value;
}
const sum = [];
fire([
'change',
...prefix.reduce((rs, p) => {
sum.push(p);
rs.push(`change.${sum.join('.')}`);
return rs;
}, [])
], prefix.join('.'), oldValue, (value === null || value === void 0 ? void 0 : value.valueOf)
? value.valueOf()
: value);
}
},
get: () => {
if (descriptor && descriptor.get) {
return descriptor.get.call(obj);
}
return store[key];
},
enumerable: true,
configurable: true
});
if (isPlainObject(store[key])) {
initAccessors(store[key], prefix);
}
});
Object.defineProperty(obj, 'on', {
value: on
});
};
initAccessors(obj);
return obj;
}