namespace com.keyman.dom { // Defines DOM-related utility functions that are not reliant on KMW's internal state. export class Utils { /** * Function getAbsoluteX * Scope Public * @param {Object} Pobj HTML element * @return {number} * Description Returns x-coordinate of Pobj element absolute position with respect to page */ static getAbsoluteX(Pobj: HTMLElement): number { // I1476 - Handle SELECT overlapping END var Lobj: HTMLElement if(!Pobj) { return 0; } var Lcurleft = Pobj.offsetLeft ? Pobj.offsetLeft : 0; Lobj = Pobj; // I2404 - Support for IFRAMEs if (Lobj.offsetParent) { while (Lobj.offsetParent) { Lobj = Lobj.offsetParent as HTMLElement; Lcurleft += Lobj.offsetLeft; } } // Correct position if element is within a frame (but not if the controller is in document within that frame) // We used to reference a KMW state variable `this.keyman._MasterDocument`, but it was only ever set to `window.document`. if(Lobj && Lobj.ownerDocument && (Pobj.ownerDocument != window.document)) { var Ldoc=Lobj.ownerDocument; // I2404 - Support for IFRAMEs if(Ldoc && Ldoc.defaultView && Ldoc.defaultView.frameElement) { return Lcurleft + Utils.getAbsoluteX(Ldoc.defaultView.frameElement) - Ldoc.documentElement.scrollLeft; } } return Lcurleft; } /** * Function getAbsoluteY * Scope Public * @param {Object} Pobj HTML element * @return {number} * Description Returns y-coordinate of Pobj element absolute position with respect to page */ static getAbsoluteY(Pobj: HTMLElement): number { var Lobj: HTMLElement if(!Pobj) { return 0; } var Lcurtop = Pobj.offsetTop ? Pobj.offsetTop : 0; Lobj = Pobj; // I2404 - Support for IFRAMEs if (Lobj.ownerDocument && Lobj instanceof Lobj.ownerDocument.defaultView.HTMLElement) { while (Lobj.offsetParent) { Lobj = Lobj.offsetParent as HTMLElement; Lcurtop += Lobj.offsetTop; } } // Correct position if element is within a frame (but not if the controller is in document within that frame) // We used to reference a KMW state variable `this.keyman._MasterDocument`, but it was only ever set to `window.document`. if(Lobj && Lobj.ownerDocument && (Pobj.ownerDocument != window.document)) { var Ldoc=Lobj.ownerDocument; // I2404 - Support for IFRAMEs if(Ldoc && Ldoc.defaultView && Ldoc.defaultView.frameElement) { return Lcurtop + Utils.getAbsoluteY(Ldoc.defaultView.frameElement) - Ldoc.documentElement.scrollTop; } } return Lcurtop; } /** * Checks the type of an input DOM-related object while ensuring that it is checked against the correct prototype, * as class prototypes are (by specification) scoped upon the owning Window. * * See https://stackoverflow.com/questions/43587286/why-does-instanceof-return-false-on-chrome-safari-and-edge-and-true-on-firefox * for more details. * * @param {Element|Event} Pelem An element of the web page or one of its IFrame-based subdocuments. * @param {string} className The plain-text name of the expected Element type. * @return {boolean} */ static instanceof(Pelem: Event|EventTarget, className: string): boolean { // We must write special checks for our custom-defined element types! if(className == "TouchAliasElement") { if(this.instanceof(Pelem, "HTMLDivElement")) { let div = Pelem; // We should probably implement a slightly more robust check, but this should get us started well enough. return div['base'] !== undefined; } else { return false; } } var scopedClass; if (Pelem['Window']) { // Window objects contain the class definitions for types held within them. So, we can check for those. return className == 'Window'; } else if (Pelem['defaultView']) { // Covers Document. scopedClass = Pelem['defaultView'][className]; } else if(Pelem['ownerDocument']) { scopedClass = (Pelem as Node).ownerDocument.defaultView[className]; } else if(Pelem['target']) { var event = Pelem as Event; if(this.instanceof(event.target, 'Window')) { scopedClass = event.target[className]; } else if(this.instanceof(event.target, 'Document')) { scopedClass = (event.target as Document).defaultView[className]; } else if(this.instanceof(event.target, 'HTMLElement')) { scopedClass = (event.target as HTMLElement).ownerDocument.defaultView[className]; } } if(scopedClass) { return Pelem instanceof scopedClass; } else { return false; } } } }