All Downloads are FREE. Search and download functionalities are using the official Maven repository.

inspector.inspector.js Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2013 eBay Software Foundation and ios-driver committers
 *
 * 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.
 */

Inspector.logLevel = 4; // 0=none, 1=error, 2=error +warning, 3= error,warning,info   4 = all

function Inspector(selector) {

    this.busy = false;

    this.lock = false;
    this.recorder = new Recorder(this);
    this.log = new Logger(this);
    this.selector = selector;

    this.screenshotPath = $("#screenshot").attr("src");
    this.jsTreeConfig = {
        "core": {
            "animation": 0,
            "load_open": true
        },
        "json_data": {
            "ajax": {
                "url": "tree"
            }
        },
        "themes": {
            "theme": "apple"
        },
        "plugins": ["themes", "json_data", "ui"]
    };

    this.init();
}

Inspector.prototype.reloadData = function () {
    var me = this;
    me.jstree = $(me.selector).jstree(me.jsTreeConfig);
    me.jstree.bind("loaded.jstree", function (event, data) {
        me.onTreeLoaded(event, data);
    });
    me.jstree.bind("hover_node.jstree", function (event, data) {
        me.onNodeMouseOver(event, data);
    });

}
/**
 *
 * @param selector {string} jquery selector of the element that will host the jsTree.
 */
Inspector.prototype.init = function () {
    var me = this;

    this.reloadData();

    $("#mouseOver").mousemove(function (event) {
        me.onMouseMove(event);
    });
    $("#mouseOver").click(function (event) {
        me.onMouseClick(event);
    });

    $(document).keydown(function (e) {

        console.log("keyboardDisplayed" + me.keyboardDisplayed);
        if (me.keyboardDisplayed) {
            // console.log("keyboardDisplayed");
        }
        var ESC_KEY = 27;
        if (e.ctrlKey) {
            me.toggleLock();
        } else if (e.keyCode === ESC_KEY) {
            me.toggleXPath();
        }
    });

    me.toggleXPath(false);

    $('#xpathInput').keyup(function () {
        var xpath = $(this).val();

        try {
            var elements = me.findElementsByXpath2(xpath);
            me.select(elements);
            $('#xpathLog').html("found " + elements.length + " results.");
        } catch (err) {
            me.unselect();
            $('#xpathLog').html("Error: " + err.message);
        }
    });

}

/**
 * select the list of elements.Elements are XML nodes from a xpath query.
 * @param elements
 */
Inspector.prototype.select = function (elements) {
    this.unselect();
    for (var i = 0; i < elements.length; i++) {
        var element = elements[i];
        if (element.getAttributeNode("ref")) {
            var ref = element.getAttributeNode("ref").nodeValue;
            var node = new NodeFinder(this.root).getNodeByReference(ref);
            this.selectOne(node, elements.length === 1);
        }

    }
}

/**
 * mouse over the object tree.
 * @param e
 * @param data
 */
Inspector.prototype.onNodeMouseOver = function (e, data) {
    if (!this.lock) {
        this.unselect();
        this.selectOne(data, true);
    }
}

/**
 * init all variable when the tree is done loading.
 * @param event
 * @param data
 */
Inspector.prototype.onTreeLoaded = function (event, data) {
    this.root = this.jstree.jstree('get_json')[0];
    this.xml = this.root.metadata.xml;
    this.keyboardDisplayed = this.root.metadata.keyboard;
    var webView = this.extractWebView(this.getRootNode());
    if (webView != null) {
        setHTMLSource(webView.metadata.source);
    } else {
        setHTMLSource(null);
    }
    this.expandTree();
    this.loadXpathContext();

    if (this.recorder.on) {
        $("#screenshot").attr("src", this.screenshotPath + "?time=" + new Date().getTime());
        this.busy = false;
    }
}

/**
 * unselect everything. Highlight on the device, the tree, and the optional details.
 */
Inspector.prototype.unselect = function () {
    $('#details').html("");
    $('#xpathLog').html("");
    this.jstree.jstree('deselect_all');
    this.highlight();
}

/**
 * Select the specified node. Node is a node from jstree.
 * @param node
 * @param displayDetails {boolean} true will display the info in the right column. If more than one
 * node is displayed, the node details will overwrite each other.
 */
Inspector.prototype.selectOne = function (node, displayDetails) {
    var rect;
    var type;
    var ref;
    var name;
    var label;
    var value;
    var l10n;

    if (node.metadata) {// from tree parsing, json node
        rect = node.metadata.rect;
        type = node.metadata.type;
        ref = node.metadata.reference;
        name = node.metadata.name;
        label = node.metadata.label;
        value = node.metadata.value;
        l10n = node.metadata.l10n
    } else {// from listener, jstree node
        rect = node.rslt.obj.data("rect");
        type = node.rslt.obj.data('type');
        ref = node.rslt.obj.data('reference');
        name = node.rslt.obj.data('name');
        label = node.rslt.obj.data('label');
        value = node.rslt.obj.data('value');
        l10n = node.rslt.obj.data('l10n');

    }

    this.jstree.jstree('select_node', '#' + ref);
    var translationFound = (l10n.matches != 0);

    this.highlight(rect.x, rect.y, rect.h, rect.w, translationFound, ref);
    if (displayDetails) {
        this.showDetails(type, ref, name, label, value, rect, l10n);
        this.showActions(type, ref);
    }

}

