UNPKG

@mlightcad/common

Version:

The common package provides shared utilities and base classes that are used across the RealDWG-Web ecosystem. This package contains fundamental components for color management, event handling, logging, performance monitoring, and file loading operations.

175 lines 6.76 kB
import { AcCmEventManager } from './AcCmEventManager'; import { clone, defaults, has, isEmpty, isEqual } from './AcCmLodashUtils'; /** * This class is used to store attributes of one data model. It has the following benifits. * - Get notification when value of one attributes is changed * - Have one `changed` property to store all of changed values * - Store all of states of one model in one key/value object and make it is easy to serialize/deserialize model and model changes * * Actually implementation of this class is based on class Model in Backbone.js. However, Model class in Backbone * is too heavy. So we implement it again based on source code of class Model in Backbone.js. Morever, we want to * keep our library with less external dependencies and don't want to introduce depenedency on Backbone.js. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any var AcCmObject = /** @class */ (function () { /** * Create one object to store attributes. For performance reason, values of attributes passed to constructor * will not be cloned to `attributes` property. So it means that value of `attributes` property in this object * is just a reference to arguments `attributes` passed to constructor. * @param attributes Input attributes to store in this object */ function AcCmObject(attributes, defaultAttrs) { this.events = { attrChanged: new AcCmEventManager(), modelChanged: new AcCmEventManager() }; this._changing = false; this._previousAttributes = {}; this._pending = false; var attrs = attributes || {}; if (defaultAttrs) { defaults(attrs, defaultAttrs); } this.attributes = attrs; this.changed = {}; } /** * For strongly-typed access to attributes, use the `get` method only privately in public getter properties. * @example * get name(): string { * return super.get("name") * } */ AcCmObject.prototype.get = function (key) { return this.attributes[key]; }; AcCmObject.prototype.set = function (key, val, options) { if (key == null) return this; // Handle both `"key", value` and `{key: value}` -style arguments. var attrs; if (typeof key === 'object') { attrs = key; options = val; } else { attrs = {}; attrs[key] = val; } options || (options = {}); // Extract attributes and options. var unset = options.unset; var silent = options.silent; var changes = []; var changing = this._changing; this._changing = true; if (!changing) { this._previousAttributes = clone(this.attributes); this.changed = {}; } var current = this.attributes; var changed = this.changed; var prev = this._previousAttributes; // For each `set` attribute, update or delete the current value. for (var attr in attrs) { val = attrs[attr]; if (!isEqual(current[attr], val)) changes.push(attr); if (!isEqual(prev[attr], val)) { changed[attr] = val; } else { delete changed[attr]; } unset ? delete current[attr] : (current[attr] = val); } // Trigger all relevant attribute changes. if (!silent) { // @ts-expect-error just keep backbone implementation as is if (changes.length) this._pending = options; for (var i = 0; i < changes.length; i++) { this.events.attrChanged.dispatch({ object: this, attrName: changes[i], attrValue: current[changes[i]], options: options }); } } // You might be wondering why there's a `while` loop here. Changes can // be recursively nested within `"change"` events. if (changing) return this; if (!silent) { while (this._pending) { // @ts-expect-error just keep backbone implementation as is options = this._pending; this._pending = false; this.events.modelChanged.dispatch({ object: this, options: options }); } } this._pending = false; this._changing = false; return this; }; AcCmObject.prototype.has = function (key) { return this.get(key) != null; }; /** * Determine if the model has changed since the last `"change"` event. * If you specify an attribute name, determine if that attribute has changed. */ AcCmObject.prototype.hasChanged = function (key) { if (key == null) return !isEmpty(this.changed); return has(this.changed, key); }; /** * Return an object containing all the attributes that have changed. Useful for determining what parts * of a view need to be updated and/or what attributes need to be persisted to the server. * * Unset attributes will be set to undefined. You can also pass an attributes object to diff against * the model, determining if there *would be* a change. */ AcCmObject.prototype.changedAttributes = function (diff) { if (!diff) return this.hasChanged() ? clone(this.changed) : {}; var old = this._changing ? this._previousAttributes : this.attributes; var changed = {}; for (var attr in diff) { var val = diff[attr]; if (isEqual(old[attr], val)) continue; changed[attr] = val; } return changed; }; /** * Get the previous value of an attribute, recorded at the time the last `"change"` event was fired. */ AcCmObject.prototype.previous = function (key) { if (key == null || !this._previousAttributes) return null; return this._previousAttributes[key]; }; /** * Get all of the attributes of the model at the time of the previous `"change"` event. */ AcCmObject.prototype.previousAttributes = function () { return clone(this._previousAttributes); }; /** * Create a new model with identical attributes to this one. */ AcCmObject.prototype.clone = function () { var attrs = clone(this.attributes); return new AcCmObject(attrs); }; return AcCmObject; }()); export { AcCmObject }; //# sourceMappingURL=AcCmObject.js.map