summaryrefslogtreecommitdiff
path: root/ext/mixed/js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mixed/js')
-rw-r--r--ext/mixed/js/core.js105
1 files changed, 104 insertions, 1 deletions
diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js
index 9142a846..5bee4670 100644
--- a/ext/mixed/js/core.js
+++ b/ext/mixed/js/core.js
@@ -260,7 +260,7 @@ function promiseTimeout(delay, resolveValue) {
/*
- * Common events
+ * Common classes
*/
class EventDispatcher {
@@ -348,3 +348,106 @@ class EventListenerCollection {
this._eventListeners = [];
}
}
+
+/**
+ * Class representing a generic value with an override stack.
+ * Changes can be observed by listening to the 'change' event.
+ */
+class DynamicProperty extends EventDispatcher {
+ /**
+ * Creates a new instance with the specified value.
+ * @param value The value to assign.
+ */
+ constructor(value) {
+ super();
+ this._value = value;
+ this._defaultValue = value;
+ this._overrides = [];
+ }
+
+ /**
+ * Gets the default value for the property, which is assigned to the
+ * public value property when no overrides are present.
+ */
+ get defaultValue() {
+ return this._defaultValue;
+ }
+
+ /**
+ * Assigns the default value for the property. If no overrides are present
+ * and if the value is different than the current default value,
+ * the 'change' event will be triggered.
+ * @param value The value to assign.
+ */
+ set defaultValue(value) {
+ this._defaultValue = value;
+ if (this._overrides.length === 0) { this._updateValue(); }
+ }
+
+ /**
+ * Gets the current value for the property, taking any overrides into account.
+ */
+ get value() {
+ return this._value;
+ }
+
+ /**
+ * Gets the number of overrides added to the property.
+ */
+ get overrideCount() {
+ return this._overrides.length;
+ }
+
+ /**
+ * Adds an override value with the specified priority to the override stack.
+ * Values with higher priority will take precedence over those with lower.
+ * For tie breaks, the override value added first will take precedence.
+ * If the newly added override has the highest priority of all overrides
+ * and if the override value is different from the current value,
+ * the 'change' event will be fired.
+ * @param value The override value to assign.
+ * @param priority The priority value to use, as a number.
+ * @returns A string token which can be passed to the clearOverride function
+ * to remove the override.
+ */
+ setOverride(value, priority=0) {
+ const overridesCount = this._overrides.length;
+ let i = 0;
+ for (; i < overridesCount; ++i) {
+ if (priority > this._overrides[i].priority) { break; }
+ }
+ const token = generateId(16);
+ this._overrides.splice(i, 0, {value, priority, token});
+ if (i === 0) { this._updateValue(); }
+ return token;
+ }
+
+ /**
+ * Removes a specific override value. If the removed override
+ * had the highest priority, and the new value is different from
+ * the previous value, the 'change' event will be fired.
+ * @param token The token for the corresponding override which is to be removed.
+ * @returns true if an override was returned, false otherwise.
+ */
+ clearOverride(token) {
+ for (let i = 0, ii = this._overrides.length; i < ii; ++i) {
+ if (this._overrides[i].token === token) {
+ this._overrides.splice(i, 1);
+ if (i === 0) { this._updateValue(); }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Updates the current value using the current overrides and default value.
+ * If the new value differs from the previous value, the 'change' event will be fired.
+ */
+ _updateValue() {
+ const value = this._overrides.length > 0 ? this._overrides[0].value : this._defaultValue;
+ if (this._value === value) { return; }
+ this._value = value;
+ this.trigger('change', {value});
+ }
+}