Xpath Locator

Capture and test Xpath locators

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Xpath Locator
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Capture and test Xpath locators
// @author       Maged Ahmed
// @include      *
// @grant        none
// @noframes
// @license      MIT
// ==/UserScript==
let highlightElementCount = 0;
let currentElement;
let highlightInterval;
let elements = [];
let elementIndex = 0;
const MENU = `
<br/>
<br/>
<fieldset>
    <legend>Find by xpath:</legend>
    <p><label>
        <textarea id="xpValue" placeholder="xpath: //*" rows="10" maxlength="9999"></textarea>
    </label></p>
    <p>
        <button id="xpElementFind" class="xpMenuBtn"><i class="fa fa-search"></i></button>
        <button id="xpElementPrev" class="xpMenuBtn"><i class="fa fa-arrow-left"></i></button>
        <button id="xpElementNext" class="xpMenuBtn"><i class="fa fa-arrow-right"></i></button>
        <button id="xpElementClear" class="xpMenuBtn"><i class="fa fa-close"></i></button>
        <button id="xpElementCopy" class="xpMenuBtn"><i class="fa fa-copy"></i></button>
        <span id="xpElementsCount"></span>
    </p>
</fieldset>
<fieldset>
    <legend>Capture xpath:</legend>
    <p>
        <label for="xpId">Add id</label>
        <input id="xpId" type="checkbox" checked="checked"/>
    </p>
    <p>
        <label for="xpName">Add name</label>
        <input id="xpName" type="checkbox" checked="checked"/>
    </p>
    <p>
        <label for="xpClass">Add class</label>
        <input id="xpClass" type="checkbox"/>
    </p>
    <p>
        <label for="xpType">Add type</label>
        <input id="xpType" type="checkbox"/>
    </p>
    <p><label for="xpText">Add text</label>
        <input id="xpText" type="checkbox"/>
    </p>
    <p>
        <label for="xpIndex">Add index</label>
        <input id="xpIndex" type="checkbox"/>
    </p>
    <p>
        <label for="xpLength">Xpath Length</label>
        <input type="number" id="xpLength" value="1" min="1" max="100">
    </p>
    <p>
        <button id="xpCapture"><i class="fa fa-location-arrow"></i> Capture</button>
        <button id="xpUpdate"><i class="fa fa-refresh"></i> Update</button>
    </p>
</fieldset>
<fieldset class="xpElementData">
    <legend>Element Properties</legend>
    <table>
    <col style="width:50px">
  <tr>
    <td>Tag</td>
    <td><div id="xpElementTag" style="max-height:100px; overflow:scroll"></div></td>
  </tr>
  <tr>
    <td>Id</td>
    <td><div id="xpElementId" style="max-height:100px; overflow:scroll"></div></td>
  </tr>
  <tr>
    <td>Name</td>
    <td><div id="xpElementName"  style="max-height:100px; overflow:scroll"></div></td>
  </tr>
  <tr>
    <td>Class</td>
    <td><div id="xpElementClass"  style="max-height:100px; overflow:scroll"></div></td>
  </tr>
  <tr>
    <td>Type</td>
    <td><div id="xpElementType"  style="max-height:100px; overflow:scroll"></div></td>
  </tr>
  <tr>
    <td>Text</td>
    <td><div id="xpElementText"  style="max-height:100px; overflow:scroll"></div></td>
  </tr>
  <tr>
    <td>Xpath</td>
    <td><div id="xpElementXpath"  style="max-height:100px; overflow:scroll"></div></td>
  </tr>
</table>
</fieldset>
`;
const SCRIPT_CSS = `
.selectedElement {
    color: lime !important;
                background-color: yellow !important;
                outline-offset: -2px !important;
    outline: 4px solid lime !important;
}
.capturedElement {
    color: aqua !important;
                background-color: yellow !important;
                outline-offset: -2px !important;
    outline: 4px solid aqua !important;
}
.xpMenu p,
.xpMenu label,
.xpMenu button,
.xpMenu a,
.xpMenu fieldset,
.xpMenu div,
.xpMenu span,
.xpMenuLink a {
                font: 16px arial, sans-serif;
                margin: 0;
    padding: .5 1em;
    min-height: 1;
    border: 1px solid transparent;
    box-shadow: none;
    outline: none;
                color: white;
}
.xpMenuLink a:hover {
    text-decoration: none;
                left: 0;
                color: white;
}
.xpMenuLink a {
                position: fixed;
                left: -100px;
                transition: 0.3s;
                padding: 5px;
                width: 100px;
                text-decoration: none;
                color: CornflowerBlue;
                border-radius: 0 20px 20px 0;
                z-index: 999999;
                top: 0px ;
                background-color: CornflowerBlue;
                white-space: nowrap;
                box-sizing: initial;
}
a.xpMenuLinkOpened{
                left: 0;
                color: white;
}
.xpMenu {
                height: 100%;
                width: 0;
                position: fixed;
                top: 0;
                left: 0;
                background-color: CornflowerBlue;
                overflow-x: hidden;
                transition: 0.5s;
                z-index: 999998;
}
.xpMenu a {
                text-decoration: none;
                font-size: 25px;
                color: black;
                display: block;
                transition: 0.3s;
}
.xpMenu a:hover {
    text-decoration: none;
                color: white;
}
.xpMenu .xpMenuClosebtn {
                color: DIMGRAY;
                position: absolute;
                top: 0;
                right: 25px;
                font-size: 36px;
                margin-left: 50px;
                cursor: pointer;
}
.xpMenu button {
                font-size: 14px !important;
                appearance: button !important;
                cursor: pointer !important;
                background-color: lightblue !important;
}
.xpMenu .xpMenuBtn {
                color: white !important;
                background-color: CornflowerBlue !important;
                border: 2px solid #6495ED !important;
                border-radius: 10px !important;
}
.xpMenu textarea {
                width: 100%;
                box-sizing: border-box;
                resize: vertical ;
                color: black;
}
.xpMenu fieldset {
                display: block;
                margin-inline-start: 2px;
                margin-inline-end: 2px;
                padding-block-start: 0.35em;
                padding-inline-start: 0.75em;
                padding-inline-end: 0.75em;
                padding-block-end: 0.625em;
                min-inline-size: min-content;
                border-width: 2px;
                border-style: groove;
                border-color: threedface;
                border-image: initial;
}
.xpMenu table {
  font: 14px arial, sans-serif;
  width: 100%;
  height: 100%;
  table-layout:fixed;
}
.xpMenu td div {
  font: 14px arial, sans-serif;
}
.xpMenu td {
  text-align: left;
  border: 1px solid white;
  word-wrap: break-word;
  white-space: pre-wrap;
}
`;
function openNav() {
    // const currentPosition = window.scrollY;
    // window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
    // setTimeout(() => { window.scrollTo(0,currentPosition); }, Math.floor(document.body.scrollHeight/25));
    document.getElementById("xpMenuLinkOpen").classList.add("xpMenuLinkOpened");
    document.getElementById("xpMenu").style.width = "300px";
    document.body.style.marginLeft = "300px";
    document.querySelectorAll('*').forEach(function(node) {
        if (window.getComputedStyle(node, null).getPropertyValue('position') === 'fixed' &&
            window.getComputedStyle(node, null).getPropertyValue('left') === "0px" &&
            (node.id !== 'xpMenuLinkOpen' && node.id !== 'xpMenu')) {
            node.style.marginLeft = "300px";
        }
    });
}
function closeNav() {
    document.getElementById("xpMenuLinkOpen").classList.remove("xpMenuLinkOpened");
    document.getElementById("xpMenu").style.width = "0";
    document.body.style.marginLeft = "0";
    document.querySelectorAll('*').forEach(function(node) {
        if (window.getComputedStyle(node, null).getPropertyValue('position') === 'fixed') {
            node.style.marginLeft = "0";
        }
    });
    clearAll();
}
function getElementsByXpath(path) {
    const result = document.evaluate(path, document, null, XPathResult.ANY_TYPE, null);
    let node, nodes = [];
    while ((node = result.iterateNext())) {
        nodes.push(node);
    }
    return nodes;
}
function unHighlightElement() {
    if (currentElement instanceof HTMLElement) {
        clearInterval(highlightInterval);
        currentElement.classList.remove("capturedElement");
        currentElement.classList.remove("selectedElement");
        highlightInterval = null;
    }
}
function highlightElement() {
    currentElement.classList.toggle("selectedElement");
    highlightElementCount++;
    if (highlightElementCount === 5) {
        clearInterval(highlightInterval);
        highlightElementCount = 0;
        highlightInterval = null;
    }
}
function findElements() {
    clearElements();
    unsetCapture();
    const xpath = document.getElementById("xpValue").value;
    elements = getElementsByXpath(xpath);
    elementIndex = 0;
    selectElement()
}
function nextElement() {
    if (elementIndex < elements.length - 1) {
        elementIndex++;
        selectElement()
    }
}
function prevElement() {
    if (elementIndex > 0) {
        elementIndex--;
        selectElement()
    }
}
function selectElement() {
    unHighlightElement();
    if (elements.length > 0 && (currentElement = elements[elementIndex]) && currentElement instanceof HTMLElement) {
        currentElement.scrollIntoView({
            behavior: "smooth",
            block: "center",
            inline: "center"
        });
        document.getElementById("xpElementsCount").textContent = "[" + (elementIndex + 1) + " / " + elements.length + "]";
        displayElementInfo();
        highlightInterval = setInterval(function() {
            highlightElement();
        }, 100);
    } else {
        document.getElementById("xpElementsCount").textContent = '';
    }
}
function displayElementInfo() {
    document.getElementById("xpElementTag").textContent = currentElement.tagName;
    document.getElementById("xpElementId").textContent = currentElement.id;
    document.getElementById("xpElementName").textContent = currentElement.name;
    document.getElementById("xpElementClass").textContent = currentElement.className;
    document.getElementById("xpElementType").textContent = currentElement.type;
    document.getElementById("xpElementText").textContent = currentElement.textContent;
    document.getElementById("xpElementXpath").textContent = getXPath(currentElement, true, true, true, false, false, true, 5);
}
function clearElements() {
    unHighlightElement();
    document.getElementById("xpElementsCount").textContent = '';
    elements = [];
    elementIndex = 0;
}
function clearAll() {
    document.getElementById("xpValue").value = '';
    clearElements();
    unsetCapture();
}
function unsetCapture() {
    unHighlightElement();
    document.onmousemove = null;
    document.oncontextmenu = null;
}
function captureElement() {
    if (currentElement) {
        const xpId = document.getElementById("xpId").checked;
        const xpName = document.getElementById("xpName").checked;
        const xpClass = document.getElementById("xpClass").checked;
        const xpType = document.getElementById("xpType").checked;
        const xpText = document.getElementById("xpText").checked;
        const xpIndex = document.getElementById("xpIndex").checked;
        const xpLength = document.getElementById("xpLength").value;
        document.getElementById("xpValue").value = getXPath(currentElement, xpId, xpName, xpClass, xpType, xpText, xpIndex, xpLength);
        findElements();
    }
}
function setCapture() {
    clearElements();
    unsetCapture();
    document.onmousemove = function(event) {
        if (currentElement) {
            currentElement.classList.remove("capturedElement");
        }
        const target = event.target;
        target.classList.add("capturedElement");
        currentElement = target;
    };
    document.oncontextmenu = function() {
        unsetCapture();
        captureElement();
        return false
    };
}
function copyXpath() {
    document.getElementById("xpValue").select();
    document.execCommand("copy");
}
function getXPath(element, getId, getName, getClass, getType, getText, getIndex, maxCount) {
    let uiElementText;
    let xpath = '';
    let count = 0;
    while (element) {
        let pathIndex = "";
        if (getIndex) {
            try {
                let index = 0;
                for (let sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
                    if (sibling.nodeType === Node.DOCUMENT_TYPE_NODE) {
                        continue;
                    }
                    if (sibling.nodeName === element.nodeName) {
                        ++index;
                    }
                }
                pathIndex = (index ? "[" + (index + 1) + "]" : "[1]");
            } catch (err) {
                continue;
            }
        }
        let nodeXpath = '';
        try {
            if (element.id && getId) {
                nodeXpath += '@id=\"' + element.id + '\"';
            }
        } catch (err) {
        }
        try {
            if (element.name && getName) {
                if (nodeXpath !== '') {
                    nodeXpath += ' and ';
                }
                nodeXpath += '@name=\"' + element.name + '\"';
            }
        } catch (err) {
        }
        try {
            if (element.hasAttribute("type") && typeof element.type !== 'undefined' && getType) {
                if (nodeXpath !== '') {
                    nodeXpath += ' and ';
                }
                nodeXpath += '@type=\"' + element.type + '\"';
            }
        } catch (err) {
        }
        try {
            if (element.className && nodeXpath === '' && getClass) {
                if (nodeXpath !== '') {
                    nodeXpath += ' and ';
                }
                nodeXpath += '@class=\"' + element.className + '\"';
            }
        } catch (err) {
        }
        try {
            if (element.textContent && element.textContent.length < 50 && element.textContent === element.innerHTML && getText) {
                uiElementText = element.textContent;
                try {
                    uiElementText = uiElementText.trim();
                } catch (err) {
                    uiElementText = uiElementText.replace(/^\s+|\s+$/g, '');
                }
                if (nodeXpath !== '') {
                    nodeXpath += ' and ';
                }
                if (element.textContent === uiElementText && element.textContent.length > 0) {
                    nodeXpath += 'text()=\"' + element.textContent + '\"';
                } else {
                    nodeXpath += 'normalize-space() = \"' + uiElementText + '\"';
                }
            } else if (element.text && element.text.length < 50 && element.text === element.innerHTML && getText) {
                uiElementText = element.text;
                try {
                    uiElementText = uiElementText.trim();
                } catch (err) {
                    uiElementText = uiElementText.replace(/^\s+|\s+$/g, '');
                }
                if (uiElementText.length > 0) {
                    if (nodeXpath !== '') {
                        nodeXpath += ' and ';
                    }
                    if (element.text === uiElementText) {
                        nodeXpath += 'contains(text(),\'' + uiElementText + '\')';
                    } else {
                        nodeXpath += 'contains(normalize-space(),\'' + uiElementText + '\')';
                    }
                }
            } else if (element.innerText && element.innerText.length < 50 && element.innerText === element.innerHTML && getText) {
                uiElementText = element.innerText;
                try {
                    uiElementText = uiElementText.trim();
                } catch (err) {
                    uiElementText = uiElementText.replace(/^\s+|\s+$/g, '');
                }
                if (uiElementText.length > 0) {
                    if (nodeXpath !== '') {
                        nodeXpath += ' and ';
                    }
                    if (element.innerText === uiElementText) {
                        nodeXpath += 'contains(text(),\'' + uiElementText + '\')';
                    } else {
                        nodeXpath += 'contains(normalize-space(),\'' + uiElementText + '\')';
                    }
                }
            } else if (element.nodeName.toLocaleLowerCase() === "a" || count === 0) {
                uiElementText = element.textContent;
                try {
                    uiElementText = uiElementText.trim().substring(0, 20);
                    uiElementText = uiElementText.replace("'", "') and contains (.,'");
                } catch (err) {
                    uiElementText = uiElementText.replace(/^\s+|\s+$/g, '');
                    uiElementText = uiElementText.replace("'", "') and contains (.,'");
                }
                if (uiElementText.length > 0) {
                    if (nodeXpath !== '') {
                        nodeXpath += ' and ';
                    }
                    if (element.textContent === uiElementText) {
                        nodeXpath += 'contains(normalize-space(),\'' + uiElementText + '\')';
                    } else {
                        nodeXpath += 'contains(.,\'' + uiElementText + '\')';
                    }
                }
            }
        } catch (err) {
        }
        /** Getting the Element's Tag Name
         **/
        const currentElementTagName = element.nodeName.toLocaleLowerCase();
        /** Building Xpath for the current Element Node
         **/
        if (nodeXpath === '') {
            xpath = '/' + currentElementTagName + pathIndex + xpath;
        } else {
            xpath = '/' + currentElementTagName + pathIndex + '[' + nodeXpath + ']' + xpath;
        }
        /** Switching focus to parent node
         **/
        element = element.parentElement;
        /** Incrementing the element counter and breaking the loop in case we reach the maximum number of elements defined by the user
         **/
        count++;
        if (count >= maxCount) {
            break;
        }
    }
    window.captured = null;
    return '/' + xpath;
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css';
document.head.appendChild(link);
const s = document.createElement('style');
s.innerHTML = SCRIPT_CSS;
document.head.appendChild(s);
const xpMenuDiv = document.createElement("div");
xpMenuDiv.id = "xpMenuLink";
xpMenuDiv.className = "xpMenuLink";
document.body.appendChild(xpMenuDiv);
const xpMenuLink = document.createElement("a");
xpMenuLink.id = "xpMenuLinkOpen";
xpMenuLink.innerHTML = "Xpath Locator";
xpMenuLink.onclick = openNav;
xpMenuDiv.appendChild(xpMenuLink);
const xpMenu = document.createElement("div");
xpMenu.className = "xpMenu";
xpMenu.id = "xpMenu";
xpMenu.innerHTML = MENU;
document.body.appendChild(xpMenu);
const xpCloseMenu = document.createElement("div");
xpCloseMenu.id = "xpMenuClosebtn";
xpCloseMenu.className = "xpMenuClosebtn";
xpCloseMenu.innerHTML = "&times;";
xpCloseMenu.onclick = closeNav;
xpCloseMenu.href = "#";
xpMenu.appendChild(xpCloseMenu);
document.getElementById("xpElementFind").onclick = findElements;
document.getElementById("xpElementNext").onclick = nextElement;
document.getElementById("xpElementPrev").onclick = prevElement;
document.getElementById("xpElementClear").onclick = clearAll;
document.getElementById("xpCapture").onclick = setCapture;
document.getElementById("xpUpdate").onclick = captureElement;
document.getElementById("xpElementCopy").onclick = copyXpath;