diff --git a/PR-225/COMMITHASH b/PR-225/COMMITHASH
new file mode 100644
index 00000000..99a7dda0
--- /dev/null
+++ b/PR-225/COMMITHASH
@@ -0,0 +1 @@
+2c7d058330f3571949f4f1d545027831a0010e91
\ No newline at end of file
diff --git a/PR-225/VERSION b/PR-225/VERSION
new file mode 100644
index 00000000..714dcd0e
--- /dev/null
+++ b/PR-225/VERSION
@@ -0,0 +1 @@
+2c7d058
\ No newline at end of file
diff --git a/PR-225/index.html b/PR-225/index.html
new file mode 100644
index 00000000..96cbe503
--- /dev/null
+++ b/PR-225/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+ 1.3.0 - Firebolt Certification App - version 1.3.0
+
+
+
+
+
+
+
+
+
diff --git a/PR-225/lib/lightning-inspect.js b/PR-225/lib/lightning-inspect.js
new file mode 100644
index 00000000..e1ba5238
--- /dev/null
+++ b/PR-225/lib/lightning-inspect.js
@@ -0,0 +1,866 @@
+/*
+ * If not stated otherwise in this file or this component's LICENSE file the
+ * following copyright and licenses apply:
+ *
+ * Copyright 2020 Metrological
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+window.attachInspector = function({Application, Element, ElementCore, Stage, Component, ElementTexturizer, Texture}) {
+
+ const isAlreadyAttached = window.hasOwnProperty('mutationCounter');
+ if (isAlreadyAttached) {
+ return;
+ }
+
+ window.mutationCounter = 0;
+ window.mutatingChildren = false;
+ var observer = new MutationObserver(function(mutations) {
+ var fa = ["x", "y", "w", "h", "alpha", "mountX", "mountY", "pivotX", "pivotY", "scaleX", "scaleY", "rotation", "visible", "clipping", "rect", "colorUl", "colorUr", "colorBl", "colorBr", "color", "borderWidthLeft", "borderWidthRight", "borderWidthTop", "borderWidthBottom", "borderWidth", "borderColorLeft", "borderColorRight", "borderColorTop", "borderColorBottom", "borderColor", "zIndex", "forceZIndexContext", "renderToTexture", "renderToTextureLazy", "renderOffscreen", "colorizeResultTexture", "texture"];
+ var fac = fa.map(function(v) {return v.toLowerCase()});
+
+ mutations.forEach(function(mutation) {
+ if (mutation.type == 'childList') {
+
+ var node = mutation.target;
+ var c = mutation.target.element;
+ }
+
+ if (mutation.type == 'attributes' && mutation.attributeName !== 'style' && mutation.attributeName !== 'class') {
+ var n = mutation.attributeName.toLowerCase();
+ var c = mutation.target.element;
+
+ if (c.__ignore_attrib_changes === window.mutationCounter) {
+ // Ignore attribute changes that were caused by actual value modifications by js.
+ return;
+ }
+
+ var v = mutation.target.getAttribute(mutation.attributeName);
+
+ if (n.startsWith("texture-")) {
+ if (c.displayedTexture) {
+ const att = n.substr(8).split("_")
+ const camelCaseAtt = att[0] + att.slice(1).map(a => {
+ return a.substr(0,1).toUpperCase() + a.substr(1).toLowerCase()
+ }).join()
+
+ c.displayedTexture[camelCaseAtt] = v
+ }
+ return
+ }
+
+ var index = fac.indexOf(n);
+ if (index !== -1) {
+ var rn = fa[index];
+ var pv;
+ try {
+ if (v === null) {
+ switch(rn) {
+ case "pivotX":
+ case "pivotY":
+ pv = 0.5;
+ break;
+ case "alpha":
+ case "scaleX":
+ case "scaleY":
+ pv = 1;
+ break;
+ case "visible":
+ pv = true;
+ break;
+ case "clipping":
+ pv = false;
+ break;
+ case "rect":
+ pv = false;
+ break;
+ case "zIndex":
+ pv = 0;
+ break;
+ case "forceZIndexContext":
+ pv = false;
+ break;
+ case "color":
+ pv = 0xffffffff;
+ break;
+ case "colorUl":
+ case "colorUr":
+ case "colorBl":
+ case "colorBr":
+ if (mutation.target.hasAttribute("color")) {
+ // This may happen when the separate values are combined.
+ return;
+ }
+ pv = 0xffffffff;
+ break;
+ case "renderToTexture":
+ pv = false
+ break;
+ case "renderToTextureLazy":
+ pv = false
+ break;
+ case "renderOffscreen":
+ pv = false
+ break;
+ case "colorizeResultTexture":
+ pv = false
+ break;
+ default:
+ pv = 0;
+ }
+ } else {
+ switch(rn) {
+ case "color":
+ case "colorUl":
+ case "colorUr":
+ case "colorBl":
+ case "colorBr":
+ pv = parseInt(v, 16);
+ break;
+ case "visible":
+ case "clipping":
+ case "rect":
+ case "forceZIndexContext":
+ case "renderToTexture":
+ case "renderToTextureLazy":
+ case "renderOffscreen":
+ case "colorizeResultTexture":
+ pv = (v === "true");
+ break;
+ case "texture":
+ pv = JSON.parse(v)
+ break
+ default:
+ pv = parseFloat(v);
+ if (isNaN(pv)) throw "e";
+ }
+ }
+
+ var fv;
+ switch(rn) {
+ case "color":
+ var f = ['colorUl','colorUr','colorBl','colorBr'].map(function(q) {
+ return mutation.target.hasAttribute(q);
+ });
+
+ if (!f[0]) c["colorUl"] = pv;
+ if (!f[1]) c["colorUr"] = pv;
+ if (!f[2]) c["colorBl"] = pv;
+ if (!f[3]) c["colorBr"] = pv;
+ break;
+ default:
+ c[rn] = pv;
+ }
+
+ // Set final value, not the transitioned value.
+ } catch(e) {
+ console.error('Bad (ignored) attribute value', rn);
+ }
+ }
+ }
+ });
+
+ window.mutationCounter++;
+ });
+
+ ElementCore.prototype.dhtml = function() {
+ return this._element.dhtml();
+ }
+
+ Element.prototype.dhtml = function() {
+ if (!this.debugElement) {
+ this.debugElement = document.createElement('DIV');
+ this.debugElement.setAttribute('type', this.constructor.name);
+ this.debugElement.element = this;
+ this.debugElement.style.position = 'absolute';
+
+ this.debugElement.id = "" + this.id;
+ observer.observe(this.debugElement, {attributes: true});
+ }
+ if (this.stage.root === this && !this.dhtml_root) {
+ // Root element.
+ var root = document.createElement('DIV');
+ document.body.appendChild(root);
+ var self = this;
+ let updateRootStyleFromCanvas = function (bcr) {
+ const p = self.stage.getRenderPrecision() / self.stage.getOption('devicePixelRatio');
+ root.style.left = bcr.left + 'px';
+ root.style.top = bcr.top + 'px';
+ root.style.width = Math.ceil(bcr.width / p) + 'px';
+ root.style.height = Math.ceil(bcr.height / p) + 'px';
+ root.style.transformOrigin = '0 0 0';
+ root.style.transform = 'scale(' + p + ',' + p + ')';
+ }
+
+ if (window.ResizeObserver != null) {
+ const resize_ob = new ResizeObserver(function (entries) {
+ updateRootStyleFromCanvas(entries[0].target.getBoundingClientRect());
+ });
+ // start observing for resize
+ resize_ob.observe(this.stage.getCanvas());
+ } else {
+ setTimeout(function () {
+ updateRootStyleFromCanvas(self.stage.getCanvas().getBoundingClientRect());
+ }, 1000);
+ }
+
+ root.style.position = 'absolute';
+ root.style.overflow = 'hidden';
+ root.style.zIndex = '65535';
+ root.appendChild(this.debugElement);
+
+ this.dhtml_root = root;
+ }
+ return this.debugElement;
+ };
+
+ var oElement = Element;
+
+ var oSetParent = oElement.prototype._setParent;
+ Element.prototype._setParent = function(parent) {
+ var prevParent = this.parent;
+ oSetParent.apply(this, arguments);
+
+ if (!window.mutatingChildren) {
+ if (parent && parent.dhtml) {
+ var index = parent._children.getIndex(this);
+ if (index == parent._children.get().length - 1) {
+ parent.dhtml().appendChild(this.dhtml());
+ } else {
+ parent.dhtml().insertBefore(this.dhtml(), parent.dhtml().children[index]);
+ }
+ } else {
+ if (prevParent && prevParent.dhtml) {
+ prevParent.dhtml().removeChild(this.dhtml());
+ }
+ }
+ }
+ };
+
+ var oInit = Stage.prototype.init;
+ Stage.prototype.init = function() {
+ oInit.apply(this, arguments);
+
+ // Apply stage scaling.
+ this.root.core.updateDebugTransforms();
+ };
+
+ var oAddTag = oElement.prototype.addTag;
+ Element.prototype.addTag = function(tag) {
+ oAddTag.apply(this, arguments);
+
+ if (tag) {
+ this.dhtml().classList.add(tag);
+ }
+ };
+
+ var oRemoveTag = oElement.prototype.removeTag;
+ Element.prototype.removeTag = function(tag) {
+ oRemoveTag.apply(this, arguments);
+
+ if (tag) {
+ this.dhtml().classList.remove(tag);
+ }
+ };
+
+// Change an attribute due to new value inputs.
+ var val = function(c, n, v, dv) {
+ if (c._element) {
+ c = c._element;
+ }
+ if (v == dv) {
+ if (c.dhtmlRemoveAttribute) {
+ c.dhtmlRemoveAttribute(n);
+ }
+ } else {
+ if (c.dhtmlSetAttribute) {
+ c.dhtmlSetAttribute(n, v);
+ }
+ }
+ };
+
+ var valStrict = function(c, n, v, dv) {
+ if (c._element) {
+ c = c._element;
+ }
+ if (v === dv) {
+ if (c.dhtmlRemoveAttribute) {
+ c.dhtmlRemoveAttribute(n);
+ }
+ } else {
+ if (c.dhtmlSetAttribute) {
+ c.dhtmlSetAttribute(n, v);
+ }
+ }
+ };
+
+ Element.prototype.dhtmlRemoveAttribute = function() {
+ // We don't want the attribute listeners to be called during the next observer cycle.
+ this.__ignore_attrib_changes = window.mutationCounter;
+ this.dhtml().removeAttribute.apply(this.dhtml(), arguments);
+ };
+
+ Element.prototype.dhtmlSetAttribute = function() {
+ this.__ignore_attrib_changes = window.mutationCounter;
+ this.dhtml().setAttribute.apply(this.dhtml(), arguments);
+ };
+
+ if (typeof Component !== "undefined") {
+ Object.defineProperty(Component.prototype, '_state', {
+ get: function() {
+ return this.__state;
+ },
+ set: function(v) {
+ if (this.__state !== v) {
+ if (this.__state !== null) { // Ignore initial.
+ val(this, 'state', v ? v.__path : "", "");
+ }
+ this.__state = v;
+ }
+ }
+ });
+ }
+
+ Element.prototype.$ref = Element.prototype.__ref;
+ Object.defineProperty(Element.prototype, '__ref', {
+ get: function() {
+ return this.$ref;
+ },
+ set: function(v) {
+ if (this.$ref !== v) {
+ val(this, 'ref', v, null);
+ this.$ref = v;
+ }
+ }
+ });
+
+ ElementCore.prototype.$x = ElementCore.prototype._x;
+ Object.defineProperty(ElementCore.prototype, '_x', {
+ get: function() {
+ return this.$x;
+ },
+ set: function(v) {
+ if (this.$x !== v) {
+ val(this, 'x', v, 0);
+ this.$x = v;
+ this.updateLeft();
+ }
+ }
+ });
+
+ ElementCore.prototype.$y = ElementCore.prototype._y;
+ Object.defineProperty(ElementCore.prototype, '_y', {
+ get: function() {
+ return this.$y;
+ },
+ set: function(v) {
+ if (this.$y !== v) {
+ val(this, 'y', v, 0);
+ this.$y = v;
+ this.updateTop();
+ }
+ }
+ });
+
+ Element.prototype.$w = Element.prototype._w;
+ Object.defineProperty(Element.prototype, '_w', {
+ get: function() {
+ return this.$w;
+ },
+ set: function(v) {
+ if (this.$w !== v) {
+ val(this, 'w', v, 0);
+ this.$w = v;
+ }
+ }
+ });
+
+ Element.prototype.$h = Element.prototype._h;
+ Object.defineProperty(Element.prototype, '_h', {
+ get: function() {
+ return this.$h;
+ },
+ set: function(v) {
+ if (this.$h !== v) {
+ val(this, 'h', v, 0);
+ this.$h = v;
+ }
+ }
+ });
+
+ ElementCore.prototype.updateLeft = function() {
+ var mx = this._mountX * this._w;
+ var x = this._x - mx;
+ this.dhtml().style.left = x + 'px';
+ };
+
+ ElementCore.prototype.updateTop = function() {
+ var my = this._mountY * this._h;
+ var y = this._y - my;
+ this.dhtml().style.top = y + 'px';
+ };
+
+ ElementCore.prototype.__w = 0;
+ Object.defineProperty(ElementCore.prototype, '_w', {
+ get: function() {
+ return this.__w;
+ },
+ set: function(v) {
+ this.__w = v;
+ this.dhtml().style.width = v + 'px';
+ this.updateLeft();
+ }
+ });
+
+ ElementCore.prototype.__h = 0;
+ Object.defineProperty(ElementCore.prototype, '_h', {
+ get: function() {
+ return this.__h;
+ },
+ set: function(v) {
+ this.__h = v;
+ this.dhtml().style.height = v + 'px';
+ this.updateTop();
+ }
+ });
+
+ ElementCore.prototype.$alpha = 1;
+ Object.defineProperty(ElementCore.prototype, '_alpha', {
+ get: function() {
+ return this.$alpha;
+ },
+ set: function(v) {
+ if (this.$alpha !== v) {
+ val(this, 'alpha', v, 1);
+ this.$alpha = v;
+ this.dhtml().style.opacity = v;
+ this.dhtml().style.display = this.$visible && this.$alpha ? 'block' : 'none';
+ }
+ }
+ });
+
+ ElementCore.prototype.$visible = true;
+ Object.defineProperty(ElementCore.prototype, '_visible', {
+ get: function() {
+ return this.$visible;
+ },
+ set: function(v) {
+ if (this.$visible !== v) {
+ val(this, 'visible', v, true);
+ this.$visible = v;
+ this.dhtml().style.visibility = v ? 'visible' : 'hidden';
+ this.dhtml().style.display = this.$visible && this.$alpha ? 'block' : 'none';
+ }
+ }
+ });
+
+ ElementCore.prototype.$rotation = 0;
+ Object.defineProperty(ElementCore.prototype, '_rotation', {
+ get: function() {
+ return this.$rotation;
+ },
+ set: function(v) {
+ if (this.$rotation !== v) {
+ val(this, 'rotation', v, 0);
+ this.$rotation = v;
+ this.updateDebugTransforms();
+ }
+ }
+ });
+
+
+ ElementCore.prototype.$scaleX = 1;
+ Object.defineProperty(ElementCore.prototype, '_scaleX', {
+ get: function() {
+ return this.$scaleX;
+ },
+ set: function(v) {
+ if (this.$scaleX !== v) {
+ val(this, 'scaleX', v, 1);
+ this.$scaleX = v;
+ this.updateDebugTransforms();
+ }
+ }
+ });
+
+ ElementCore.prototype.$scaleY = 1;
+ Object.defineProperty(ElementCore.prototype, '_scaleY', {
+ get: function() {
+ return this.$scaleY;
+ },
+ set: function(v) {
+ if (this.$scaleY !== v) {
+ val(this, 'scaleY', v, 1);
+ this.$scaleY = v;
+ this.updateDebugTransforms();
+ }
+ }
+ });
+
+ ElementCore.prototype.$pivotX = 0.5;
+ Object.defineProperty(ElementCore.prototype, '_pivotX', {
+ get: function() {
+ return this.$pivotX;
+ },
+ set: function(v) {
+ if (this.$pivotX !== v) {
+ val(this, 'pivotX', v, 0.5);
+ this.$pivotX = v;
+ this.updateDebugTransforms();
+ }
+ }
+ });
+
+ ElementCore.prototype.$pivotY = 0.5;
+ Object.defineProperty(ElementCore.prototype, '_pivotY', {
+ get: function() {
+ return this.$pivotY;
+ },
+ set: function(v) {
+ if (this.$pivotY !== v) {
+ val(this, 'pivotY', v, 0.5);
+ this.$pivotY = v;
+ this.updateDebugTransforms();
+ }
+ }
+ });
+
+ ElementCore.prototype.$mountX = 0;
+ Object.defineProperty(ElementCore.prototype, '_mountX', {
+ get: function() {
+ return this.$mountX;
+ },
+ set: function(v) {
+ if (this.$mountX !== v) {
+ val(this, 'mountX', v, 0);
+ this.$mountX = v;
+ this.updateLeft();
+ }
+ }
+ });
+
+ ElementCore.prototype.$mountY = 0;
+ Object.defineProperty(ElementCore.prototype, '_mountY', {
+ get: function() {
+ return this.$mountY;
+ },
+ set: function(v) {
+ if (this.$mountY !== v) {
+ val(this, 'mountY', v, 0);
+ this.$mountY = v;
+ this.updateTop();
+ }
+ }
+ });
+
+ ElementCore.prototype.__zIndex = 0;
+ Object.defineProperty(ElementCore.prototype, '_zIndex', {
+ get: function() {
+ return this.__zIndex;
+ },
+ set: function(v) {
+ if (this.__zIndex !== v) {
+ val(this, 'zIndex', v, 0);
+ this.__zIndex = v;
+ if (this.__zIndex || v) {
+ this.dhtml().style.zIndex = v;
+ }
+ }
+ }
+ });
+
+ ElementCore.prototype.__forceZIndexContext = false;
+ Object.defineProperty(ElementCore.prototype, '_forceZIndexContext', {
+ get: function() {
+ return this.__forceZIndexContext;
+ },
+ set: function(v) {
+ if (this.__forceZIndexContext !== v) {
+ val(this, 'forceZIndexContext', v, false);
+ this.__forceZIndexContext = v;
+ }
+ }
+ });
+
+ ElementCore.prototype.__clipping = false;
+ Object.defineProperty(ElementCore.prototype, '_clipping', {
+ get: function() {
+ return this.__clipping;
+ },
+ set: function(v) {
+ if (this.__clipping !== v) {
+ val(this, 'clipping', v, false);
+ this.__clipping = v;
+ var nv = v ? 'hidden' : 'visible';
+ if (v || !v && (this.dhtml().style.overflow == 'hidden')) {
+ this.dhtml().style.overflow = nv;
+ }
+ }
+ }
+ });
+
+ ElementCore.prototype.__withinBoundsMargin = false;
+ Object.defineProperty(ElementCore.prototype, '_withinBoundsMargin', {
+ get: function() {
+ return this.__withinBoundsMargin;
+ },
+ set: function(v) {
+ if (this.__withinBoundsMargin !== v) {
+ val(this, 'withinBoundsMargin', v, false);
+ this.__withinBoundsMargin = v;
+ }
+ }
+ });
+
+ ElementCore.prototype.__colorUl = 0xFFFFFFFF;
+ Object.defineProperty(ElementCore.prototype, '_colorUl', {
+ get: function() {
+ return this.__colorUl;
+ },
+ set: function(v) {
+ if (this.__colorUl !== v) {
+ val(this, 'colorUl', v.toString(16), "ffffffff");
+ this.__colorUl = v;
+ checkColors(this);
+ }
+ }
+ });
+
+ ElementCore.prototype.__colorUr = 0xFFFFFFFF;
+ Object.defineProperty(ElementCore.prototype, '_colorUr', {
+ get: function() {
+ return this.__colorUr;
+ },
+ set: function(v) {
+ if (this.__colorUr !== v) {
+ val(this, 'colorUr', v.toString(16), "ffffffff");
+ this.__colorUr = v;
+ checkColors(this);
+ }
+ }
+ });
+
+ ElementCore.prototype.__colorBl = 0xFFFFFFFF;
+ Object.defineProperty(ElementCore.prototype, '_colorBl', {
+ get: function() {
+ return this.__colorBl;
+ },
+ set: function(v) {
+ if (this.__colorBl !== v) {
+ val(this, 'colorBl', v.toString(16), "ffffffff");
+ this.__colorBl = v;
+ checkColors(this);
+ }
+ }
+ });
+
+ ElementCore.prototype.__colorBr = 0xFFFFFFFF;
+ Object.defineProperty(ElementCore.prototype, '_colorBr', {
+ get: function() {
+ return this.__colorBr;
+ },
+ set: function(v) {
+ if (this.__colorBr !== v) {
+ val(this, 'colorBr', v.toString(16), "ffffffff");
+ this.__colorBr = v;
+ checkColors(this);
+ }
+ }
+ });
+
+ Element.prototype.$texture = null;
+ Object.defineProperty(Element.prototype, '__texture', {
+ get: function() {
+ return this.$texture;
+ },
+ set: function(v) {
+ this.$texture = v;
+
+ val(this, 'rect', this.rect, false);
+ val(this, 'src', this.src, null);
+ }
+ });
+
+ Element.prototype.$testId = null;
+ Object.defineProperty(Element.prototype, 'testId', {
+ get: function() {
+ return this.$testId;
+ },
+ set: function(v) {
+ if (this.$testId !== v) {
+ this.$testId = v;
+ val(this, 'data-testid', v, null);
+ }
+ }
+ });
+
+ var checkColors = function(elementRenderer) {
+ let element = elementRenderer._element;
+ if (elementRenderer._colorBr === undefined) {
+ // Element initialization.
+ return;
+ }
+
+ if (elementRenderer._colorUl === elementRenderer._colorUr && elementRenderer._colorUl === elementRenderer._colorBl && elementRenderer._colorUl === elementRenderer._colorBr) {
+ if (elementRenderer._colorUl !== 0xffffffff) {
+ element.dhtmlSetAttribute('color', elementRenderer._colorUl.toString(16));
+ } else {
+ element.dhtmlRemoveAttribute('color');
+ }
+ element.dhtmlRemoveAttribute('colorul');
+ element.dhtmlRemoveAttribute('colorur');
+ element.dhtmlRemoveAttribute('colorbl');
+ element.dhtmlRemoveAttribute('colorbr');
+ } else {
+ val(element, 'colorUr', elementRenderer.colorUr.toString(16), "ffffffff");
+ val(element, 'colorUl', elementRenderer.colorUl.toString(16), "ffffffff");
+ val(element, 'colorBr', elementRenderer.colorBr.toString(16), "ffffffff");
+ val(element, 'colorBl', elementRenderer.colorBl.toString(16), "ffffffff");
+ element.dhtmlRemoveAttribute('color');
+ }
+ };
+
+ ElementTexturizer.prototype.__enabled = false;
+ Object.defineProperty(ElementTexturizer.prototype, '_enabled', {
+ get: function() {
+ return this.__enabled;
+ },
+ set: function(v) {
+ if (this.__enabled !== v) {
+ val(this, 'renderToTexture', v, false);
+ this.__enabled = v;
+ }
+ }
+ });
+
+ ElementTexturizer.prototype.__lazy = false;
+ Object.defineProperty(ElementTexturizer.prototype, '_lazy', {
+ get: function() {
+ return this.__lazy;
+ },
+ set: function(v) {
+ if (this.__lazy !== v) {
+ val(this, 'renderToTextureLazy', v, false);
+ this.__lazy = v;
+ }
+ }
+ });
+
+ ElementTexturizer.prototype.__colorize = false;
+ Object.defineProperty(ElementTexturizer.prototype, '_colorize', {
+ get: function() {
+ return this.__colorize;
+ },
+ set: function(v) {
+ if (this.__colorize !== v) {
+ val(this, 'colorizeResultTexture', v, false);
+ this.__colorize = v;
+ }
+ }
+ });
+
+ ElementTexturizer.prototype.__renderOffscreen = false;
+ Object.defineProperty(ElementTexturizer.prototype, '_renderOffscreen', {
+ get: function() {
+ return this.__renderOffscreen;
+ },
+ set: function(v) {
+ if (this.__renderOffscreen !== v) {
+ val(this, 'renderOffscreen', v, false);
+ this.__renderOffscreen = v;
+ }
+ }
+ });
+
+ ElementCore.prototype.updateDebugTransforms = function() {
+ const stage = this._element.stage
+
+ if (this._pivotX !== 0.5 || this._pivotY !== 0.5) {
+ this.dhtml().style.transformOrigin = (this._pivotX * 100) + '% ' + (this._pivotY * 100) + '%';
+ } else if (this.dhtml().style.transformOrigin) {
+ this.dhtml().style.transformOrigin = '50% 50%';
+ }
+
+ var r = this._rotation;
+ var sx = this._scaleX;
+ var sy = this._scaleY;
+
+ if ((sx !== undefined && sy !== undefined) && (this._element.id === 0)) {
+ // Root element: must be scaled.
+ if (stage.options.w !== stage.options.renderWidth || stage.options.h !== stage.options.renderHeight) {
+ sx *= (stage.options.w / stage.options.renderWidth);
+ sy *= (stage.options.h / stage.options.renderHeight);
+ }
+ }
+ var parts = [];
+ if (r) parts.push('rotate(' + r + 'rad)');
+ if ((sx !== undefined && sy !== undefined) && (sx !== 1 || sy !== 1)) parts.push('scale(' + sx + ', ' + sy + ')');
+
+ this.dhtml().style.transform = parts.join(' ');
+ };
+
+ var updateTextureAttribs = function(element) {
+ if (element.texture) {
+ const nonDefaults = element.texture.getNonDefaults()
+ const keys = Object.keys(nonDefaults)
+ keys.forEach(key => {
+ let f = ""
+ for (let i = 0, n = key.length; i < n; i++) {
+ const c = key.charAt(i)
+ if (c !== c.toLowerCase()) {
+ f += "_" + c.toLowerCase()
+ } else {
+ f += c
+ }
+ }
+ valStrict(element, `texture-${f}`, nonDefaults[key], false);
+ })
+ }
+ }
+
+ const _performUpdateSource = Texture.prototype._performUpdateSource
+ Texture.prototype._performUpdateSource = function() {
+ _performUpdateSource.apply(this, arguments)
+ this.elements.forEach(v => {
+ updateTextureAttribs(v)
+ })
+ }
+
+ const _setDisplayedTexture = Element.prototype._setDisplayedTexture
+ Element.prototype._setDisplayedTexture = function() {
+ _setDisplayedTexture.apply(this, arguments)
+ updateTextureAttribs(this)
+ }
+
+ const _updateFocus = Application.prototype.__updateFocus
+ Application.prototype.__updateFocus = function() {
+ const prev = this._focusPath && this._focusPath.length ? this._focusPath[this._focusPath.length - 1] : null;
+ _updateFocus.apply(this, arguments)
+ const focused = this._focusPath && this._focusPath.length ? this._focusPath[this._focusPath.length - 1] : null;
+
+ if (prev != focused) {
+ if (prev) {
+ val(prev, 'focused', false, false);
+ }
+ if (focused) {
+ val(focused, 'focused', true, false);
+ }
+ }
+ }
+};
+
+if (window.lng) {
+ // Automatically attach inspector if lng was already loaded.
+ attachInspector(lng);
+}
diff --git a/PR-225/main.bundle.b7c4bd3f9ec33c06f7ed.js b/PR-225/main.bundle.b7c4bd3f9ec33c06f7ed.js
new file mode 100644
index 00000000..2521c050
--- /dev/null
+++ b/PR-225/main.bundle.b7c4bd3f9ec33c06f7ed.js
@@ -0,0 +1,195338 @@
+/*! version 1.3.0 */
+/******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ }
+/******/ };
+/******/
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = function(exports) {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/
+/******/ // create a fake namespace object
+/******/ // mode & 1: value is a module id, require it
+/******/ // mode & 2: merge all properties of value into the ns
+/******/ // mode & 4: return value when already ns object
+/******/ // mode & 8|1: behave like require
+/******/ __webpack_require__.t = function(value, mode) {
+/******/ if(mode & 1) value = __webpack_require__(value);
+/******/ if(mode & 8) return value;
+/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ var ns = Object.create(null);
+/******/ __webpack_require__.r(ns);
+/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ return ns;
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = "./src/index.js");
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ "./node_modules/@apidevtools/json-schema-ref-parser/lib/bundle.js":
+/*!************************************************************************!*\
+ !*** ./node_modules/@apidevtools/json-schema-ref-parser/lib/bundle.js ***!
+ \************************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const $Ref = __webpack_require__(/*! ./ref */ "./node_modules/@apidevtools/json-schema-ref-parser/lib/ref.js");
+const Pointer = __webpack_require__(/*! ./pointer */ "./node_modules/@apidevtools/json-schema-ref-parser/lib/pointer.js");
+const url = __webpack_require__(/*! ./util/url */ "./node_modules/@apidevtools/json-schema-ref-parser/lib/util/url.js");
+
+module.exports = bundle;
+
+/**
+ * Bundles all external JSON references into the main JSON schema, thus resulting in a schema that
+ * only has *internal* references, not any *external* references.
+ * This method mutates the JSON schema object, adding new references and re-mapping existing ones.
+ *
+ * @param {$RefParser} parser
+ * @param {$RefParserOptions} options
+ */
+function bundle (parser, options) {
+ // console.log('Bundling $ref pointers in %s', parser.$refs._root$Ref.path);
+
+ // Build an inventory of all $ref pointers in the JSON Schema
+ let inventory = [];
+ crawl(parser, "schema", parser.$refs._root$Ref.path + "#", "#", 0, inventory, parser.$refs, options);
+
+ // Remap all $ref pointers
+ remap(inventory);
+}
+
+/**
+ * Recursively crawls the given value, and inventories all JSON references.
+ *
+ * @param {object} parent - The object containing the value to crawl. If the value is not an object or array, it will be ignored.
+ * @param {string} key - The property key of `parent` to be crawled
+ * @param {string} path - The full path of the property being crawled, possibly with a JSON Pointer in the hash
+ * @param {string} pathFromRoot - The path of the property being crawled, from the schema root
+ * @param {object[]} inventory - An array of already-inventoried $ref pointers
+ * @param {$Refs} $refs
+ * @param {$RefParserOptions} options
+ */
+function crawl (parent, key, path, pathFromRoot, indirections, inventory, $refs, options) {
+ let obj = key === null ? parent : parent[key];
+
+ if (obj && typeof obj === "object" && !ArrayBuffer.isView(obj)) {
+ if ($Ref.isAllowed$Ref(obj)) {
+ inventory$Ref(parent, key, path, pathFromRoot, indirections, inventory, $refs, options);
+ }
+ else {
+ // Crawl the object in a specific order that's optimized for bundling.
+ // This is important because it determines how `pathFromRoot` gets built,
+ // which later determines which keys get dereferenced and which ones get remapped
+ let keys = Object.keys(obj)
+ .sort((a, b) => {
+ // Most people will expect references to be bundled into the the "definitions" property,
+ // so we always crawl that property first, if it exists.
+ if (a === "definitions") {
+ return -1;
+ }
+ else if (b === "definitions") {
+ return 1;
+ }
+ else {
+ // Otherwise, crawl the keys based on their length.
+ // This produces the shortest possible bundled references
+ return a.length - b.length;
+ }
+ });
+
+ // eslint-disable-next-line no-shadow
+ for (let key of keys) {
+ let keyPath = Pointer.join(path, key);
+ let keyPathFromRoot = Pointer.join(pathFromRoot, key);
+ let value = obj[key];
+
+ if ($Ref.isAllowed$Ref(value)) {
+ inventory$Ref(obj, key, path, keyPathFromRoot, indirections, inventory, $refs, options);
+ }
+ else {
+ crawl(obj, key, keyPath, keyPathFromRoot, indirections, inventory, $refs, options);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Inventories the given JSON Reference (i.e. records detailed information about it so we can
+ * optimize all $refs in the schema), and then crawls the resolved value.
+ *
+ * @param {object} $refParent - The object that contains a JSON Reference as one of its keys
+ * @param {string} $refKey - The key in `$refParent` that is a JSON Reference
+ * @param {string} path - The full path of the JSON Reference at `$refKey`, possibly with a JSON Pointer in the hash
+ * @param {string} pathFromRoot - The path of the JSON Reference at `$refKey`, from the schema root
+ * @param {object[]} inventory - An array of already-inventoried $ref pointers
+ * @param {$Refs} $refs
+ * @param {$RefParserOptions} options
+ */
+function inventory$Ref ($refParent, $refKey, path, pathFromRoot, indirections, inventory, $refs, options) {
+ let $ref = $refKey === null ? $refParent : $refParent[$refKey];
+ let $refPath = url.resolve(path, $ref.$ref);
+ let pointer = $refs._resolve($refPath, pathFromRoot, options);
+ if (pointer === null) {
+ return;
+ }
+
+ let depth = Pointer.parse(pathFromRoot).length;
+ let file = url.stripHash(pointer.path);
+ let hash = url.getHash(pointer.path);
+ let external = file !== $refs._root$Ref.path;
+ let extended = $Ref.isExtended$Ref($ref);
+ indirections += pointer.indirections;
+
+ let existingEntry = findInInventory(inventory, $refParent, $refKey);
+ if (existingEntry) {
+ // This $Ref has already been inventoried, so we don't need to process it again
+ if (depth < existingEntry.depth || indirections < existingEntry.indirections) {
+ removeFromInventory(inventory, existingEntry);
+ }
+ else {
+ return;
+ }
+ }
+
+ inventory.push({
+ $ref, // The JSON Reference (e.g. {$ref: string})
+ parent: $refParent, // The object that contains this $ref pointer
+ key: $refKey, // The key in `parent` that is the $ref pointer
+ pathFromRoot, // The path to the $ref pointer, from the JSON Schema root
+ depth, // How far from the JSON Schema root is this $ref pointer?
+ file, // The file that the $ref pointer resolves to
+ hash, // The hash within `file` that the $ref pointer resolves to
+ value: pointer.value, // The resolved value of the $ref pointer
+ circular: pointer.circular, // Is this $ref pointer DIRECTLY circular? (i.e. it references itself)
+ extended, // Does this $ref extend its resolved value? (i.e. it has extra properties, in addition to "$ref")
+ external, // Does this $ref pointer point to a file other than the main JSON Schema file?
+ indirections, // The number of indirect references that were traversed to resolve the value
+ });
+
+ // Recursively crawl the resolved value
+ if (!existingEntry) {
+ crawl(pointer.value, null, pointer.path, pathFromRoot, indirections + 1, inventory, $refs, options);
+ }
+}
+
+/**
+ * Re-maps every $ref pointer, so that they're all relative to the root of the JSON Schema.
+ * Each referenced value is dereferenced EXACTLY ONCE. All subsequent references to the same
+ * value are re-mapped to point to the first reference.
+ *
+ * @example:
+ * {
+ * first: { $ref: somefile.json#/some/part },
+ * second: { $ref: somefile.json#/another/part },
+ * third: { $ref: somefile.json },
+ * fourth: { $ref: somefile.json#/some/part/sub/part }
+ * }
+ *
+ * In this example, there are four references to the same file, but since the third reference points
+ * to the ENTIRE file, that's the only one we need to dereference. The other three can just be
+ * remapped to point inside the third one.
+ *
+ * On the other hand, if the third reference DIDN'T exist, then the first and second would both need
+ * to be dereferenced, since they point to different parts of the file. The fourth reference does NOT
+ * need to be dereferenced, because it can be remapped to point inside the first one.
+ *
+ * @param {object[]} inventory
+ */
+function remap (inventory) {
+ // Group & sort all the $ref pointers, so they're in the order that we need to dereference/remap them
+ inventory.sort((a, b) => {
+ if (a.file !== b.file) {
+ // Group all the $refs that point to the same file
+ return a.file < b.file ? -1 : +1;
+ }
+ else if (a.hash !== b.hash) {
+ // Group all the $refs that point to the same part of the file
+ return a.hash < b.hash ? -1 : +1;
+ }
+ else if (a.circular !== b.circular) {
+ // If the $ref points to itself, then sort it higher than other $refs that point to this $ref
+ return a.circular ? -1 : +1;
+ }
+ else if (a.extended !== b.extended) {
+ // If the $ref extends the resolved value, then sort it lower than other $refs that don't extend the value
+ return a.extended ? +1 : -1;
+ }
+ else if (a.indirections !== b.indirections) {
+ // Sort direct references higher than indirect references
+ return a.indirections - b.indirections;
+ }
+ else if (a.depth !== b.depth) {
+ // Sort $refs by how close they are to the JSON Schema root
+ return a.depth - b.depth;
+ }
+ else {
+ // Determine how far each $ref is from the "definitions" property.
+ // Most people will expect references to be bundled into the the "definitions" property if possible.
+ let aDefinitionsIndex = a.pathFromRoot.lastIndexOf("/definitions");
+ let bDefinitionsIndex = b.pathFromRoot.lastIndexOf("/definitions");
+
+ if (aDefinitionsIndex !== bDefinitionsIndex) {
+ // Give higher priority to the $ref that's closer to the "definitions" property
+ return bDefinitionsIndex - aDefinitionsIndex;
+ }
+ else {
+ // All else is equal, so use the shorter path, which will produce the shortest possible reference
+ return a.pathFromRoot.length - b.pathFromRoot.length;
+ }
+ }
+ });
+
+ let file, hash, pathFromRoot;
+ for (let entry of inventory) {
+ // console.log('Re-mapping $ref pointer "%s" at %s', entry.$ref.$ref, entry.pathFromRoot);
+
+ if (!entry.external) {
+ // This $ref already resolves to the main JSON Schema file
+ entry.$ref.$ref = entry.hash;
+ }
+ else if (entry.file === file && entry.hash === hash) {
+ // This $ref points to the same value as the prevous $ref, so remap it to the same path
+ entry.$ref.$ref = pathFromRoot;
+ }
+ else if (entry.file === file && entry.hash.indexOf(hash + "/") === 0) {
+ // This $ref points to a sub-value of the prevous $ref, so remap it beneath that path
+ entry.$ref.$ref = Pointer.join(pathFromRoot, Pointer.parse(entry.hash.replace(hash, "#")));
+ }
+ else {
+ // We've moved to a new file or new hash
+ file = entry.file;
+ hash = entry.hash;
+ pathFromRoot = entry.pathFromRoot;
+
+ // This is the first $ref to point to this value, so dereference the value.
+ // Any other $refs that point to the same value will point to this $ref instead
+ entry.$ref = entry.parent[entry.key] = $Ref.dereference(entry.$ref, entry.value);
+
+ if (entry.circular) {
+ // This $ref points to itself
+ entry.$ref.$ref = entry.pathFromRoot;
+ }
+ }
+
+ // console.log(' new value: %s', (entry.$ref && entry.$ref.$ref) ? entry.$ref.$ref : '[object Object]');
+ }
+}
+
+/**
+ * TODO
+ */
+function findInInventory (inventory, $refParent, $refKey) {
+ for (let i = 0; i < inventory.length; i++) {
+ let existingEntry = inventory[i];
+ if (existingEntry.parent === $refParent && existingEntry.key === $refKey) {
+ return existingEntry;
+ }
+ }
+}
+
+function removeFromInventory (inventory, entry) {
+ let index = inventory.indexOf(entry);
+ inventory.splice(index, 1);
+}
+
+
+/***/ }),
+
+/***/ "./node_modules/@apidevtools/json-schema-ref-parser/lib/dereference.js":
+/*!*****************************************************************************!*\
+ !*** ./node_modules/@apidevtools/json-schema-ref-parser/lib/dereference.js ***!
+ \*****************************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const $Ref = __webpack_require__(/*! ./ref */ "./node_modules/@apidevtools/json-schema-ref-parser/lib/ref.js");
+const Pointer = __webpack_require__(/*! ./pointer */ "./node_modules/@apidevtools/json-schema-ref-parser/lib/pointer.js");
+const { ono } = __webpack_require__(/*! @jsdevtools/ono */ "./node_modules/@jsdevtools/ono/esm/index.js");
+const url = __webpack_require__(/*! ./util/url */ "./node_modules/@apidevtools/json-schema-ref-parser/lib/util/url.js");
+
+module.exports = dereference;
+
+/**
+ * Crawls the JSON schema, finds all JSON references, and dereferences them.
+ * This method mutates the JSON schema object, replacing JSON references with their resolved value.
+ *
+ * @param {$RefParser} parser
+ * @param {$RefParserOptions} options
+ */
+function dereference (parser, options) {
+ // console.log('Dereferencing $ref pointers in %s', parser.$refs._root$Ref.path);
+ let dereferenced = crawl(parser.schema, parser.$refs._root$Ref.path, "#", new Set(), new Set(), new Map(), parser.$refs, options);
+ parser.$refs.circular = dereferenced.circular;
+ parser.schema = dereferenced.value;
+}
+
+/**
+ * Recursively crawls the given value, and dereferences any JSON references.
+ *
+ * @param {*} obj - The value to crawl. If it's not an object or array, it will be ignored.
+ * @param {string} path - The full path of `obj`, possibly with a JSON Pointer in the hash
+ * @param {string} pathFromRoot - The path of `obj` from the schema root
+ * @param {Set