Simplifying ignite html repo and adding latest changes.
This commit is contained in:
421
ignite-html.js
Normal file
421
ignite-html.js
Normal file
@ -0,0 +1,421 @@
|
||||
/**
|
||||
* 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.
|
||||
* @ignore
|
||||
*/
|
||||
class IgniteProperty {
|
||||
constructor(val, onChange = null) {
|
||||
this.onChangeCallbacks = [];
|
||||
this.onPushCallbacks = [];
|
||||
this.onPopCallbacks = [];
|
||||
this.onShiftCallbacks = [];
|
||||
this.onUnshiftCallbacks = [];
|
||||
this.onSpliceCallbacks = [];
|
||||
|
||||
this.arrayCallbacks = [];
|
||||
this.reflected = [];
|
||||
this._value = val;
|
||||
this.ignoreValueChange = false;
|
||||
|
||||
//If we were passed an onchange function attach it.
|
||||
if (onChange) {
|
||||
this.attachOnChange(onChange);
|
||||
}
|
||||
|
||||
//Attempt to patch the value if it's a list.
|
||||
this.patchArray();
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.setValue(val, true);
|
||||
}
|
||||
|
||||
setValue(val, reflect) {
|
||||
//If the ignore value change flag is set exit.
|
||||
if (this.ignoreValueChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the old value
|
||||
var old = this._value;
|
||||
|
||||
//Based on the old value, see if we need to convert the new value to match the original type.
|
||||
if (typeof old === typeof true) {
|
||||
val = val != null && val != undefined ? val.toString().toLowerCase().trim() : val;
|
||||
val = val == "true" || val == "1" || val == "yes" || val == "t" || val == "y";
|
||||
} else if (typeof old === typeof 0) {
|
||||
val = Number(val != null && val != undefined ? val.toString().trim() : "0");
|
||||
val = isNaN(val) ? 0 : val;
|
||||
} else if (typeof old === typeof "") {
|
||||
val = val != null && val != undefined ? val.toString() : null;
|
||||
}
|
||||
|
||||
//Set the new value
|
||||
this._value = val;
|
||||
|
||||
//Attempt to patch the value if it's an array.
|
||||
this.patchArray();
|
||||
|
||||
//If we want to reflect the value then bubble it up.
|
||||
if (reflect) {
|
||||
this.ignoreValueChange = true; //Ignore changes incase we are connected to any reflected properties.
|
||||
this.reflected.forEach(reflect => reflect.value = val);
|
||||
this.ignoreValueChange = false;
|
||||
}
|
||||
|
||||
//Invoke any callbacks letting them know the value changed.
|
||||
this.invokeOnChange(old, val);
|
||||
}
|
||||
|
||||
patchArray() {
|
||||
//Disconnect any existing array callbacks
|
||||
if (this.arrayCallbacks.length > 0) {
|
||||
this.arrayCallbacks.forEach(callback => callback.disconnect());
|
||||
this.arrayCallbacks = [];
|
||||
}
|
||||
|
||||
//If our value is an array and it hasn't been patched, then patch it.
|
||||
if (Array.isArray(this._value) && this._value.onPushCallbacks == undefined) {
|
||||
this._value.onPushCallbacks = [];
|
||||
this._value.onPopCallbacks = [];
|
||||
this._value.onShiftCallbacks = [];
|
||||
this._value.onUnshiftCallbacks = [];
|
||||
this._value.onSpliceCallbacks = [];
|
||||
|
||||
this._value.push = function () {
|
||||
var len = Array.prototype.push.apply(this, arguments);
|
||||
this.onPushCallbacks.forEach(callback => callback.invoke(Array.from(arguments)));
|
||||
return len;
|
||||
};
|
||||
|
||||
this._value.pop = function () {
|
||||
var len = Array.prototype.pop.apply(this, arguments);
|
||||
this.onPopCallbacks.forEach(callback => callback.invoke());
|
||||
return len;
|
||||
}
|
||||
|
||||
this._value.unshift = function () {
|
||||
var len = Array.prototype.unshift.apply(this, arguments);
|
||||
this.onUnshiftCallbacks.forEach(callback => callback.invoke(Array.from(arguments)));
|
||||
return len;
|
||||
}
|
||||
|
||||
this._value.shift = function () {
|
||||
var len = Array.prototype.shift.apply(this, arguments);
|
||||
this.onShiftCallbacks.forEach(callback => callback.invoke());
|
||||
return len;
|
||||
};
|
||||
|
||||
this._value.splice = function (start, deleteCount = 0, ...items) {
|
||||
var removed = Array.prototype.splice.apply(this, arguments);
|
||||
this.onSpliceCallbacks.forEach(callback => callback.invoke(start, deleteCount, items));
|
||||
return removed;
|
||||
}
|
||||
|
||||
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.attachOnUnshift = function (func) {
|
||||
var callback = new IgniteCallback(func, detach => this.detachOnUnshift(detach));
|
||||
this.onUnshiftCallbacks.push(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
this._value.attachOnShift = function (func) {
|
||||
var callback = new IgniteCallback(func, detach => this.detachOnShift(detach));
|
||||
this.onShiftCallbacks.push(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
this._value.attachonSplice = function (func) {
|
||||
var callback = new IgniteCallback(func, detach => this.detachonSplice(detach));
|
||||
this.onSpliceCallbacks.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._value.detachOnUnshift = function (callback) {
|
||||
this.onUnshiftCallbacks = this.onUnshiftCallbacks.filter(unshift => unshift != callback);
|
||||
}
|
||||
|
||||
this._value.detachOnShift = function (callback) {
|
||||
this.onShiftCallbacks = this.onShiftCallbacks.filter(shift => shift != callback);
|
||||
}
|
||||
|
||||
this._value.detachonSplice = function (callback) {
|
||||
this.onSpliceCallbacks = this.onSpliceCallbacks.filter(slice => slice != callback);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(this._value) && this._value.onPushCallbacks) {
|
||||
//This array has already been patched but attach to it so we get callbacks.
|
||||
this.arrayCallbacks.push(this._value.attachOnPush((items) => this.invokeOnPush(items)));
|
||||
this.arrayCallbacks.push(this._value.attachOnPop(() => this.invokeOnPop()));
|
||||
this.arrayCallbacks.push(this._value.attachOnShift(() => this.invokeOnShift()));
|
||||
this.arrayCallbacks.push(this._value.attachOnUnshift((items) => this.invokeOnUnshift(items)));
|
||||
this.arrayCallbacks.push(this._value.attachonSplice((start, deleteCount, items) => this.invokeonSplice(start, deleteCount, items)));
|
||||
}
|
||||
}
|
||||
|
||||
invokeOnChange(oldValue, newValue) {
|
||||
//Enter a new rendering context since this event may contain code that expects a new context.
|
||||
IgniteRenderingContext.push();
|
||||
this.onChangeCallbacks.forEach(callback => callback.invoke(oldValue, newValue));
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
invokeOnPush(items) {
|
||||
IgniteRenderingContext.push();
|
||||
this.onPushCallbacks.forEach(callback => callback.invoke(this._value, items));
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
invokeOnPop() {
|
||||
IgniteRenderingContext.push();
|
||||
this.onPopCallbacks.forEach(callback => callback.invoke(this._value));
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
invokeOnShift() {
|
||||
IgniteRenderingContext.push();
|
||||
this.onShiftCallbacks.forEach(callback => callback.invoke(this._value));
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
invokeOnUnshift(items) {
|
||||
IgniteRenderingContext.push();
|
||||
this.onUnshiftCallbacks.forEach(callback => callback.invoke(this._value, items));
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
invokeonSplice(start, deleteCount, items) {
|
||||
IgniteRenderingContext.push();
|
||||
this.onSpliceCallbacks.forEach(callback => callback.invoke(this._value, start, deleteCount, items));
|
||||
IgniteRenderingContext.pop();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
attachOnShift(onShift) {
|
||||
var callback = new IgniteCallback(onShift, detach => this.detachOnShift(detach));
|
||||
this.onShiftCallbacks.push(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
attachOnUnshift(onUnshift) {
|
||||
var callback = new IgniteCallback(onUnshift, detach => this.detachOnUnshift(detach));
|
||||
this.onUnshiftCallbacks.push(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
attachOnSplice(onSplice) {
|
||||
var callback = new IgniteCallback(onSplice, detach => this.detachonSplice(detach));
|
||||
this.onSpliceCallbacks.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);
|
||||
}
|
||||
|
||||
detachOnShift(callback) {
|
||||
this.onShiftCallbacks = this.onShiftCallbacks.filter(shift => shift != callback);
|
||||
}
|
||||
|
||||
detachOnUnshift(callback) {
|
||||
this.onUnshiftCallbacks = this.onUnshiftCallbacks.filter(unshift => unshift != callback);
|
||||
}
|
||||
|
||||
detachonSplice(callback) {
|
||||
this.onSpliceCallbacks = this.onSpliceCallbacks.filter(slice => slice != 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a prototype to help get property values from an array
|
||||
*/
|
||||
Array.prototype.getPropertyValues = function () {
|
||||
var ret = [];
|
||||
this.forEach(prop => {
|
||||
if (prop instanceof IgniteProperty) {
|
||||
ret.push(prop.value);
|
||||
} else {
|
||||
ret.push(prop);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a prototype to help get old property values from an array
|
||||
*/
|
||||
Array.prototype.getOldPropertyValues = function (property, oldValue) {
|
||||
var ret = [];
|
||||
this.forEach(prop => {
|
||||
if (prop == property) {
|
||||
ret.push(oldValue);
|
||||
} else {
|
||||
if (prop instanceof IgniteProperty) {
|
||||
ret.push(prop.value);
|
||||
} else {
|
||||
ret.push(prop);
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* The outline of a ignite callback that can be invoked and disconnected
|
||||
* to help maintain and cleanup callbacks.
|
||||
* @ignore
|
||||
*/
|
||||
class IgniteCallback {
|
||||
constructor(callback, detach) {
|
||||
this.callback = callback;
|
||||
this.detach = detach;
|
||||
}
|
||||
|
||||
invoke(...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.
|
||||
* @ignore
|
||||
*/
|
||||
class IgniteRenderingContext {
|
||||
static enter() {
|
||||
if (!IgniteRenderingContext.RenderCount) {
|
||||
IgniteRenderingContext.RenderCount = 0;
|
||||
}
|
||||
|
||||
IgniteRenderingContext.RenderCount++;
|
||||
}
|
||||
|
||||
static leave() {
|
||||
if (IgniteRenderingContext.RenderCount) {
|
||||
IgniteRenderingContext.RenderCount--;
|
||||
}
|
||||
}
|
||||
|
||||
static push() {
|
||||
if (IgniteRenderingContext.Stack == null) {
|
||||
IgniteRenderingContext.Stack = [];
|
||||
}
|
||||
|
||||
IgniteRenderingContext.Stack.push(IgniteRenderingContext.RenderCount);
|
||||
IgniteRenderingContext.RenderCount = 0;
|
||||
}
|
||||
|
||||
static pop() {
|
||||
if (IgniteRenderingContext.Stack && IgniteRenderingContext.Stack.length > 0) {
|
||||
IgniteRenderingContext.RenderCount = IgniteRenderingContext.Stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
static ready(callback) {
|
||||
//Setup the callbacks if it's not init'd.
|
||||
if (!IgniteRenderingContext.ReadyCallbacks) {
|
||||
IgniteRenderingContext.ReadyCallbacks = [];
|
||||
}
|
||||
|
||||
//Add this ignite callback.
|
||||
IgniteRenderingContext.ReadyCallbacks.push(callback);
|
||||
|
||||
//Clear the existing timer if there is one.
|
||||
if (IgniteRenderingContext.ReadyTimer) {
|
||||
clearTimeout(IgniteRenderingContext.ReadyTimer);
|
||||
}
|
||||
|
||||
//Set a new timeout, it will only run once all elements are ready because
|
||||
//of the way single threaded timers work.
|
||||
IgniteRenderingContext.ReadyTimer = setTimeout(() => {
|
||||
IgniteRenderingContext.ReadyCallbacks.forEach((ready) => ready.invoke());
|
||||
IgniteRenderingContext.ReadyCallbacks = [];
|
||||
IgniteRenderingContext.ReadyTimer = null;
|
||||
}, 1);
|
||||
}
|
||||
|
||||
static get rendering() {
|
||||
if (IgniteRenderingContext.RenderCount && IgniteRenderingContext.RenderCount > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
window.IgniteRenderingContext = IgniteRenderingContext;
|
||||
|
||||
export {
|
||||
IgniteProperty,
|
||||
IgniteRenderingContext,
|
||||
IgniteCallback
|
||||
};
|
Reference in New Issue
Block a user