ignite-html/src/ignite-html.js

199 lines
5.9 KiB
JavaScript
Raw Normal View History

/**
* The outline of a ignite property which is a managed property that
* can be used to invoke call back functions when the value of the property changes.
*/
class IgniteProperty {
constructor(val) {
this.onChangeCallbacks = [];
this.onPushCallbacks = [];
this.onPopCallbacks = [];
this.callbacks = [];
this._value = val;
//Attempt to patch the value if it's a list.
this.patchList();
}
get value() {
return this._value;
}
set value(val) {
var old = this._value;
this._value = val;
//Disconnect any existing callbacks
this.callbacks.forEach(callback => callback.disconnect());
this.callbacks = [];
//Attempt to patch the value if it's a list.
this.patchList();
//Invoke any callbacks letting them know the value changed.
this.invokeOnChange(old, val);
}
patchList() {
if (Array.isArray(this._value) && this._value.onPushCallbacks == undefined) {
this._value.onPushCallbacks = [];
this._value.onPopCallbacks = [];
this._value.push = function () {
var len = Array.prototype.push.apply(this, arguments);
this.onPushCallbacks.forEach((callback) => callback.invoke(arguments[0]));
return len;
};
this._value.pop = function () {
var len = Array.prototype.pop.apply(this, arguments);
this.onPopCallbacks.forEach((callback) => callback.invoke());
return len;
}
this._value.attachOnPush = function (func) {
var callback = new IgniteCallback(func, (detach) => this.detachOnPush(detach));
this.onPushCallbacks.push(callback);
return callback;
}
this._value.attachOnPop = function (func) {
var callback = new IgniteCallback(func, (detach) => this.detachOnPop(detach));
this.onPopCallbacks.push(callback);
return callback;
}
this._value.detachOnPush = function (callback) {
this.onPushCallbacks = this.onPushCallbacks.filter(push => push != callback);
}
this._value.detachOnPop = function (callback) {
this.onPopCallbacks = this.onPopCallbacks.filter(pop => pop != callback);
}
this.callbacks.push(this._value.attachOnPush(() => this.invokeOnPush()));
this.callbacks.push(this._value.attachOnPop(() => this.invokeOnPop()));
} else if (Array.isArray(this._value) && this._value.onPushCallbacks) {
this.callbacks.push(this._value.attachOnPush(() => this.invokeOnPush()));
this.callbacks.push(this._value.attachOnPop(() => this.invokeOnPop()));
}
}
invokeOnChange(oldValue, newValue) {
for (var i = 0; i < this.onChangeCallbacks.length; i++) {
this.onChangeCallbacks[i].invoke(oldValue, newValue);
}
}
invokeOnPush() {
for (var i = 0; i < this.onPushCallbacks.length; i++) {
this.onPushCallbacks[i].invoke(null, this._value);
}
}
invokeOnPop() {
for (var i = 0; i < this.onPopCallbacks.length; i++) {
this.onPopCallbacks[i].invoke(null, this._value);
}
}
attachOnChange(onChange) {
var callback = new IgniteCallback(onChange, (detach) => this.detachOnChange(detach));
this.onChangeCallbacks.push(callback);
return callback;
}
attachOnPush(onPush) {
var callback = new IgniteCallback(onPush, (detach) => this.detachOnPush(detach));
this.onPushCallbacks.push(callback);
return callback;
}
attachOnPop(onPop) {
var callback = new IgniteCallback(onPop, (detach) => this.detachOnPop(detach));
this.onPopCallbacks.push(callback);
return callback;
}
detachOnChange(callback) {
this.onChangeCallbacks = this.onChangeCallbacks.filter(change => change != callback);
}
detachOnPush(callback) {
this.onPushCallbacks = this.onPushCallbacks.filter(push => push != callback);
}
detachOnPop(callback) {
this.onPopCallbacks = this.onPopCallbacks.filter(pop => pop != callback);
}
}
/**
* Return the value of the property if we try to convert
* the property to a string.
*/
IgniteProperty.prototype.toString = function () {
return this.value.toString();
}
/**
* The outline of a ignite callback that can be invoked and disconnected
* to help maintain and cleanup callbacks.
*/
class IgniteCallback {
constructor(callback, detach) {
this.callback = callback;
this.detach = detach;
}
invoke(...params) {
console.log("Invoke with params:", params);
if (this.callback) {
this.callback(...params);
}
}
disconnect() {
this.callback = null;
if (this.detach) {
this.detach(this);
}
}
}
/**
* The outline of a simple rendering context which allows us to
* know if we are currently rendering anything ignite related. This works
* because Javascript is single threaded, if events could break the current execution
* this would fail. But it's safe since events cannot do that.
*/
class IgniteRenderingContext {
static enter() {
if (!IgniteRenderingContext.RenderCount) {
IgniteRenderingContext.RenderCount = 0;
}
IgniteRenderingContext.RenderCount++;
}
static leave() {
if (IgniteRenderingContext.RenderCount) {
IgniteRenderingContext.RenderCount--;
}
}
static get rendering() {
if (IgniteRenderingContext.RenderCount && IgniteRenderingContext.RenderCount > 0) {
return true;
}
return false;
}
}
window.IgniteRenderingContext = IgniteRenderingContext;
export {
IgniteProperty
};