@phosphor/properties
Version:
PhosphorJS - Attached Properties
191 lines (190 loc) • 6.41 kB
JavaScript
"use strict";
/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2017, PhosphorJS Contributors
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* A class which attaches a value to an external object.
*
* #### Notes
* Attached properties are used to extend the state of an object with
* semantic data from an unrelated class. They also encapsulate value
* creation, coercion, and notification.
*
* Because attached property values are stored in a hash table, which
* in turn is stored in a WeakMap keyed on the owner object, there is
* non-trivial storage overhead involved in their use. The pattern is
* therefore best used for the storage of rare data.
*/
var AttachedProperty = /** @class */ (function () {
/**
* Construct a new attached property.
*
* @param options - The options for initializing the property.
*/
function AttachedProperty(options) {
this._pid = Private.nextPID();
this.name = options.name;
this._create = options.create;
this._coerce = options.coerce || null;
this._compare = options.compare || null;
this._changed = options.changed || null;
}
/**
* Get the current value of the property for a given owner.
*
* @param owner - The property owner of interest.
*
* @returns The current value of the property.
*
* #### Notes
* If the value has not yet been set, the default value will be
* computed and assigned as the current value of the property.
*/
AttachedProperty.prototype.get = function (owner) {
var value;
var map = Private.ensureMap(owner);
if (this._pid in map) {
value = map[this._pid];
}
else {
value = map[this._pid] = this._createValue(owner);
}
return value;
};
/**
* Set the current value of the property for a given owner.
*
* @param owner - The property owner of interest.
*
* @param value - The value for the property.
*
* #### Notes
* If the value has not yet been set, the default value will be
* computed and used as the previous value for the comparison.
*/
AttachedProperty.prototype.set = function (owner, value) {
var oldValue;
var map = Private.ensureMap(owner);
if (this._pid in map) {
oldValue = map[this._pid];
}
else {
oldValue = map[this._pid] = this._createValue(owner);
}
var newValue = this._coerceValue(owner, value);
this._maybeNotify(owner, oldValue, map[this._pid] = newValue);
};
/**
* Explicitly coerce the current property value for a given owner.
*
* @param owner - The property owner of interest.
*
* #### Notes
* If the value has not yet been set, the default value will be
* computed and used as the previous value for the comparison.
*/
AttachedProperty.prototype.coerce = function (owner) {
var oldValue;
var map = Private.ensureMap(owner);
if (this._pid in map) {
oldValue = map[this._pid];
}
else {
oldValue = map[this._pid] = this._createValue(owner);
}
var newValue = this._coerceValue(owner, oldValue);
this._maybeNotify(owner, oldValue, map[this._pid] = newValue);
};
/**
* Get or create the default value for the given owner.
*/
AttachedProperty.prototype._createValue = function (owner) {
var create = this._create;
return create(owner);
};
/**
* Coerce the value for the given owner.
*/
AttachedProperty.prototype._coerceValue = function (owner, value) {
var coerce = this._coerce;
return coerce ? coerce(owner, value) : value;
};
/**
* Compare the old value and new value for equality.
*/
AttachedProperty.prototype._compareValue = function (oldValue, newValue) {
var compare = this._compare;
return compare ? compare(oldValue, newValue) : oldValue === newValue;
};
/**
* Run the change notification if the given values are different.
*/
AttachedProperty.prototype._maybeNotify = function (owner, oldValue, newValue) {
var changed = this._changed;
if (changed && !this._compareValue(oldValue, newValue)) {
changed(owner, oldValue, newValue);
}
};
return AttachedProperty;
}());
exports.AttachedProperty = AttachedProperty;
/**
* The namespace for the `AttachedProperty` class statics.
*/
(function (AttachedProperty) {
/**
* Clear the stored property data for the given owner.
*
* @param owner - The property owner of interest.
*
* #### Notes
* This will clear all property values for the owner, but it will
* **not** run the change notification for any of the properties.
*/
function clearData(owner) {
Private.ownerData.delete(owner);
}
AttachedProperty.clearData = clearData;
})(AttachedProperty = exports.AttachedProperty || (exports.AttachedProperty = {}));
exports.AttachedProperty = AttachedProperty;
/**
* The namespace for the module implementation details.
*/
var Private;
(function (Private) {
/**
* A weak mapping of property owner to property map.
*/
Private.ownerData = new WeakMap();
/**
* A function which computes successive unique property ids.
*/
Private.nextPID = (function () {
var id = 0;
return function () {
var rand = Math.random();
var stem = ("" + rand).slice(2);
return "pid-" + stem + "-" + id++;
};
})();
/**
* Lookup the data map for the property owner.
*
* This will create the map if one does not already exist.
*/
function ensureMap(owner) {
var map = Private.ownerData.get(owner);
if (map) {
return map;
}
map = Object.create(null);
Private.ownerData.set(owner, map);
return map;
}
Private.ensureMap = ensureMap;
})(Private || (Private = {}));