// ==UserScript==
// @name i,o,pでXPath生成ツール
// @description i:生成 o:除外条件付き生成 p:除外条件+1つの要素のみを生成 Shift+i:除外条件変更 Shift+o:要求条件入力 Shift+p:要求Hits数変更
// @match *://*/*
// @version 0.4.2
// @grant GM_setClipboard
// @run-at document-end
// @noframes
// @require https://code.jquery.com/jquery-3.6.4.min.js
// @require https://code.jquery.com/ui/1.13.2/jquery-ui.min.js
// @namespace https://greasyfork.org/users/181558
// ==/UserScript==
(function() {
const SITEINFO_UAP = 1; // -> [] で「AutoPagerize」用のSITEINFOを出力
const SITEINFO_WCA = 1; // -> [] で「web漫画にショートカットキーを追加」用のSITEINFOの雛形を出力
const SITEINFO_YHM = 1; // -> [] で「ヤフオクで非表示とメモ」用のSITEINFOの雛形を出力
const TEXT_MAX = 150; // @text等の文字列指定をする最大の長さ これ以上の長さの@text=""やcontains(text(),"")の指定はしない
const tips = "\n\nTips:\n「ABCやDEFを含まず、GHIかJKLを含み、かつMNOとPQRも含む」\n!ABC|DEF GHI|JKL MNO PQR\n\n";
const genNoboruRitsu = 0.1; // 0.5 遡る確率 初期値は50%
const genNoboruRitsuCSS = 0.5; // 0.5 遡る確率 初期値は50%
const XPATH_CSS_RATIO = 0.66; // XPATH:CSS のCSSの割合
const maxLines = 60; // 一度に提案する最大数調整 大きいほど少ない(maxLines/縦解像度)
const TryMulti = 1; // 一度に生成する試行回数調整 増やすと沢山試す=遅い代わりに結果が増える
var excludeRE = "href=|text\\(\\)|title=|alt=|src="; // o,pキーではこれを含むXPath式は除外する Shift+iで変更可
var requireRE = ""; // o,pキーではこれを含むXPath式を残す Shift+oで変更可
const SHIFT_O_SUGGEST = "@id=|\\#|@class|\\.|\\[class\\*\\=";
var requireHits = 1; // pキーではこのhits数のXPath式を残す Shift+pで変更可
let GF = {};
const autoNextLinkDecision = /page.*last\(\)|nav|next|right|>|>|»|次/gmi;
const autoNextLinkDecisionNot = /href/gmi;
const autoPageElementDecision = /main|list|content/gmi;
const verbose = 0; // 詳細をconsole.log()
let addstyle = {
added: new Set(),
add: function(str) {
if (this.added.has(str)) return;
end(document.head, `<style>${str}</style>`);
this.added.add(str);
},
}
addstyle.add(`#ioptable table,#ioptable tr,#ioptable td {padding:0 !important;}`)
String.prototype.match0 = function(re) { let tmp = this.match(re); if (!tmp) { return null } else if (tmp.length > 1) { return tmp[1] } else return tmp[0] } // gフラグ不可
let inYOUTUBE = location.hostname.match0(/^www\.youtube\.com|^youtu\.be/);
var mousex = 0;
var mousey = 0;
var shiftYenText = "";
var bubparent = 0;
var EscToHidePanel;
var BrightStyle = "0px 0px 6px 6px rgba(0, 250, 0, 0.5), inset 0 0 12px rgba(0, 250, 0, 0.2)";
var panel;
const CancelStyle = /box-shadow: none;|\@style=\"\"|(\sand\s)\sand\s|\[\]|\sand\s(\])/gmi;
const cancelrep = "$1$2";
var testxpold = [];
var testxpoldstyle = [];
var frameOldEle, frameOldStyle, frameOldTimer;
var ap1, ap2;
GF.ancestor = 0;
/*
//リアルタイムキー入力
var input_key_buffer = new Array();
document.addEventListener('keyup', function(e) {
input_key_buffer[e.keyCode] = false;
}, false);
document.addEventListener('blur', function(e) {
input_key_buffer.length = 0;
}, false);
*/
document.addEventListener('keydown', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.getAttribute('contenteditable') !== null || ((e.target.closest('#chat-messages,ytd-comments-header-renderer') || document.activeElement.closest('#chat-messages,ytd-comments-header-renderer')))) return;
// input_key_buffer[e.keyCode] = true;
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && !e.getModifierState("Shift") && e.keyCode == 73) {
mark(9);
e.preventDefault();
e.stopPropagation();
return false;
} //i 7
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && !e.getModifierState("Shift") && e.keyCode == 79) {
mark(7);
e.preventDefault();
e.stopPropagation();
return false;
} //o
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && !e.getModifierState("Shift") && e.keyCode == 80) {
mark(0);
e.preventDefault();
e.stopPropagation();
return false;
} //p
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && e.getModifierState("Shift") && e.keyCode == 73) {
excludeRE = window.prompt("Enter exclude word(s) RegExp\n\ncurrent:\n" + excludeRE + tips, excludeRE || "") || null;
e.preventDefault();
e.stopPropagation();
return false;
} //
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && e.getModifierState("Shift") && e.keyCode == 79) {
requireRE = window.prompt("Enter require word(s) RegExp\n\ncurrent:\n" + requireRE + tips, requireRE || SHIFT_O_SUGGEST) || null;
e.preventDefault();
e.stopPropagation();
return false;
} //
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && e.getModifierState("Shift") && e.keyCode == 80) {
requireHits = proInput("Enter required hits", requireHits, 0) || 1;
e.preventDefault();
e.stopPropagation();
return false;
} //
// }
// }, false);
}, true);
document.addEventListener("mousemove", function(e) {
mousex = e.clientX;
mousey = e.clientY;
}, false);
GF.ancestor = 0;
GF.buttons = e => {
if (e?.target?.matches("#iopancesplus")) GF.ancestor++;
else if (e?.target?.matches("#iopancesminus") && GF.ancestor > 0) GF.ancestor--;
mark(GF?.lastmode ?? 7, GF?.lastMark)
}
return;
function storetest(xpath) {
writebacktest();
testxpold = [];
// cl(xpath)
if (xpath) {
for (let ele of elegeta(xpath)) {
testxpold.push(ele);
testxpoldstyle.push(ele.style.boxShadow);
if (ele) ele.style.boxShadow = BrightStyle;
}
}
return;
}
function writebacktest() {
if (testxpold) {
for (let ele of testxpold) {
if (ele) ele.style.boxShadow = testxpoldstyle.shift();
}
testxpold = [];
}
return;
}
function mark(mode = 0, ele = null) {
GF.lastmode = mode;
if (ele && !document.contains(ele)) { $(panel).remove(); return; }
//let oldpanel=eleget0('#iopzerokey')
if (eleget0("#iopzerokey")) { //if (panel) {
writebacktest();
GF.panelLeft = eleget0("#iopzerokey")?.getBoundingClientRect()?.left
GF.panelTop = eleget0("#iopzerokey")?.getBoundingClientRect()?.top
/*GF.panelW1 = eleget0("#iopzerokey th#testXPathResult")?.clientWidth
GF.panelW2 = eleget0("#iopzerokey th#testXPathPossibly")?.clientWidth
GF.panelW1 = GF.panelW1?`width:${GF.panelW1-16}px !important; max-width:${GF.panelW1-16}px !important; min-width:${GF.panelW1-16}px !important;":""}`:""
GF.panelW2 = GF.panelW2?`width:${GF.panelW2-16}px !important; max-width:${GF.panelW2-16}px !important; min-width:${GF.panelW2-16}px !important;":""}`:""*/
$(panel).remove();
panel = null;
testxpold = null;
} else {
GF.panelLeft = undefined; //`${eleget0("#iopzerokey")?.getBoundingClientRect()?.left}px`
GF.panelTop = null; //`${eleget0("#iopzerokey")?.getBoundingClientRect()?.top}px`
// GF.panelW1 = undefined; //`${eleget0("#iopzerokey")?.getBoundingClientRect()?.top}px`
// GF.panelW2 = undefined; //`${eleget0("#iopzerokey")?.getBoundingClientRect()?.top}px`
}
if (frameOldEle) {
frameOldEle.style.outline = frameOldStyle;
frameOldEle = null;
clearTimeout(frameOldTimer);
}
if (!ele) {
ele = document.elementFromPoint(mousex, mousey);
}
GF.lastMark = ele
for (let i = 0; i < GF.ancestor && ele != document.body; i++) { ele = ele?.parentNode; }
popup(makecontent(mode, ele), /$^/, ele, mode);
let csssel = `${ele.tagName?.toLowerCase()}${ele.id?"#"+ele.id:""}${ele.className?"."+ele.className.trim().replace(/\s+/g,".").trim():""}`
var editableArea = eleget0('textarea#testXPath')
if (csssel && editableArea) {
editableArea.value = csssel;
putResult(csssel, "noflash")
}
frameOldStyle = ele.style.outline;
frameOldEle = ele;
ele.style.outline = "red dotted 4px";
frameOldTimer = setTimeout(function() {
if (frameOldEle) {
frameOldEle.style.outline = frameOldStyle;
frameOldEle = null;
}
}, 4000);
//GF.hold=0
}
function putResult(xpath, command = "") { // エディットエリアが書き換えられたら
writebacktest();
let elealen = elegeta(xpath)?.length;
let target = document?.getElementById("testXPath");
if (!target) return;
let lineHeight = 1;
target.setAttribute("rows", lineHeight);
for (let t = 0; t < 9; t++) { if ((target.scrollHeight) >= target.offsetHeight) { target.setAttribute("rows", lineHeight++); } }
$(eleget1('//th[@id="testXPathResult"]')).text("Err");
$('#testXPathPossibly span').remove()
var ele = eleget1(xpath);
if (ele == "Err") { $(eleget1('//th[@id="testXPathResult"]')).text(ele); } else {
$(eleget0('//th[@id="testXPathResult"]')).text((elegeta(xpath, document, "visible").length != elealen ? elegeta(xpath, document, "visible").length + " | " : "") + elealen);
if (elealen > 0) {
let [col, esti] = maybe(xpath)
end(eleget0('#testXPathPossibly'), `<span style="${col}">${ esti }</span>`)
}
}
if (command.match0(/noflash/) == false) storetest(xpath);
return;
}
// バルーン表示
function popup(txt, unRCtxt = null, posele, mode) {
addstyle.add('#iopzerokey,#iopzerokey table,#iopzerokey tr,#iopzerokey td,#iopzerokey th,#iopzerokey textarea{all:revert;}')
cl(mousey + "\n" + document.documentElement.clientHeight + "\n" + window.innerHeight)
let eler = posele.getBoundingClientRect();
let boxPos = "top:0; left:0;" //((mousey < Math.min(window.innerHeight, document.documentElement.clientHeight) / 2) ? "bottom:0px;" : "top:0px;") + ((mousex < document.documentElement.clientWidth / 2) ? "right:0px; " : "left:0px;");
let ele = eleget0("//table[@id='iopzerokey']");
if (ele) ele.parentElement.removeChild(ele);
// 表ヘッダ
let lists = document.createElement('table');
var line = document.createElement('tr');
line.id = "header"
addstyle.add(`#iopancesplus,#iopancesminus,#iopregenarate{cursor:pointer; } #iopregenarate{float:left; color:#26c;}`)
begin(line,
/* `<th style='text-align:center; min-width:5em; padding:0 8px; white-space: nowrap; ${GF.panelW1}'>Hits</th>
<th style='text-align:center; padding:0 8px; white-space: nowrap; ${GF.panelW2}'>Possibly</th>
*/
`<th style='text-align:center; min-width:5em; max-width:5em !important; padding:0 2px; white-space: nowrap;'>Hits</th>
<th style='text-align:center; min-width:7em; max-width:7em !important; padding:0 2px; white-space: nowrap;'>Possibly</th>
<th style='text-align:center; padding:0 8px;'><span style='color:#80f; float:left;' title="iopキーで生成する要素を親に遡る世代数\nパネル上でShift+ホイール上下で変更"">trace ancestor=${GF.ancestor} <span id="iopancesplus">[+]</span>/<span id="iopancesminus">[-]</span></span><span id="iopregenarate" title="クリックで再生成"> [Regenerate]</span>XPath${ mode != 9 && excludeRE ? "<span style='color:red;float:right;padding:0 4px;'>-" + excludeRE+"</span>" : "" }${ requireRE ? "<span style='color:blue;float:right;padding:0 4px;'>+" + requireRE + "</span>" : "" }${(mode == 0 ? "<span style='color:darkgreen;float:right;padding:0 4px;'>n=" + requireHits + "</span>" : "") }</th>`)
$(line).on("mousedown", "#iopancesplus,#iopancesminus,#iopregenarate", GF.buttons)
lists.appendChild(line);
// <th style='text-align:center; padding:0 8px;'><span style='color:#80f; float:left;' title="iopキーで生成する要素を親に遡る世代数\nパネル上でShift+ホイール上下で変更"">trace ancestor=${GF.ancestor} <span id="iopancesplus">[+]</span>/<span id="iopancesminus">[-]</span></span>XPath <span id="iopregenarate" title="クリックで再生成">[Regenerate]</span>${ mode != 9 && excludeRE ? "<span style='color:red;float:right;padding:0 4px;'>-" + excludeRE+"</span>" : "" }${ requireRE ? "<span style='color:blue;float:right;padding:0 4px;'>+" + requireRE + "</span>" : "" }${(mode == 0 ? "<span style='color:darkgreen;float:right;padding:0 4px;'>n=" + requireHits + "</span>" : "") }</th>`)
// エディットエリア
var editableline = document.createElement('tr');
lists.appendChild(editableline);
begin(editableline, "<th style='text-align:center; vertical-align:middle; padding:0 8px; white-space: nowrap;' id='testXPathResult'>test</th><th style='text-align:center; vertical-align:middle; padding:0 8px; white-space: nowrap;' id='testXPathPossibly'> </th><th><textarea rows='2' style='width:95%; min-height:2em; min-rowsline-height:1.0; overflow-x:hidden;' id='testXPath'>" + /* txt[txt.length - 1] */ "" + "</textarea></th>")
editableline.onclick = function() { arguments[0].stopPropagation(); return false; }
editableline.onmouseover = function(e) {
putResult(eleget0('//textarea[@id="testXPath"]')?.value?.replace(/\\\"/gmi, "\""));
if (eleget1(eleget0('//textarea[@id="testXPath"]')?.value) != "Err") {
storetest(eleget0('//textarea[@id="testXPath"]')?.value);
this.style.backgroundColor = '#ffffff';
}
}
editableline.onmouseout = function() {
if (eleget1(eleget0('//textarea[@id="testXPath"]')?.value) != "Err") {
writebacktest();
this.style.backgroundColor = '';
}
}
editableline.addEventListener('input', function(e) { // エディットエリアが書き換えられた
putResult(e.target.value.replace(/\\\"/gmi, "\""));
if (eleget1(e.target.value.replace(/\\\"/gmi, "\"")) != "Err")
storetest(e.target.value.replace(/\\\"/gmi, "\""));
}, false);
function appendAPb(place, text, matchLv) {
var ele3 = place.appendChild(document.createElement('td'));
$(ele3).hover(function() { $(this).css('cursor', 'pointer'); }, function() { $(this).css('cursor', 'default'); });
begin(ele3, text)
ele3.title = text == "->" ? "set this as nextLink/uAutoPagerize" : "set this as pageElement/uAutoPagerize";
ele3.style.whiteSpace = "nowrap";
ele3.style.opacity = 0.5;
ele3.style.backgroundColor = matchLv ? "#ffaaaa" : "";
ele3.style.verticalAlign = "middle";
return ele3;
}
var ele3 = appendAPb(editableline, "->");
ele3.onclick = function(e) {
let xpath = eleget0('//textarea[@id="testXPath"]').value;
let origXpath = xpath;
if (eleget1(xpath) != "Err") {
for (var sa = 0; sa < 10; sa++) {
if (eleget1(xpath) != "Err" && eleget0(xpath).tagName == "A") {
apc();
ap1 = xpath;
writeClipboard(xpath);
if (ap2) { apmake(ap1, ap2); }
break;
} else {
writebacktest();
xpath += "/..";
storetest();
}
}
if (sa == 10) {
//alert("Went back to the parent element for 10 generations, but A tag was not found.");
xpath = origXpath;
apc();
ap1 = xpath;
writeClipboard(xpath);
if (ap2) { apmake(ap1, ap2); }
// alert("Went back to the parent element for 10 generations, but <A> tag was not found.");
// apc();
e.stopPropagation();
}
}
}
var ele3 = appendAPb(editableline, "[]");
ele3.onclick = function(e) {
let xpath = eleget0('//textarea[@id="testXPath"]').value;
if (eleget1(xpath) !== "Err" && eleget0(xpath).nodeType === 1) {
apc();
ap2 = xpath;
writeClipboard(xpath);
if (ap1) { apmake(ap1, ap2); }
}
}
// 表
for (let xpath of txt) {
if (xpath) {
let ele2 = eleget0(xpath);
let elea = elegeta(xpath);
let elealen = elea?.length
var line = document.createElement('tr');
// maybe タイプ判定
let [col, esti] = maybe(xpath)
var xpathDisp = xpath.replace(/(@id=|@class)/gmi, '<font style="color:magenta;"><b>$1</b></font>'); //.replace(requireRE?RegExp("("+requireRE+")","gmi"):/\0/, '<font style="background-color:#f0f0ff;">$1</font>');
// begin(line, "<td nowrap data-hits='" + elegeta(xpath, document, "visible").length + "' title=\"クリックでこのヒット数指定に変更(Shift+P)\" style=\"cursor:pointer; text-align:center;" + col + "padding:0 8px;\">" + (elegeta(xpath, document, "visible").length != elegeta(xpath).length ? elegeta(xpath, document, "visible").length + " | " : "") + elegeta(xpath).length + "</td><td style='" + col + "text-align:center; padding:0 8px; white-space: nowrap;'>" + esti + "</td><td style=\"" + col + " text-align:left;\">" + xpathDisp + "</td>")
begin(line, "<td nowrap data-hits='" + elegeta(xpath, document, "visible").length + "' title=\"クリックでこのヒット数指定に変更(Shift+P)\" style=\"cursor:pointer; text-align:center;" + col + "padding:0 8px;\">" + (elegeta(xpath, document, "visible").length != elegeta(xpath).length ? elegeta(xpath, document, "visible").length + " | " : "") + elegeta(xpath).length + "</td><td style='" + col + "text-align:center; padding:0 8px; '>" + esti + "</td><td style=\"" + col + " text-align:left;\">" + xpathDisp + "</td>")
line.onmouseover = function() {
storetest(xpath);
this.style.backgroundColor = '#ffffff';
}
line.onmouseout = function() {
writebacktest();
this.style.backgroundColor = '';
}
line.onclick = function(e) {
if (e?.target?.dataset?.hits) {
requireHits = Number(e?.target?.dataset?.hits)
e.stopPropagation()
e.preventDefault()
setTimeout(() => {
//GF.hold = 1;
mark(0, GF?.lastMark);
//GF.hold = 0;
}, 111)
return false;
}
var txt = e?.ctrlKey ? xpath.replace(/\"/gm, "\\\"") : xpath;
eleget0('//textarea[@id="testXPath"]').value = txt;
putResult(txt.replace(/\\\"/gmi, "\""));
e.stopPropagation()
e.preventDefault()
return false;
}
line.ondblclick = function(e) {
var txt = e?.ctrlKey ? xpath.replace(/\"/gm, "\\\"") : xpath;
writeClipboard(txt);
eleget0('//textarea[@id="testXPath"]').value = txt; //a.value;
eleget0('//th[@id="testXPathResult"]').innerText = elegeta(txt).length;
writebacktest();
$("#testXPath").remove();
$(panel).hide(400, function() { setTimeout(function() { $(panel).remove() }, 100) });
// panel = null;
e.stopPropagation()
e.preventDefault()
return false;
}
// uAutoPagerize用ボタン
var ele3 = appendAPb(line, "->", (elegeta(xpath).length < 3) && !!(xpath.match(autoNextLinkDecision)) && !(xpath.match(autoNextLinkDecisionNot)));
ele3.onclick = function(e) {
let origXpath = xpath;
if (eleget1(xpath) != "Err") {
for (var sa = 0; sa < 10; sa++) {
if (eleget1(xpath) != "Err" && eleget0(xpath)?.tagName == "A") {
apc();
ap1 = xpath;
writeClipboard(xpath);
if (ap2) { apmake(ap1, ap2); }
break;
} else {
writebacktest();
xpath += "/..";
storetest();
}
}
if (sa == 10) {
//alert("Went back to the parent element for 10 generations, but A tag was not found.");
xpath = origXpath;
apc();
ap1 = xpath;
writeClipboard(xpath);
if (ap2) { apmake(ap1, ap2); }
}
}
}
var ele3 = appendAPb(line, "[]", (elegeta(xpath).length == 1) && !!xpath.match(autoPageElementDecision));
ele3.onclick = function(e) {
if (eleget1(xpath) != "Err") {
apc();
ap2 = xpath;
writeClipboard(xpath);
if (ap1) { apmake(ap1, ap2); }
}
}
function apmake(ap1, ap2) {
var msi = "";
let domain = location.href.match(/^https?:\/{2,}(.*?)(?:\/|\?|#|$)/)[1];
if (SITEINFO_UAP) { msi += "{\n url : '^https?://" + domain.replace(/\./g, "\\\\.") + "',\n nextLink : '" + ap1 + "',\n pageElement : '" + ap2 + "',\n},\n"; }
if (SITEINFO_WCA) { msi += `\n// @match *://${ domain }/*\n{\n url: '//${ domain }/',\n firstEpisode: '${ap1}',\n lastEpisode: '${ ap2}',\n},\n`; }
// if (SITEINFO_YHM) { msi += "\n// @match *://" + domain + "/*\n{\n id:'" + domain + "',\n urlRE: '//" + domain + "/',\n listTitleXP: '" + ap1 + "',\n listTitleSearchXP: '" + ap1 + "[+++]/../../..',\n listTitleMemoSearchXP: '" + ap1 + "[+++]',\n listGen:3,\n detailURLRE:/$^/,\n detailTitleXP:'',\n detailTitleSearchXP:'',\n},\n"; }
if (SITEINFO_YHM && ap2.match0(/^\/\//)) { msi += "\n// @match *://" + domain + "/*\n{\n id:'" + domain + "',\n urlRE: '//" + domain + "/',\n listTitleXP: '" + ap1 + "',\n listTitleSearchXP: '" + ap1 + "[+++]/../../..',\n listTitleMemoSearchXP: '" + ap1 + "[+++]',\n listGen:3,\n detailURLRE:/$^/,\n detailTitleXP:'',\n detailTitleSearchXP:'',\n},\n"; }
if (SITEINFO_YHM && !ap2.match0(/^\/\//)) { msi += "\n// @match *://" + domain + "/*\n{\n id:'" + domain + "',\n urlRE: '//" + domain + "/',\n title: '" + ap1 + "',\n box: '" + ap2 + "',\n detailURLRE:/$^/,\n detailTitleXP:'',\n detailTitleSearchXP:'',\n},\n"; }
writeClipboard(msi);
alert("The following has been copied to the clipboard.\n" + msi);
return;
}
function apc() {
writebacktest();
$(eleget0('//*[@id="iopzerokey"]')).fadeOut(400, function() { setTimeout(function() { $(panel).remove() }, 100) });
}
lists.appendChild(line);
}
}
let opa = 0.9;
panel = document.createElement("div");
panel.appendChild(lists)
// panel.setAttribute("style", "all:initial; resize:both; overflow: auto; font-family:sans-serif; max-width:95%;" + boxPos + " z-index:2147483647; opacity:" + opa + "; text-align:left; line-height:1.1em; position:fixed; font-size:12px; margin:15px; text-decoration:none; padding:0.5em; outline-radius:7px; background-color:#d0e8ff; color:#000000; box-shadow:5px 5px 8px #0003; outline:2px solid #fff;");
// panel.setAttribute("style", "all:initial; resize:both; overflow: auto; font-family:sans-serif; max-width:95%;" + boxPos + " z-index:2147483647; opacity:" + opa + "; text-align:left; line-height:1.1em; position:fixed; font-size:12px; margin:0px; text-decoration:none; padding:0.5em; outline-radius:7px; background-color:#d0e8ff; color:#000000; box-shadow:5px 5px 8px #0003; outline:2px solid #fff;");
panel.setAttribute("style", "all:initial; resize:both; overflow: auto; font-family:sans-serif; max-width:90%;" + boxPos + " z-index:2147483647; opacity:" + opa + "; text-align:left; line-height:1.1em; position:fixed; font-size:12px; margin:0px; text-decoration:none; padding:0.5em; outline-radius:7px; background-color:#d0e8ff; color:#000000; box-shadow:5px 5px 8px #0003; outline:2px solid #fff;");
panel.id = "iopzerokey";
// Escでパネルを消す
EscToHidePanel = document.addEventListener('keydown', function closePanel(e) {
if (!e.getModifierState("Alt") && !e.getModifierState("Control") && !e.getModifierState("Shift") && (e.key == "Escape")) {
if (eleget1(eleget0('//textarea[@id="testXPath"]')?.value) != "Err") { writebacktest(); }
$("#testXPath").remove();
$(panel).hide(400, function() { setTimeout(function() { $(panel).remove() }, 100) });
this.removeEventListener('keydown', closePanel, false);
e.stopPropagation()
e.preventDefault()
return false;
}
}, true);
//GF.panelX=null
//GF.panelY=null
panel.addEventListener("wheel", e => {
if (!e?.shiftKey) return;
if (e?.deltaY < 0) GF.ancestor++;
else GF.ancestor > 0 && GF.ancestor--;
//GF.hold = 1;
//GF.panelLeft=`${eleget0("#iopzerokey")?.getBoundingClientRect()?.left}px`
//GF.panelTop=`${eleget0("#iopzerokey")?.getBoundingClientRect()?.top}px`
//console.log(GF.panelX)
mark(GF?.lastmode ?? 7, GF?.lastMark)
//GF.hold = 0;
e.preventDefault()
e.stopPropagation()
return false;
})
document.body.appendChild(panel);
// if (!GF?.hold&&GF?.panelX&&GF?.panelY) {
//console.log(GF.panelLeft,GF.panelTop)
if (GF.panelLeft !== null && GF.panelTop !== null) {
panel.style.left = `${GF.panelLeft}px`;
panel.style.top = `${GF.panelTop}px`;
// GF.left =`${GF?.panelX}px`// `${(mousex < document.documentElement.clientWidth / 2)?clientWidth()-$("#iopzerokey").width()-50:0 }px`
// GF.top =`${GF?.panelY}px`// `${(mousey < clientHeight() / 2) ? clientHeight()-$("#iopzerokey").height()-50:0}px`
} else {
panel.style.left = `${(mousex < document.documentElement.clientWidth / 2)?clientWidth()-$("#iopzerokey").width()-50:0 }px`
panel.style.top = `${(mousey < clientHeight() / 2) ? clientHeight()-$("#iopzerokey").height()-10:0}px`
}
/*panel.style.left = GF.left
panel.style.top = GF.top
*/
//$(panel).draggable({ handle: "tr#header th,#iopancesplus,#iopancesminus" });
$(panel).draggable({ cancel: "td,textarea" });
}
function maybe(xpath) {
let lc = (xpath.match(/last\(\)|\:last-child|\:last-of-type/g));
let elealen = elegeta(xpath)?.length
if (lc) lc = lc.length;
let esti = []
if (xpath.match(/href=/)) esti.push(`<span style= "color:red;">href</span>`)
if (lc >= 1) esti.push(`<span style="color:blue;">last</span>`)
if ((xpath.match(/\[1\]|\:nth-child\(1\)|\(1 of |\:nth-of-type\(1\)/) && elealen === 1)) esti.push(`<span style= "color:green;">1st</span>`)
let [ids, classes] = [xpath.match(/@id=|\#/g)?.length, xpath.indexOf('"http') == -1 ? xpath.match(/@class|\.|\[class\*\=\"/g)?.length : 0]
if (ids) esti.push(`<span style="color:magenta;">${ids}id</span>`)
if (classes) esti.push(`<span style="color:magenta;">${classes}class</span>`)
let col = xpath.match(/href=/) ? "color:red;" : lc > 1 ? "color:blue; font-weight:bold;" : lc == 1 ? "color:blue;" : (xpath.match(/\[1\]|\:nth-child\(1\)|\(1 of |\:nth-of-type\(1\)/) && elealen === 1) ? "color:green;" : xpath.match(/@id=|@class|\#|\.|\[class\*\=\"/) ? "color:magenta;" : "";
esti = esti.join(" ")
return [col, esti?.trim()]
}
function writeClipboard(txt) {
GM_setClipboard(txt);
}
function makecontent(mode, ele) {
var retStr = [];
var maxline = window.innerHeight / maxLines;
var maxtrial = maxline * TryMulti * (mode != 9 ? 32 : 3) * (requireRE > "\0" ? 3 : 1) * ((mode == 0 && requireHits == 1) ? 3 : 1);
var retStrl = 0;
hajime();
let stime = Date.now()
// for (var i = 0; i < maxtrial && retStrl < maxline; i++) {
for (var i = 0; Date.now() - stime < 1000 && retStrl < maxline; i++) {
var xp = Math.random() > XPATH_CSS_RATIO ? getElementXPath(mode, ele) : getElementXPathCSS(mode, ele)
if (xp && !(mode !== 9 && RegExp((excludeRE || "$^").replace(/|/gm, "|").replace(/^[\!|!](\S*)/, "^(?!.*($1)).*").replace(/(\S*)[ ](\S*)/gm, "^(?=.*($1))(?=.*\($2\))").replace(/\s| /gm, ".*"), "i").test(xp))) {
retStr.push(xp);
var retStr = retStr.sort().reduce(function(previous, current) { if (previous[previous.length - 1] !== current) { previous.push(current); } return previous; }, []).sort(function(a, b) { return a.length - b.length < 0 ? -1 : 1; }); // 重複を削除&ソート
retStrl = retStr.length;
}
}
owari();
cl("trymax:" + maxtrial + "\nmakemax:" + maxline + "\ntried:" + i + "\nmake:" + retStr.length);
// 1つは全属性を記述しただけのもの
if (getAttrOfEle(mode, ele, true)) {
var path = "//" + ele.tagName + "[" + getAttrOfEle(mode, ele, true) + "";
retStr.push(path.replace(CancelStyle, cancelrep).replace(CancelStyle, cancelrep));
}
// 1つはフルパスを辿っただけのもの
retStr.push(getElementXPathFullPath(ele).replace(CancelStyle, cancelrep).replace(CancelStyle, cancelrep));
// 1つはフルパス全属性を辿っただけのもの
retStr.push(getElementXPathFullpathAndFullproperty(mode, ele).replace(CancelStyle, cancelrep).replace(CancelStyle, cancelrep));
return retStr;
}
function getElementXPath(mode, ele) {
var path = "";
var origele = ele;
for (let i = 0; i == 0 || (ele && ele.nodeType == 1 && Math.random() > genNoboruRitsu); ele = ele.parentNode, i++) {
//兄弟番号を調べる
var ps = getPrevSib(ele);
var ns = getNextSib(ele);
var idx = 0;
var arg = "";
if (!ns && ps && Math.random() > 0.2) {
var arg = "[last()]";
} else
if (!ps && ns && Math.random() > 0.2) {
var arg = "[1]";
} else {
for (var idx = 1, sib = getPrevSib(ele); sib; sib = getPrevSib(sib)) { idx++; }
}
//属性を調べる
var att = getAttrOfEle(mode, ele);
if (att) att = "[" + att;
//背番号かfirst/lastか属性か何も付けないかを32,32,32,4%ずつ
var rnd = Math.random();
if ((rnd > 0.68) && arg) var opt = arg;
else if ((rnd > 0.36) && idx > 1) var opt = "[" + idx + "]";
else if ((rnd > 0.04) && att) var opt = att;
else var opt = "";
path = "/" + ele.tagName.toLowerCase() + (opt) + path;
}
if (!path) return "";
path = "/" + path;
path = path.replace(CancelStyle, cancelrep).replace(CancelStyle, cancelrep);
if (requireRE > "" && !(RegExp((requireRE || "$^").replace(/|/gm, "|").replace(/^[\!|!](\S*)/, "^(?!.*($1)).*").replace(/(\S*)[ ](\S*)/gm, "^(?=.*($1))(?=.*\($2\))").replace(/\s| /gm, ".*"), "i").test(path))) return "";
let hits = elegeta(path).length; // 検算
let hitsVisible = elegeta(path).filter(e => e?.offsetHeight).length; // 検算
if (hits == 0 || (mode == 8 && hits < 2) || (mode == 0 && (requireHits != hits && requireHits != hitsVisible))) return false;
return path;
}
function getAttrOfEle(mode, ele, allFlag = false) {
if (ele.tagName.match(/html|body/gmi)) return "";
let attrs = ele.attributes;
let xp = "";
let att2 = [];
for (let att of attrs) {
if (att.value === "") continue;
if (att.value.length < TEXT_MAX) {
if (att.name == "class" && Math.random() > 0.2 && !allFlag) {
att2.push({ name: "contains(@class,", value: "\"" + att.value + "\")" })
} else {
att2.push({ name: "@" + att.name + "=", value: "\"" + att.value + "\"" })
}
}
}
//text
if (!allFlag) {
if (Math.random() > (0.5)) {
if (ele.innerText && !ele.innerText.match(/[\r\n]/)) { att2.push({ name: "text()=", value: "\"" + ele.innerText + "\"" }) }
} else {
if (ele.innerText && !ele.innerText.match(/[\r\n]/)) { att2.push({ name: "contains(text(),", value: "\"" + ele.innerText + "\")" }) }
}
}
for (let j = 0; j < att2.length; j++) {
if ((Math.random() > (allFlag ? 0 : att2[j].name.match(/href/) ? 0.7 : 0.5))) {
xp += att2[j].name + att2[j].value + " and ";
}
}
xp = xp.replace(/ and $/, "]").replace(/^(.*)\[$/, "$1");
return xp;
}
function getElementXPathCSS(mode, ele) {
var path = "";
var origele = ele;
for (let i = 0; i == 0 || (ele && ele.nodeType == 1 && Math.random() > genNoboruRitsuCSS); ele = ele.parentNode, i++) {
//兄弟番号を調べる
var ps = getPrevSib(ele);
var ns = getNextSib(ele);
let cla = [...ele.classList].filter(v => v !== "")
let classes = (cla.length) ? cla.filter(v => Math.random() > 0.5).map(v =>
Math.random() > (v.match(/[0-9\_\-]+/) ? 0.66 : 0.9) ? `[class*="${v}"]` :
`.${v}`).join("") : "";
let idname = `${ele?.id>""&&Math.random()>0.66&&ele?.id?("#"+ele?.id) : ""}`;
//属性を調べる
var att = getAttrOfEleCSS(mode, ele);
if (att) att = "[" + att;
var idx = 0;
var arg = "";
if (!ns && ps && Math.random() > 0.2) {
var arg = Math.random() > 0.5 ? ":last-of-type" : ":last-child";
} else
if (!ps && ns && Math.random() > 0.2) {
var arg = [`:nth-child(1 of ${ele.tagName.toLowerCase()}${idname}${classes})`, `:nth-of-type(1)`, `:nth-child(1)`][~~(Math.random() * 3)]
} else {
for (var idx = 1, sib = getPrevSib(ele); sib; sib = getPrevSib(sib)) { idx++; }
}
//背番号かfirst/lastか属性か何も付けないかを32,32,32,4%ずつ
var rnd = Math.random();
if ((rnd > 0.68) && arg) var opt = arg;
// else if ((rnd > 0.5) && idx > 1) var opt = ":nth-child(" + idx + ")";
else if ((rnd > 0.5) && idx > 1) var opt = Math.random() > 0.5 ? `:nth-child(${idx} of ${ele.tagName.toLowerCase()}${idname}${classes})` : `:nth-child(${idx})`;
else if ((rnd > 0.04) && att) var opt = att;
else var opt = "";
path = ` > ${ele.tagName.toLowerCase()}${idname}${classes}${opt}${path}`;
}
if (!path) return "";
path = path.replace(CancelStyle, cancelrep).replace(CancelStyle, cancelrep);
path = path.trim()?.replace(/^\>\s/, "");
if (requireRE > "" && !(RegExp((requireRE || "$^").replace(/|/gm, "|").replace(/^[\!|!](\S*)/, "^(?!.*($1)).*").replace(/(\S*)[ ](\S*)/gm, "^(?=.*($1))(?=.*\($2\))").replace(/\s| /gm, ".*"), "i").test(path))) return "";
let hits = elegeta(path).length; // 検算
let hitsVisible = elegeta(path).filter(e => e?.offsetHeight).length; // 検算
if (hits == 0 || (mode == 8 && hits < 2) || (mode == 0 && (requireHits != hits && requireHits != hitsVisible))) return false;
//if (hits == 0 || (mode == 8 && hits < 2) || (mode == 0 && requireHits != hits)) return false;
return path;
}
function getAttrOfEleCSS(mode, ele, allFlag = false) {
if (ele.tagName.match(/html|body/gmi)) return "";
let attrs = ele.attributes;
let xp = "";
let att2 = [];
for (let att of attrs) {
if (att.value.length < TEXT_MAX && att.value !== "") {
if (att.name != "class" && att.name != "id" && Math.random() > 0.5) {
att2.push({ name: "" + att.name + "=", value: "\"" + att.value + "\"" })
}
}
}
for (let j = 0; j < att2.length; j++) {
if ((Math.random() > (allFlag ? 0 : att2[j].name.match(/href/) ? 0.7 : 0.5))) {
xp += att2[j].name + att2[j].value + "][";
}
}
xp = xp.replace(/\[$/, "").replace(/^(.*)\[$/, "$1");
return xp;
}
function getElementXPathFullPath(ele) {
var path = "";
for (; ele && ele.nodeType == 1; ele = ele.parentNode) {
for (var idx = 1, sib = ele.previousSibling; sib; sib = sib.previousSibling) {
if (sib.nodeType == 1 && sib.tagName == ele.tagName) idx++
}
path = "/" + ele.tagName + (!ele.tagName.match(/html|body/gi) ? "[" + idx + "]" : "") + path;
}
return path;
}
function getElementXPathFullpathAndFullproperty(mode, ele) {
var path = "";
for (; ele && ele.nodeType == 1; ele = ele.parentNode) {
if (getAttrOfEle(mode, ele, true)) { path = "/" + ele.tagName + "[" + getAttrOfEle(mode, ele, true) + path; } else { path = "/" + ele.tagName + path; }
}
path = "/" + path;
return path;
}
function str2numMinMax(str, min, max) {
var ans = Number(str.replace(/[A-Za-z0-9.]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 65248);
}).replace(/[^-^0-9^\.]/g, ""));
if (ans > max) ans = max;
if (ans < min) ans = min;
return ans;
}
function elegeta(xpath, node = document, command = "") {
if (!xpath || !node) return [];
let inscreen = command === "inscreen" ? 1 : 0
let visible = command === "visible" ? 1 : 0
let text
if (/:inscreen/.test(xpath)) {
inscreen = 1;
xpath = xpath.replace(/:inscreen/, "")
}
if (/:visible/.test(xpath)) {
visible = 1;
xpath = xpath.replace(/:visible/, "")
}
if (/:text\*=/.test(xpath)) {
text = xpath.replace(/^.*:text\*=(.*)$/, "$1");
xpath = xpath.replace(/:text.+$/, "")
}
try {
if (!/^\.?\//.test(xpath)) { var array = [...node.querySelectorAll(xpath)] } else {
var array = [];
var ele = document.evaluate("." + xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
let l = ele.snapshotLength;
for (var i = 0; i < l; i++) array[i] = ele.snapshotItem(i);
}
if (inscreen) array = array.filter(e => { var eler = e.getBoundingClientRect(); return (eler.bottom >= 0 && eler.right >= 0 && eler.left <= document.documentElement.clientWidth && eler.top <= document.documentElement.clientHeight) }) // 画面内に1ピクセルでも入っている
if (visible) array = array.filter(e => e.offsetHeight)
if (text) array = array.filter(e => new RegExp(text).test(e.innerText))
if (command !== "check") {
let inpanel = document.body.querySelector('#iopzerokey')
if (inpanel && Array.isArray(array)) array = array.filter(v => !inpanel.contains(v))
}
return array
} catch (e) { /*alert(e + "\n" + xpath + "\n" + JSON.stringify(node));*/ return command === "check" ? "Err" : []; }
}
function eleget1(xpath) {
if (!xpath) return "";
let ret = elegeta(xpath, document, "check")
if (ret === "Err") return ret
if (ret.length) return ret[0];
else return [];
}
function eleget0(xpath, node = document) {
if (!xpath || !node) return null;
let inscreen = 0
let visible = 0
if (/:inscreen/.test(xpath)) {
inscreen = 1;
xpath = xpath.replace(/:inscreen/, "")
}
if (/:visible/.test(xpath)) {
visible = 1;
xpath = xpath.replace(/:visible/, "")
}
if (!/^\.?\//.test(xpath)) var ele = node.querySelector(xpath)
else try {
var eles = document.evaluate("." + xpath.replace(/:visible$/, ""), node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
if (eles.snapshotLength > 0) var ele = eles.snapshotItem(0);
else return null;
} catch (e) { /* alert(e + "\n" + xpath + "\n" + JSON.stringify(node));*/ return null; }
if (inscreen) { var eler = ele.getBoundingClientRect(); if (!(eler.top > 0 && eler.left > 0 && eler.left < document.documentElement.clientWidth && eler.top < document.documentElement.clientHeight)) return null }
if (visible) { if (!ele.offsetHeight) return null }
return ele
}
function getNextSib(ele) { // 同じタグの弟ノードを走査
if (!ele.nextElementSibling) return null;
let orgtag = ele.tagName;
do {
ele = ele.nextElementSibling;
if (!ele) return null;
if (ele.tagName == orgtag) return ele;
} while (1)
return null;
}
function getPrevSib(ele) { // 同じタグの兄ノードを走査
if (!ele.previousElementSibling) return null;
let orgtag = ele.tagName;
do {
ele = ele.previousElementSibling;
if (!ele) return null;
if (ele.tagName == orgtag) return ele;
} while (1)
return null;
}
function proInput(prom, defaultval, min, max = Number.MAX_SAFE_INTEGER) {
return Math.min(Math.max(
Number(window.prompt(prom, defaultval).replace(/[A-Za-z0-9]/g, function(s) {
return String.fromCharCode(s.charCodeAt(0) - 65248);
}).replace(/[^-^0-9^\.]/g, "")), min), max);
}
var start_ms;
function hajime() { start_ms = new Date().getTime(); }
function owari() {
var elapsed_ms = new Date().getTime() - start_ms;
cl('処理時間:' + elapsed_ms / 1000 + "秒");
}
function cl(...args) {
if (verbose) console.log(...args)
}
function sani(s) { return s?.replace(/&/g, "&")?.replace(/"/g, """)?.replace(/'/g, "'")?.replace(/`/g, '`')?.replace(/</g, "<")?.replace(/>/g, ">") || "" }
function before(e, html) { e?.insertAdjacentHTML('beforebegin', html); return e?.previousElementSibling; }
function begin(e, html) { e?.insertAdjacentHTML('afterbegin', html); return e?.firstChild; }
function end(e, html) { e?.insertAdjacentHTML('beforeend', html); return e?.lastChild; }
function after(e, html) { e?.insertAdjacentHTML('afterend', html); return e?.nextElementSibling; }
function clientHeight() { return Math.min(document.documentElement.clientHeight, window.innerHeight) }
function clientWidth() { return document.documentElement.clientWidth }
})();