Multiple bug fixes and improvements. Shift, Unshift, and Splice are now supported to do inline array modifications without losing the original array reference across elements.
This commit is contained in:
		| @@ -8,6 +8,10 @@ class IgniteProperty { | ||||
|         this.onChangeCallbacks = []; | ||||
|         this.onPushCallbacks = []; | ||||
|         this.onPopCallbacks = []; | ||||
|         this.onShiftCallbacks = []; | ||||
|         this.onUnshiftCallbacks = []; | ||||
|         this.onSpliceCallbacks = []; | ||||
|  | ||||
|         this.arrayCallbacks = []; | ||||
|         this.reflected = []; | ||||
|         this._value = val; | ||||
| @@ -64,31 +68,70 @@ class IgniteProperty { | ||||
|         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(arguments[0])); | ||||
|                 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()); | ||||
|                 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)); | ||||
|                 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)); | ||||
|                 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); | ||||
|             } | ||||
| @@ -97,51 +140,89 @@ class IgniteProperty { | ||||
|                 this.onPopCallbacks = this.onPopCallbacks.filter(pop => pop != callback); | ||||
|             } | ||||
|  | ||||
|             this.arrayCallbacks.push(this._value.attachOnPush(() => this.invokeOnPush())); | ||||
|             this.arrayCallbacks.push(this._value.attachOnPop(() => this.invokeOnPop())); | ||||
|         } else if (Array.isArray(this._value) && this._value.onPushCallbacks) { | ||||
|             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(() => this.invokeOnPush())); | ||||
|             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) { | ||||
|         for (var i = 0; i < this.onChangeCallbacks.length; i++) { | ||||
|             this.onChangeCallbacks[i].invoke(oldValue, newValue); | ||||
|         } | ||||
|         this.onChangeCallbacks.forEach(callback => callback.invoke(oldValue, newValue)); | ||||
|     } | ||||
|  | ||||
|     invokeOnPush() { | ||||
|         for (var i = 0; i < this.onPushCallbacks.length; i++) { | ||||
|             this.onPushCallbacks[i].invoke(null, this._value); | ||||
|         } | ||||
|     invokeOnPush(items) { | ||||
|         this.onPushCallbacks.forEach(callback => callback.invoke(this._value, items)); | ||||
|     } | ||||
|  | ||||
|     invokeOnPop() { | ||||
|         for (var i = 0; i < this.onPopCallbacks.length; i++) { | ||||
|             this.onPopCallbacks[i].invoke(null, this._value); | ||||
|         } | ||||
|         this.onPopCallbacks.forEach(callback => callback.invoke(this._value)); | ||||
|     } | ||||
|  | ||||
|     invokeOnShift() { | ||||
|         this.onShiftCallbacks.forEach(callback => callback.invoke(this._value)); | ||||
|     } | ||||
|  | ||||
|     invokeOnUnshift(items) { | ||||
|         this.onUnshiftCallbacks.forEach(callback => callback.invoke(this._value, items)); | ||||
|     } | ||||
|  | ||||
|     invokeonSplice(start, deleteCount, items) { | ||||
|         this.onSpliceCallbacks.forEach(callback => callback.invoke(this._value, start, deleteCount, items)); | ||||
|     } | ||||
|  | ||||
|     attachOnChange(onChange) { | ||||
|         var callback = new IgniteCallback(onChange, (detach) => this.detachOnChange(detach)); | ||||
|         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)); | ||||
|         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)); | ||||
|         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); | ||||
|     } | ||||
| @@ -153,6 +234,18 @@ class IgniteProperty { | ||||
|     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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -39,7 +39,9 @@ class IgniteTemplate { | ||||
|  | ||||
|         if (children) { | ||||
|             for (var i = 0; i < children.length; i++) { | ||||
|                 if (children[i] instanceof IgniteProperty) { | ||||
|                 if (children[i] === undefined || children[i] === null) { | ||||
|                     continue; //Skip undefined or null children. | ||||
|                 } else if (children[i] instanceof IgniteProperty) { | ||||
|                     this.children.push(new html(children[i])); | ||||
|                 } else if (children[i] instanceof String || typeof children[i] === 'string') { | ||||
|                     this.children.push(new html(children[i])); | ||||
| @@ -82,8 +84,11 @@ class IgniteTemplate { | ||||
|             //Attack a callback for all the properties | ||||
|             name.forEach(prop => { | ||||
|                 this.callbacks.push(prop.attachOnChange((oldValue, newValue) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, oldValue)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPush((oldValue, newValue) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, oldValue)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPop((oldValue, newValue) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, oldValue)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPush((list, items) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, list)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnUnshift((list, items) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, list)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPop((list) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, list)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnShift((list) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, list)), converter(...name.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnSplice((list, start, deleteCount, items) => this.onClassChanged(converter(...name.getOldPropertyValues(prop, list)), converter(...name.getPropertyValues())))); | ||||
|             }); | ||||
|  | ||||
|             var value = converter(...name.getPropertyValues()); | ||||
| @@ -230,7 +235,9 @@ class IgniteTemplate { | ||||
|     child(...items) { | ||||
|         if (items) { | ||||
|             for (var i = 0; i < items.length; i++) { | ||||
|                 if (items[i] instanceof IgniteProperty) { | ||||
|                 if (items[i] === undefined || items[i] === null) { | ||||
|                     continue; //Skip undefined or null items. | ||||
|                 } else if (items[i] instanceof IgniteProperty) { | ||||
|                     this.children.push(new html(items[i])); | ||||
|                 } else if (items[i] instanceof String || typeof items[i] === 'string') { | ||||
|                     this.children.push(new html(items[i])); | ||||
| @@ -298,7 +305,7 @@ class IgniteTemplate { | ||||
|  | ||||
|     /** | ||||
|      * Adds a onblur event handler to this template. | ||||
|      * @param {Function|IgniteProperty} eventCallback THe callback function to be invoked by the event once it fires. | ||||
|      * @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires. | ||||
|      * @returns This ignite template so function calls can be chained. | ||||
|      */ | ||||
|     onBlur(eventCallback) { | ||||
| @@ -307,13 +314,22 @@ class IgniteTemplate { | ||||
|  | ||||
|     /** | ||||
|      * Adds a onfocus event handler to this template. | ||||
|      * @param {Function|IgniteProperty} eventCallback THe callback function to be invoked by the event once it fires. | ||||
|      * @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires. | ||||
|      * @returns This ignite template so function calls can be chained. | ||||
|      */ | ||||
|     onFocus(eventCallback) { | ||||
|         return this.on("focus", eventCallback); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a onchange event handler to this template. | ||||
|      * @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires. | ||||
|      * @returns This ignite template so function calls can be chained. | ||||
|      */ | ||||
|     onChange(eventCallback) { | ||||
|         return this.on("change", eventCallback); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a on enter key press event handler to this template. | ||||
|      * @param {Function|IgniteProperty} eventCallback The callback function to be invoked by the event once it fires. | ||||
| @@ -452,8 +468,11 @@ class IgniteTemplate { | ||||
|             //Attack a callback for all the properties | ||||
|             value.forEach(prop => { | ||||
|                 this.callbacks.push(prop.attachOnChange((oldValue, newValue) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPush((oldValue, newValue) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPop((oldValue, newValue) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPush((list, items) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnUnshift((list, items) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnPop((list) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnShift((list) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|                 this.callbacks.push(prop.attachOnSplice((list, start, deleteCount, items) => this.onStyleChanged(name, converter(...value.getPropertyValues())))); | ||||
|             }); | ||||
|  | ||||
|             this.styles[name] = { name: name, value: converter(...value.getPropertyValues()), priority: priority }; | ||||
| @@ -1263,8 +1282,11 @@ class list extends IgniteTemplate { | ||||
|  | ||||
|         if (list instanceof IgniteProperty) { | ||||
|             this.callbacks.push(list.attachOnChange((oldValue, newValue) => this.onListChanged(oldValue, newValue))); | ||||
|             this.callbacks.push(list.attachOnPush((oldValue, newValue) => this.onListPush(oldValue, newValue))); | ||||
|             this.callbacks.push(list.attachOnPop((oldValue, newValue) => this.onListPop(oldValue, newValue))); | ||||
|             this.callbacks.push(list.attachOnPush((list, items) => this.onListPush(list, items))); | ||||
|             this.callbacks.push(list.attachOnUnshift((list, items) => this.onListUnshift(list, items))); | ||||
|             this.callbacks.push(list.attachOnPop(list => this.onListPop(list))); | ||||
|             this.callbacks.push(list.attachOnShift(list => this.onListShift(list))); | ||||
|             this.callbacks.push(list.attachOnSplice((list, start, deleteCount, items) => this.onListSplice(list, start, deleteCount, items))); | ||||
|  | ||||
|             this.list = list.value; | ||||
|         } else { | ||||
| @@ -1331,38 +1353,121 @@ class list extends IgniteTemplate { | ||||
|         try { | ||||
|             this.construct(null); //The list changed, reconstruct this template. | ||||
|         } catch (error) { | ||||
|             console.error(error); | ||||
|             console.error("An error occurred during onListChanged:", error); | ||||
|         } | ||||
|  | ||||
|         IgniteRenderingContext.leave(); | ||||
|     } | ||||
|  | ||||
|     onListPush(oldValue, newValue) { | ||||
|     onListPush(list, items) { | ||||
|         IgniteRenderingContext.enter(); | ||||
|  | ||||
|         try { | ||||
|             var template = this.forEach(this.list[this.list.length - 1]); | ||||
|             items.forEach(item => { | ||||
|                 var template = this.forEach(item); | ||||
|  | ||||
|             if (this.elements.length > 0) { | ||||
|                 template.construct(this.element.parentElement, this.elements[this.elements.length - 1].nextSibling); | ||||
|             } else { | ||||
|                 template.construct(this.element.parentElement, this.element); | ||||
|             } | ||||
|                 if (this.elements.length > 0) { | ||||
|                     template.construct(this.element.parentElement, this.elements[this.elements.length - 1].nextSibling); | ||||
|                 } else { | ||||
|                     template.construct(this.element.parentElement, this.element); | ||||
|                 } | ||||
|  | ||||
|             this.children.push(template); | ||||
|             this.elements.push(template.element); | ||||
|         } catch { } | ||||
|                 this.children.push(template); | ||||
|                 this.elements.push(template.element); | ||||
|             }); | ||||
|         } catch (error) { | ||||
|             console.error("An error occurred during onListPush:", error); | ||||
|         } | ||||
|  | ||||
|         IgniteRenderingContext.leave(); | ||||
|     } | ||||
|  | ||||
|     onListPop(oldValue, newValue) { | ||||
|     onListUnshift(list, items) { | ||||
|         IgniteRenderingContext.enter(); | ||||
|  | ||||
|         try { | ||||
|             items.reverse(); | ||||
|             items.forEach(item => { | ||||
|                 var template = this.forEach(item); | ||||
|  | ||||
|                 if (this.elements.length > 0) { | ||||
|                     template.construct(this.element.parentElement, this.elements[0]); | ||||
|                 } else { | ||||
|                     template.construct(this.element.parentElement, this.element); | ||||
|                 } | ||||
|  | ||||
|                 this.children.unshift(template); | ||||
|                 this.elements.unshift(template.element); | ||||
|             }); | ||||
|         } catch (error) { | ||||
|             console.error("An error occurred during onListUnshift:", error); | ||||
|         } | ||||
|  | ||||
|         IgniteRenderingContext.leave(); | ||||
|     } | ||||
|  | ||||
|     onListPop(list) { | ||||
|         if (this.children.length > 0) { | ||||
|             this.children[this.children.length - 1].deconstruct(); | ||||
|             this.children.pop(); | ||||
|             this.elements.pop(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     onListShift(list) { | ||||
|         if (this.children.length > 0) { | ||||
|             this.children[0].deconstruct(); | ||||
|             this.children.shift(); | ||||
|             this.elements.shift(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     onListSplice(list, start, deleteCount, items) { | ||||
|         IgniteRenderingContext.enter(); | ||||
|  | ||||
|         //Remove any items that are needed. | ||||
|         if (deleteCount > 0 && this.children.length > 0) { | ||||
|             for (var i = start; i < Math.min(this.children.length, start + deleteCount); i++) { | ||||
|                 this.children[i].deconstruct(); | ||||
|             } | ||||
|  | ||||
|             this.children.splice(start, deleteCount); | ||||
|             this.elements.splice(start, deleteCount); | ||||
|         } | ||||
|  | ||||
|         //If the start is greater than the length of the items adjust it. | ||||
|         if (start > this.children.length) { | ||||
|             start = Math.max(this.children.length - 1, 0); | ||||
|         } | ||||
|  | ||||
|         //Append any new items if there are any. | ||||
|         if (items) { | ||||
|             items.forEach(item => { | ||||
|                 var template = this.forEach(item); | ||||
|  | ||||
|                 if (this.elements.length > 0) { | ||||
|                     template.construct(this.element.parentElement, this.elements[start]); | ||||
|                 } else { | ||||
|                     template.construct(this.element.parentElement, this.element); | ||||
|                 } | ||||
|  | ||||
|                 this.children.splice(start, 0, template); | ||||
|                 this.elements.splice(start, 0, template.element); | ||||
|  | ||||
|                 start += 1; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         IgniteRenderingContext.leave(); | ||||
|     } | ||||
|  | ||||
|     onStyleChanged(name, newValue) { | ||||
|         this.elements.forEach((element) => { | ||||
|             element.style.setProperty(name, newValue, this.styles[name].priority); | ||||
|         }); | ||||
|  | ||||
|         this.styles[name].value = newValue; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
		Reference in New Issue
	
	Block a user