UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

331 lines (245 loc) 11.9 kB
/* Siesta 5.6.1 Copyright(c) 2009-2022 Bryntum AB https://bryntum.com/contact https://bryntum.com/products/siesta/license */ Ext.define('Siesta.Project.Browser.UI.ComponentInspector', { extend : 'Ext.util.Observable', inspectedComponent : null, inspectedComponentXType : null, boxIndicatorEl : null, active : false, window : null, trackMouse : true, showSelectorText : true, bufferTime : 30, injectIntoTestFrame : false, getIndicatorEl : function () { return this.boxIndicatorEl; }, getExt : function () { return this.window.Ext; }, start : function (window, containerEl) { window = window || this.window; if (!window) throw 'Must provide a window context for the inspector'; this.window = window; var _Ext = this.getExt(); var me = this; var wrap = this.injectIntoTestFrame ? window.document.body : (containerEl || window.document.body); var boxSizingStyle = 'box-sizing: border-box;-moz-box-sizing: border-box;-ms-box-sizing: border-box;-webkit-box-sizing: border-box;'; me.boxIndicatorEl = me.boxIndicatorEl || Ext.fly(wrap).createChild({ cls : 'target-inspector-box', style : 'position : absolute;left:0;top:0;pointer-events : none;z-index : 100001; border : 2px solid red;transition-property : transform,border-color;transition-duration : 0.3s;', children : [ { tag : 'div', style : 'border:1px solid;border-color:inherit;width : 13px; height : 13px;position : absolute;margin-top : -8px;margin-left : -8px;border-radius:19px;transition-property : transform;transition-duration : 0.3s;' + boxSizingStyle, cls : 'target-inspector-coordinate', html : '<div style="border-left: 1px solid;border-color:inherit;position: absolute;top: -3px;left: 5px;height: 6px;' + boxSizingStyle +'"></div>' + '<div style="border-left: 1px solid;border-color:inherit;position: absolute;bottom: -3px;left: 5px;height: 6px;' + boxSizingStyle +'"></div>' + '<div style="border-top: 1px solid;border-color:inherit;position: absolute;top: 5px;left: -3px;width: 6px;' + boxSizingStyle +'"></div>' + '<div style="border-top: 1px solid;border-color:inherit;position: absolute;top: 5px;right: -3px;width: 6px;' + boxSizingStyle +'"></div>' } ].concat(this.showSelectorText ? { tag : 'a', cls : 'target-inspector-label', target : '_blank' } : []) }); if (this.trackMouse && _Ext && _Ext.getBody) { this.toggleMouseMoveListener(true); _Ext.getBody().on('click', this.onInspectionClick, { me : this }); } this.fireEvent('start', this); this.active = true; }, stop : function (suppressEvent) { if (!this.active) return; this.active = false; var _Ext = this.getExt(); Ext.destroy(this.boxIndicatorEl); this.boxIndicatorEl = null; if (!suppressEvent) { this.fireEvent('stop', this); } if (_Ext && _Ext.getBody) { this.toggleMouseMoveListener(false); _Ext.getBody().un('click', this.onInspectionClick, { me : this }); } this.inspectedComponent = this.inspectedComponentXType = null; }, // Listen for mousemove in the frame and any direct iframe children too toggleMouseMoveListener : function (enabled) { var _Ext = this.getExt(); if (!_Ext) return; var frames = _Ext.getBody().select('iframe'); var fn = enabled ? 'on' : 'un'; // Avoid using "this" directly due to Touch incompatibilities with Ext _Ext.getBody()[fn]('mousemove', this.onMouseMove, { me : this }, { buffer : this.bufferTime }); for (var i = 0; i < frames.getCount(); i++) { var innerExt = frames.item(i).dom.contentWindow.Ext; // Avoid using "this" directly due to Touch incompatibilities with Ext innerExt && innerExt.getBody && innerExt.getBody()[fn]('mousemove', this.onMouseMove, { me : this }, { buffer : this.bufferTime }); } }, onInspectionClick : function (e, t) { if (!this.boxIndicatorEl) return; // Avoid using "this" directly due to Touch incompatibilities with Ext var me = this.me; me.toggleMouseMoveListener(false); // If user clicks on a non-component, or clicking outside currently selected component - we abort if (!me.inspectedComponent || me.findComponentByTarget(t) !== me.inspectedComponent) { me.stop(); } else { me.fireEvent('targetselected', me, me.inspectedComponent, me.inspectedComponentXType); } }, onMouseMove : function (e, t) { //Have to avoid using "this" directly due to Touch incompatibilities with Ext var me = this.me; if (!me.boxIndicatorEl) return; var cmp = me.findComponentByTarget(t); if (cmp) { if (cmp === me.inspectedComponent) return; var xtype = me.resolveComponentXtype(cmp); me.inspectedComponent = cmp; me.inspectedComponentXType = xtype; me.highlightTarget(cmp.el.dom); me.updateHighlightContent(cmp, xtype); me.fireEvent('targethover', me, me.inspectedComponent, me.inspectedComponentXType); } }, resolveComponentXtype : function (cmp) { var xtype = (cmp.getXType && cmp.getXType()) || cmp.xtype; // If the found component doesn't have an own xtype, look up the superclass chain to find one if (!xtype) { var cls = cmp; for (var i = 0; i < 10 && !xtype; i++) { cls = cmp.superclass; xtype = cls.xtype; } } return xtype; }, updateHighlightContent : function (cmp, xtype) { var html; var link = { tag : 'a', cls : 'target-inspector-label', href : '#' }; if (typeof cmp === 'string') { html = cmp; } else if (Ext.ClassManager) { // If recorder is visible, let's add some targeting suggestions var recorderPanel = Ext.ComponentQuery.query('recorderpanel')[0]; var targetWindow = cmp.el.dom.ownerDocument.defaultView; var pageExtHasCQ = targetWindow && targetWindow.Ext && targetWindow.Ext.ComponentQuery; if (recorderPanel && recorderPanel.isVisible()) { var cq; // Make sure this is an Ext JS page and the recorder used is the Ext JS recorder (not the generic one) if (pageExtHasCQ && recorderPanel.recorder.extractor.findComponentQueryFor) { cq = recorderPanel.recorder.extractor.findComponentQueryFor(cmp); cq = (cq && cq.query) || xtype; } html = '>>' + (cq || xtype); } else { var clsName = this.findExtAncestorClassName(cmp); if (clsName) { var docsPath = Siesta.Resource('Siesta.Project.Browser.UI.DomContainer', 'docsUrlText'); var framework; if (Ext.versions.touch) { framework = 'touch'; } else { framework = 'extjs'; } link.target = '_blank'; link.href = Ext.String.format(docsPath, framework, clsName); link.title = Siesta.Resource('Siesta.Project.Browser.UI.DomContainer', 'viewDocsText') + clsName; } html = xtype; } } if (html) { link.html = html; var linkEl = this.boxIndicatorEl.down('.target-inspector-label', true); linkEl.href = link.href; linkEl.title = link.title; linkEl.innerHTML = html; } }, highlightTarget : function (node, content, point) { var boxStyle = this.boxIndicatorEl.dom.style; if (!this.active) { this.start(); } if (node) { var offsets = this.getOffsets(node); var left = (Ext.fly(node).getX() - 2 + offsets[0]); var top = (Ext.fly(node).getY() - 2 + offsets[1]); var width = ((Ext.fly(node).getWidth() || (parseInt(node.style.width.substring(0, node.style.width.length - 2), 10))) + 2); var height = ((Ext.fly(node).getHeight() || (parseInt(node.style.height.substring(0, node.style.height.length - 2), 10))) + 2); if (this.injectIntoTestFrame) { left += this.window.document.body.scrollLeft; top += this.window.document.body.scrollTop; } var translateStyle = bowser.opera ? ('translate(' + left + 'px,' + top + 'px)') : ('translate3d(' + left + 'px,' + top + 'px, 0)'); // Regular getWidth/getHeight doesn't work if another iframe is on the page boxStyle.setProperty('transform', translateStyle) boxStyle.width = width + 'px'; boxStyle.height = height + 'px'; boxStyle['border-color'] = 'red'; if (this.showSelectorText && content) { this.updateHighlightContent(content); } } else { boxStyle['border-color'] = '#ccc'; } var crosshair = this.boxIndicatorEl.down('.target-inspector-coordinate', true); if (point) { translateStyle = bowser.opera ? ('translate(' + point[0] + 'px,' + point[1] + 'px)') : ('translate3d(' + point[0] + 'px, ' + point[1] + 'px, 0)'); crosshair.style.setProperty('display', 'block'); crosshair.style.setProperty('transform', translateStyle) } else { crosshair.style.setProperty('display', 'none'); } }, findComponentByTarget : function (target) { var Ext = this.getExt(); var testDoc = this.window.document; // Handle potentially having another Ext copy loaded in another frame if (target.ownerDocument !== testDoc) { var innerFrame = (target.ownerDocument.parentWindow || target.ownerDocument.defaultView).frameElement; Ext = innerFrame.contentWindow.Ext; } var cmp while (!cmp && target && target.nodeName !== 'BODY') { cmp = Ext.getCmp(target.id); target = target.parentNode; } return cmp; }, getOffsets : function (node) { var targetDoc = this.window.document; var offsets = [0, 0] if (node.ownerDocument !== targetDoc) { var innerFrame = (node.ownerDocument.parentWindow || node.ownerDocument.defaultView).frameElement; offsets = Ext.fly(innerFrame).getXY(); offsets[0] -= node.ownerDocument.body.scrollLeft; offsets[1] -= node.ownerDocument.body.scrollTop; } return offsets; }, findExtAncestorClassName : function (cmp) { while (cmp) { var name = Ext.ClassManager.getName(cmp); if (name.match(/^Ext./)) return name; cmp = cmp.superclass; } return ''; } });