1 | ;
|
2 | /*-----------------------------------------------------------------------------
|
3 | | Copyright (c) 2014-2017, PhosphorJS Contributors
|
4 | |
|
5 | | Distributed under the terms of the BSD 3-Clause License.
|
6 | |
|
7 | | The full license is in the file LICENSE, distributed with this software.
|
8 | |----------------------------------------------------------------------------*/
|
9 | Object.defineProperty(exports, "__esModule", { value: true });
|
10 | /**
|
11 | * A class which attaches a value to an external object.
|
12 | *
|
13 | * #### Notes
|
14 | * Attached properties are used to extend the state of an object with
|
15 | * semantic data from an unrelated class. They also encapsulate value
|
16 | * creation, coercion, and notification.
|
17 | *
|
18 | * Because attached property values are stored in a hash table, which
|
19 | * in turn is stored in a WeakMap keyed on the owner object, there is
|
20 | * non-trivial storage overhead involved in their use. The pattern is
|
21 | * therefore best used for the storage of rare data.
|
22 | */
|
23 | var AttachedProperty = /** @class */ (function () {
|
24 | /**
|
25 | * Construct a new attached property.
|
26 | *
|
27 | * @param options - The options for initializing the property.
|
28 | */
|
29 | function AttachedProperty(options) {
|
30 | this._pid = Private.nextPID();
|
31 | this.name = options.name;
|
32 | this._create = options.create;
|
33 | this._coerce = options.coerce || null;
|
34 | this._compare = options.compare || null;
|
35 | this._changed = options.changed || null;
|
36 | }
|
37 | /**
|
38 | * Get the current value of the property for a given owner.
|
39 | *
|
40 | * @param owner - The property owner of interest.
|
41 | *
|
42 | * @returns The current value of the property.
|
43 | *
|
44 | * #### Notes
|
45 | * If the value has not yet been set, the default value will be
|
46 | * computed and assigned as the current value of the property.
|
47 | */
|
48 | AttachedProperty.prototype.get = function (owner) {
|
49 | var value;
|
50 | var map = Private.ensureMap(owner);
|
51 | if (this._pid in map) {
|
52 | value = map[this._pid];
|
53 | }
|
54 | else {
|
55 | value = map[this._pid] = this._createValue(owner);
|
56 | }
|
57 | return value;
|
58 | };
|
59 | /**
|
60 | * Set the current value of the property for a given owner.
|
61 | *
|
62 | * @param owner - The property owner of interest.
|
63 | *
|
64 | * @param value - The value for the property.
|
65 | *
|
66 | * #### Notes
|
67 | * If the value has not yet been set, the default value will be
|
68 | * computed and used as the previous value for the comparison.
|
69 | */
|
70 | AttachedProperty.prototype.set = function (owner, value) {
|
71 | var oldValue;
|
72 | var map = Private.ensureMap(owner);
|
73 | if (this._pid in map) {
|
74 | oldValue = map[this._pid];
|
75 | }
|
76 | else {
|
77 | oldValue = map[this._pid] = this._createValue(owner);
|
78 | }
|
79 | var newValue = this._coerceValue(owner, value);
|
80 | this._maybeNotify(owner, oldValue, map[this._pid] = newValue);
|
81 | };
|
82 | /**
|
83 | * Explicitly coerce the current property value for a given owner.
|
84 | *
|
85 | * @param owner - The property owner of interest.
|
86 | *
|
87 | * #### Notes
|
88 | * If the value has not yet been set, the default value will be
|
89 | * computed and used as the previous value for the comparison.
|
90 | */
|
91 | AttachedProperty.prototype.coerce = function (owner) {
|
92 | var oldValue;
|
93 | var map = Private.ensureMap(owner);
|
94 | if (this._pid in map) {
|
95 | oldValue = map[this._pid];
|
96 | }
|
97 | else {
|
98 | oldValue = map[this._pid] = this._createValue(owner);
|
99 | }
|
100 | var newValue = this._coerceValue(owner, oldValue);
|
101 | this._maybeNotify(owner, oldValue, map[this._pid] = newValue);
|
102 | };
|
103 | /**
|
104 | * Get or create the default value for the given owner.
|
105 | */
|
106 | AttachedProperty.prototype._createValue = function (owner) {
|
107 | var create = this._create;
|
108 | return create(owner);
|
109 | };
|
110 | /**
|
111 | * Coerce the value for the given owner.
|
112 | */
|
113 | AttachedProperty.prototype._coerceValue = function (owner, value) {
|
114 | var coerce = this._coerce;
|
115 | return coerce ? coerce(owner, value) : value;
|
116 | };
|
117 | /**
|
118 | * Compare the old value and new value for equality.
|
119 | */
|
120 | AttachedProperty.prototype._compareValue = function (oldValue, newValue) {
|
121 | var compare = this._compare;
|
122 | return compare ? compare(oldValue, newValue) : oldValue === newValue;
|
123 | };
|
124 | /**
|
125 | * Run the change notification if the given values are different.
|
126 | */
|
127 | AttachedProperty.prototype._maybeNotify = function (owner, oldValue, newValue) {
|
128 | var changed = this._changed;
|
129 | if (changed && !this._compareValue(oldValue, newValue)) {
|
130 | changed(owner, oldValue, newValue);
|
131 | }
|
132 | };
|
133 | return AttachedProperty;
|
134 | }());
|
135 | exports.AttachedProperty = AttachedProperty;
|
136 | /**
|
137 | * The namespace for the `AttachedProperty` class statics.
|
138 | */
|
139 | (function (AttachedProperty) {
|
140 | /**
|
141 | * Clear the stored property data for the given owner.
|
142 | *
|
143 | * @param owner - The property owner of interest.
|
144 | *
|
145 | * #### Notes
|
146 | * This will clear all property values for the owner, but it will
|
147 | * **not** run the change notification for any of the properties.
|
148 | */
|
149 | function clearData(owner) {
|
150 | Private.ownerData.delete(owner);
|
151 | }
|
152 | AttachedProperty.clearData = clearData;
|
153 | })(AttachedProperty = exports.AttachedProperty || (exports.AttachedProperty = {}));
|
154 | exports.AttachedProperty = AttachedProperty;
|
155 | /**
|
156 | * The namespace for the module implementation details.
|
157 | */
|
158 | var Private;
|
159 | (function (Private) {
|
160 | /**
|
161 | * A weak mapping of property owner to property map.
|
162 | */
|
163 | Private.ownerData = new WeakMap();
|
164 | /**
|
165 | * A function which computes successive unique property ids.
|
166 | */
|
167 | Private.nextPID = (function () {
|
168 | var id = 0;
|
169 | return function () {
|
170 | var rand = Math.random();
|
171 | var stem = ("" + rand).slice(2);
|
172 | return "pid-" + stem + "-" + id++;
|
173 | };
|
174 | })();
|
175 | /**
|
176 | * Lookup the data map for the property owner.
|
177 | *
|
178 | * This will create the map if one does not already exist.
|
179 | */
|
180 | function ensureMap(owner) {
|
181 | var map = Private.ownerData.get(owner);
|
182 | if (map) {
|
183 | return map;
|
184 | }
|
185 | map = Object.create(null);
|
186 | Private.ownerData.set(owner, map);
|
187 | return map;
|
188 | }
|
189 | Private.ensureMap = ensureMap;
|
190 | })(Private || (Private = {}));
|