/**
 * show the info about a node in the right details section.
 * @param type
 * @param ref
 * @param na
 * @param label
 * @param value
 * @param rect
 * @param l10n
 */
Inspector.prototype.showDetails = function (type, ref, na, label, value, rect, l10n) {
    var prettyL10N = "";

    if (l10n) {
        prettyL10N = "

L10N

"; var matches = l10n.matches; prettyL10N += "

Matches: " + matches + "

"; if (matches > 0) { prettyL10N += "

Key: " + l10n.key + "

"; var langs = l10n.langs; for (var name in langs) { var result = langs[name]; for (var a in result) { prettyL10N += "

" + a + " : " + result[a] + "

"; } } } } else { prettyL10N = "no l10n for --" + name + "--"; } $('#details').html("

Details

" + "

Type: " + type + "

" + "

Reference: " + ref + "

" + "

Name: " + na + "

" + "

Label: " + label + "

" + "

Value: " + value + "

" + "

Rect: x=" + rect.x + ",y=" + rect.y + ",h=" + rect.h + "w=" + rect.w + "

" + prettyL10N); }; /** * Highlight an area on the device. * @param x * @param y * @param h * @param w * @param translationFound * @param ref */ Inspector.prototype.highlight = function (x, y, h, w, translationFound, ref) { if (typeof x != 'undefined') { var d = $("
", { "class": "hightlight" }); d.appendTo("#rotationCenter"); d.css('border', "1px solid red"); d.css('left', x + realOffsetX + 'px'); d.css('top', y + realOffsetY + 'px'); d.css('height', h + 'px'); d.css('width', w + 'px'); d.css('position', 'absolute'); d.css('background-color', 'yellow'); d.css('z-index', '3'); d.css('opacity', '0.5'); d.css('opacity', '0.5'); d.html("
ref:" + ref + "
"); var color; if (translationFound) { color = "blue"; } else { color = "yellow"; } d.css("background-color", color); } else { $(".hightlight").remove(); } } Inspector.prototype.showActions = function (type, ref) { // check action per type. $('#reference').html(""); } Inspector.prototype.getRootNode = function () { if (this.root) { return this.root; } else { throw new Error('Cannot access the root node. The tree is not fully loaded'); } } Inspector.prototype.expandTree = function () { this.jstree.jstree("open_all"); } Inspector.prototype.extractWebView = function (node) { var type = node.metadata.type; if ("UIAWebView" === type) { return node; } else { var children = node.children; if (children) { for (var i = 0; i < children.length; i++) { var child = children[i]; var res = this.extractWebView(child); if (res) { return res; } } } } return null; } Inspector.prototype.getTreeAsXMLString = function () { if (this.xml) { return this.xml; } else { throw new Error('Cannot get the xml for that tree.The tree is not fully loaded'); } }; /** * init the xpath search content from the XML raw string. */ Inspector.prototype.loadXpathContext = function () { var parseXml; if (window.DOMParser) { parseXml = function (xmlStr) { return (new window.DOMParser()).parseFromString(xmlStr, "text/xml"); }; } else if ("undefined" !== typeof window.ActiveXObject && new window.ActiveXObject("Microsoft.XMLDOM")) { parseXml = function (xmlStr) { var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(xmlStr); return xmlDoc; }; } else { parseXml = function (xmlStr) { return null; } } var xmlObject = parseXml(this.getTreeAsXMLString()); this.xpathContext = xmlObject.ownerDocument == null ? xmlObject.documentElement : xmlObject.ownerDocument.documentElement; } /** * find elements by xpath. * @param xpath * @return {array} of elements. * @throws Error if the xpath is invalid. */ Inspector.prototype.findElementsByXpath2 = function (xpath) { var res = $(this.xpathContext).xpath(xpath); return res; } /** * mouse move for the device mouse over. * @param event */ Inspector.prototype.onMouseMove = function (event) { if (!this.lock) { //var x = event.pageX / scale - realOffsetX; //var y = event.pageY / scale - (realOffsetY + 45); var parentOffset = $("#mouseOver").offset(); //or $(this).offset(); if you really just want the current element's offset var x = event.pageX - parentOffset.left; var y = event.pageY - parentOffset.top; x = x / scale; y = y / scale; console.log(x + "," + y); var finder = new NodeFinder(this.root); var node = finder.getNodeByPosition(x, y); if (node) { this.unselect(); this.selectOne(node, true); } else { console.log('couldn t find element at ' + x + ' , ' + y); } } } /** * mouse move for the device mouse over. * @param event */ Inspector.prototype.onMouseClick = function (event) { if (this.recorder.on && !this.busy) { this.busy = true; var parentOffset = $("#mouseOver").offset(); //or $(this).offset(); if you really just want the current element's offset var x = event.pageX - parentOffset.left; var y = event.pageY - parentOffset.top; x = x / scale; y = y / scale; console.log(x + "," + y); var finder = new NodeFinder(this.root); var node = finder.getNodeByPosition(x, y); if (node) { var xpath = this.findXpathExpression(x, y, node); var confirm = this.findElementsByXpath2(xpath); if (confirm.length === 1) { this.recorder.recordClick(xpath); this.recorder.forwardClick(x, y); } else { error(xpath + " should have a single result.It has " + confirm.length); } } else { warning("couldn't find an element for that click"); } } } Inspector.prototype.refineXpathExpression = function (x, y, node, xpath, elements) { var o = node.metadata; var refined = "(" + xpath + ")"; for (var i = 1; i <= elements.length; i++) { var res = refined + "[" + i + "]"; var els = this.findElementsByXpath2(res); if (els.length == 1) { this.log.debug(els[0]); var id = els[0].ref; if (o.ref === id) { return res; } } else { console.log("Error. Expected a single result for " + res + ".Found " + els.length); } } console.log("Cannot refine xpath enough to get a single result."); }; /** * Find the easiest xpath expression for the node located @ x,y. * @param x * @param y * @param node * @return {string} the xpath expression. */ Inspector.prototype.findXpathExpression = function (x, y, node) { var o = node.metadata; var xpath = "//" + o.type; if (o.name && o.name !== 'null') { xpath += "[@name='" + o.name + "']"; } else if (o.label && o.label !== 'null') { xpath += "[@label='" + o.label + "']"; } else if (o.value && o.value !== 'null') { xpath += "[@value='" + o.value + "']"; } else { this.log.debug("no attribute.xpath will be generic."); } var elements = this.findElementsByXpath2(xpath); if (elements.length == 0) { this.log.error("Error. The xpath doesn't seem to match anything."); } else if (elements.length > 1) { this.log.debug("There are several elements matching this xpath expression." + "It is not unique enough"); return this.refineXpathExpression(x, y, node, xpath, elements); } else { return xpath; this.log.debug("ok. Found an xpath expression unique enough"); } } /** * toggle the lock mode for the page. Mouse over are disabled when the page is locked. */ Inspector.prototype.toggleLock = function () { this.lock = !this.lock; } /** * toggle the Xpath overlay. * @param force */ Inspector.prototype.toggleXPath = function (force) { var show = false; if (typeof force != 'undefined') { show = force; this.xpathMode = show; } else { show = !this.xpathMode; } if (show) { this.xpathMode = true; $("#xpathHelper").dialog({ resizable: false, dialogClass: "no-close" }); $("#xpathHelper").show(); $("#xpathInput").focus(); } else { this.xpathMode = false; $("#xpathHelper").hide(); $("#xpathInput").blur(); } } function NodeFinder(rootNode) { this.matchScore = -1; this.candidate = null; this.rootNode = rootNode; this._hasCorrectPosition = function (node, x, y) { var currentX = node.metadata.rect.x; var currentY = node.metadata.rect.y; var currentH = node.metadata.rect.h; var currentW = node.metadata.rect.w; if ((currentX <= x) && (x <= (currentX + currentW))) { if ((currentY <= y) && (y <= (currentY + currentH))) { return true; } } return false; }; this._assignIfBetterCandidate = function (newNode, x, y) { if (this._hasCorrectPosition(newNode, x, y)) { var surface = (newNode.metadata.rect.h * newNode.metadata.rect.w); if (this.candidate) { if (surface < this.matchScore) { this.matchScore = surface; this.candidate = newNode; } } else { this.matchScore = surface; this.candidate = newNode; } } }; this.getNodeByPosition = function (x, y) { this._getCandidate(this.rootNode, x, y); return this.candidate; }; this.getNodeByReference = function (ref) { return this._getNodeByReference(this.rootNode, ref); } this._getNodeByReference = function (node, ref) { var reference = node.metadata.reference; if (reference === ref) { return node; } else { if (node.children) { for (var i = 0; i < node.children.length; i++) { var child = node.children[i]; var correctOne = this._getNodeByReference(child, ref); if (correctOne) { return correctOne; } } } } } this._getCandidate = function (from, x, y) { this._assignIfBetterCandidate(from, x, y); if (from.children) { for (var i = 0; i < from.children.length; i++) { var child = from.children[i]; this._getCandidate(child, x, y); } } }; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy