diff --git a/Extension/manifest_v3/src/content-scripts/config.json b/Extension/manifest_v3/src/content-scripts/config.json index a2e684c..3706a6d 100644 --- a/Extension/manifest_v3/src/content-scripts/config.json +++ b/Extension/manifest_v3/src/content-scripts/config.json @@ -1 +1 @@ -{"language":"zh"} \ No newline at end of file +{"language":"en"} \ No newline at end of file diff --git a/Extension/manifest_v3/src/content-scripts/global.js b/Extension/manifest_v3/src/content-scripts/global.js index 36d15da..c6d0465 100644 --- a/Extension/manifest_v3/src/content-scripts/global.js +++ b/Extension/manifest_v3/src/content-scripts/global.js @@ -65,7 +65,7 @@ export function readXPath(element, type = 1, node = document.body) { if (element.className != ""){ //如果有class且某个class name只有一个元素,则使用class name生成xpath console.log("class name: " + element.className); let names = element.className.split(" "); - for (var i = 0; i < names.length; i++) { + for (let i = 0; i < names.length; i++) { if (names[i] != "") { // return '//*[@class=\"' + names[i] + '\"]'; console.log('//*[@contains(@class, \"' + names[i] + '\")]'); @@ -86,11 +86,11 @@ export function readXPath(element, type = 1, node = document.body) { return ""; } } - var ix = 1, //在nodelist中的位置,且每次点击初始化 + let ix = 1, //在nodelist中的位置,且每次点击初始化 siblings = element.parentNode.childNodes; //同级的子元素 - for (var i = 0, l = siblings.length; i < l; i++) { - var sibling = siblings[i]; + for (let i = 0, l = siblings.length; i < l; i++) { + let sibling = siblings[i]; //如果这个元素是siblings数组中的元素,则执行递归操作;arguments.callee代表当前函数的名称 if (sibling == element) { return readXPath(element.parentNode, type, node) + '/' + element.tagName.toLowerCase() + '[' + (ix) + ']'; @@ -143,7 +143,7 @@ export function addEl() { handleElement(); //处理新状态 //将虚线框显示在元素上方但屏蔽其鼠标操作 - var pos = global.NowNode.getBoundingClientRect(); + let pos = global.NowNode.getBoundingClientRect(); global.div.style.display = "block"; global.div.style.height = global.NowNode.offsetHeight + "px"; global.div.style.width = global.NowNode.offsetWidth + "px"; @@ -278,7 +278,10 @@ export function generateParameters(type, linktext = true, linkhref = true) { let nd = global.nodeList[num]["node"]; ndPath = global.nodeList[num]["xpath"]; ndAllXPaths = global.nodeList[num]["allXPaths"]; - global.outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow }); + let unique_index = Math.random().toString(36).substring(2) + Date.now().toString(36);; //唯一标识符 + global.outputParameterNodes.push({ "node": nd, + "unique_index": unique_index, + "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow }); nd.style.boxShadow = global.boxShadowColor; let pname = parameterName("文本"); let ndText = ""; @@ -296,7 +299,7 @@ export function generateParameters(type, linktext = true, linkhref = true) { // ndText = $(nd).contents().filter(function() { return this.nodeType === 3; }).text().replace(/\s+/g, ''); ndText = ""; let ndContents = nd.childNodes; - for (var i = 0; i < ndContents.length; i++) { + for (let i = 0; i < ndContents.length; i++) { if (ndContents[i].nodeType === 3) { // if it's a text node ndText += ndContents[i].textContent.trim(); // add its content to the string } @@ -344,7 +347,8 @@ export function generateParameters(type, linktext = true, linkhref = true) { "extractType": 0, //提取方式 0 普通 1 OCR "relativeXPath": global.nodeList.length > 1 ? "" : ndPath, "allXPaths": global.nodeList.length > 1 ? "" : ndAllXPaths, - "exampleValues": [{ "num": num, "value": ndText }] + "exampleValues": [{ "num": num, "value": ndText }], + "unique_index": unique_index, }); } else if (nd.tagName == "A") { //如果元素是超链接 if (linktext) { @@ -357,7 +361,8 @@ export function generateParameters(type, linktext = true, linkhref = true) { "extractType": 0, //提取方式 0 普通 1 OCR "relativeXPath": global.nodeList.length > 1 ? "" : ndPath, "allXPaths": global.nodeList.length > 1 ? "" : ndAllXPaths, - "exampleValues": [{ "num": num, "value": ndText }] + "exampleValues": [{ "num": num, "value": ndText }], + "unique_index": unique_index, }); } if (linkhref) { @@ -369,7 +374,8 @@ export function generateParameters(type, linktext = true, linkhref = true) { "desc": "", //参数描述 "relativeXPath": global.nodeList.length > 1 ? "" : ndPath, "allXPaths": global.nodeList.length > 1 ? "" : ndAllXPaths, - "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") }] + "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") }], + "unique_index": unique_index, }); } } else if (nd.tagName == "INPUT") { //如果元素是输入项 @@ -382,7 +388,8 @@ export function generateParameters(type, linktext = true, linkhref = true) { "extractType": 0, //提取方式 0 普通 1 OCR "relativeXPath": global.nodeList.length > 1 ? "" : ndPath, "allXPaths": global.nodeList.length > 1 ? "" : ndAllXPaths, - "exampleValues": [{ "num": num, "value": ndText }] + "exampleValues": [{ "num": num, "value": ndText }], + "unique_index": unique_index, }); } else { //其他所有情况 global.outputParameters.push({ @@ -394,7 +401,8 @@ export function generateParameters(type, linktext = true, linkhref = true) { "extractType": 0, //提取方式 0 普通 1 OCR "relativeXPath": global.nodeList.length > 1 ? "" : ndPath, "allXPaths": global.nodeList.length > 1 ? "" : ndAllXPaths, - "exampleValues": [{ "num": num, "value": ndText }] + "exampleValues": [{ "num": num, "value": ndText }], + "unique_index": unique_index, }); } } else { //如果元素节点已经存在,则只需要插入值就可以了 @@ -432,7 +440,8 @@ export function generateMultiParameters() { let nd = global.nodeList[num]["node"]; ndPath = global.nodeList[num]["xpath"]; ndAllXPaths = global.nodeList[num]["allXPaths"]; - global.outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow }); + let unique_index = Math.random().toString(36).substring(2) + Date.now().toString(36);; + global.outputParameterNodes.push({ "node": nd, "unique_index": unique_index, "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow }); nd.style.boxShadow = global.boxShadowColor; // ndText = $(nd).text(); ndText = nd.textContent; @@ -445,7 +454,8 @@ export function generateMultiParameters() { "desc": "", //参数描述 "relativeXPath": ndPath, "allXPaths": ndAllXPaths, - "exampleValues": [{ "num": 0, "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") }] + "exampleValues": [{ "num": 0, "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") }], + "unique_index": unique_index, }); } else if (nd.tagName == "A") { //如果元素是超链接 global.outputParameters.push({ @@ -456,7 +466,8 @@ export function generateMultiParameters() { "desc": "", //参数描述 "relativeXPath": ndPath, "allXPaths": ndAllXPaths, - "exampleValues": [{ "num": 0, "value": ndText }] + "exampleValues": [{ "num": 0, "value": ndText }], + "unique_index": unique_index, }); global.outputParameters.push({ "nodeType": 2, @@ -466,7 +477,8 @@ export function generateMultiParameters() { "desc": "", //参数描述 "relativeXPath": ndPath, "allXPaths": ndAllXPaths, - "exampleValues": [{ "num": 0, "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") }] + "exampleValues": [{ "num": 0, "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") }], + "unique_index": unique_index, }); } else if (nd.tagName == "INPUT") { //如果元素是输入项 global.outputParameters.push({ @@ -477,7 +489,8 @@ export function generateMultiParameters() { "desc": "", //参数描述 "relativeXPath": ndPath, "allXPaths": ndAllXPaths, - "exampleValues": [{ "num": 0, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") }] + "exampleValues": [{ "num": 0, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") }], + "unique_index": unique_index, }); } else { //其他所有情况 global.outputParameters.push({ @@ -488,7 +501,8 @@ export function generateMultiParameters() { "desc": "", //参数描述 "relativeXPath": ndPath, "allXPaths": ndAllXPaths, - "exampleValues": [{ "num": 0, "value": ndText }] + "exampleValues": [{ "num": 0, "value": ndText }], + "unique_index": unique_index, }); } } @@ -501,140 +515,341 @@ export function generateMultiParameters() { } //处理子元素,对于每个块中多出的特殊元素,需要特殊处理 -export function handleDescendents() { +export function handleDescendents(mode = 0) { let n = 1; chrome.storage.local.get({ parameterNum: 1 }, function(items) { let at = parseInt(new Date().getTime()); n = items.parameterNum; clearParameters(); //清除原来的参数列表 global.app._data.selectedDescendents = true; - let nd, ndText, ndPath, pname, ndAllPaths; - for (let num = 0; num < global.nodeList.length; num++) { - let tnode = global.nodeList[num]["node"]; - let stack = new Array(); //深度优先搜索遍历元素 - stack.push(tnode); //从此节点开始 - while (stack.length > 0) { - let nd = stack.pop(); // 挨个取出元素 - if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") { - continue; //对A标签内的SPAN元素不进行处理,剪枝,此时子元素根本不加入stack,即实现了此功能 - } - ndPath = readXPath(nd, 1, tnode); - ndAllPaths = getElementXPaths(nd, tnode); - let index = -1; - for (let i = 0; i < global.outputParameters.length; i++) { - if (global.outputParameters[i]["relativeXPath"] == ndPath) { - index = i; - break; + let nd, ndText, ndPath, pname, ndAllPaths, tmode; + tmode = mode; + if(mode == 2){ + let xpath_list = []; + // mode == 1; //如果是选中全部块的共有子元素,则先选中和第一个块相同的子元素,然后最后再删除第一个块中和其他块不同的子元素 + for (let num = 0; num < global.nodeList.length; num++) { + let node_xpaths = []; + let tnode = global.nodeList[num]["node"]; + let stack = new Array(); //深度优先搜索遍历元素 + stack.push(tnode); //从此节点开始 + while (stack.length > 0) { + let nd = stack.pop(); // 挨个取出元素 + if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") { + continue; //对A标签内的SPAN元素不进行处理,剪枝,此时子元素根本不加入stack,即实现了此功能 + } + ndPath = readXPath(nd, 1, tnode); + node_xpaths.push(ndPath); + for (let i = nd.children.length - 1; i >= 0; i--) { + stack.push(nd.children[i]); } } - global.outputParameterNodes.push({ - "node": nd, - "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow - }); - nd.style.boxShadow = global.boxShadowColor; - // ndText = $(nd).contents().filter(function() { - // return this.nodeType === 3; - // }).text().replace(/\s+/g, ''); - ndText = ""; - let ndContents = nd.childNodes; - for (var i = 0; i < ndContents.length; i++) { - if (ndContents[i].nodeType === 3) { // if it's a text node - ndText += ndContents[i].textContent.trim(); // add its content to the string + xpath_list.push(node_xpaths); + } + // 取第一个子数组作为初始的共有元素集合 + let commonXPaths = new Set(xpath_list[0]); + // 遍历剩余的子数组 + for (let i = 1; i < xpath_list.length; i++) { + // 使用过滤函数来筛选出与共有元素集合中的元素相同的元素 + commonXPaths = new Set(xpath_list[i].filter(element => commonXPaths.has(element))); + } + // 将共有元素集合转换为数组 + let commonXPathList = Array.from(commonXPaths); + console.log(commonXPathList); + let hash = {}; //记录index和数组位置的对应关系 + for (let num = 0; num < global.nodeList.length; num++) { + let tnode = global.nodeList[num]["node"]; + let stack = new Array(); //深度优先搜索遍历元素 + stack.push(tnode); //从此节点开始 + while (stack.length > 0) { + let nd = stack.pop(); // 挨个取出元素 + if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") { + continue; //对A标签内的SPAN元素不进行处理,剪枝,此时子元素根本不加入stack,即实现了此功能 } - } - ndText = ndText.replace(/\s+/g, ''); // remove any whitespace characters - if (index == -1) { //从第二个节点开始,只插入那些不在参数列表中的元素,根据xpath进行寻址 - //如果当前节点除了子元素外仍然有其他文字或者该元素是图片/表单项,加入子元素节点 - if (ndText != "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") { - if (nd.tagName == "IMG") { //如果元素是图片 - global.outputParameters.push({ - "nodeType": 4, //节点类型 - "contentType": 1, // 内容类型 - "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只选择了子元素没有选中全部的时候,需要判断 - "name": parameterName("参数") + (n++) + parameterName("_图片地址"), - "desc": "", //参数描述 - "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), //同理需要判断 - "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), - "exampleValues": [{ - "num": num, - "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") - }] - }); - } else if (nd.tagName == "A") { //如果元素是超链接 - global.outputParameters.push({ - "nodeType": 1, - "contentType": 0, // 内容类型 - "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": parameterName("参数") + (n++) + parameterName("_链接文本"), - "desc": "", //参数描述 - "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), - "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), - "exampleValues": [{ "num": num, "value": nd.textContent }] //注意这里的ndtext是整个a的文字!!! - }); - global.outputParameters.push({ - "nodeType": 2, - "contentType": 0, // 内容类型 - "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": parameterName("参数") + (n++) + parameterName("_链接地址"), - "desc": "", //参数描述 - "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), - "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), - "exampleValues": [{ - "num": num, - "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") - }] - }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 - global.outputParameters.push({ - "nodeType": 3, - "contentType": 1, // 内容类型 - "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": parameterName("参数") + (n++) + parameterName("_文本"), - "desc": "", //参数描述 - "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), - "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), - "exampleValues": [{ - "num": num, - "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") - }] - }); - } else { //其他所有情况 - global.outputParameters.push({ - "nodeType": 0, - "contentType": 1, // 内容类型 - "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": parameterName("参数") + (n++) + parameterName("_文本"), - "desc": "", //参数描述 - "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), - "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), - "exampleValues": [{ "num": num, "value": ndText }] - }); + ndPath = readXPath(nd, 1, tnode); + ndAllPaths = getElementXPaths(nd, tnode); + let index = -1; + for (let i = 0; i < commonXPathList.length; i++) { + if (commonXPathList[i] == ndPath) { + index = i; + break; } } - } else //如果元素节点已经存在,则只需要插入值就可以了 - { - if (nd.tagName == "IMG") { //如果元素是图片 - global.outputParameters[index]["exampleValues"].push({ - "num": num, - "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") - }); - } else if (nd.tagName == "A") { //如果元素是超链接 - global.outputParameters[index]["exampleValues"].push({ "num": num, "value": nd.textContent }); - global.outputParameters[index + 1]["exampleValues"].push({ - "num": num, - "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") - }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 - global.outputParameters[index]["exampleValues"].push({ - "num": num, - "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") - }); - } else { //其他所有情况 - global.outputParameters[index]["exampleValues"].push({ "num": num, "value": ndText }); + + if (index != -1) { //如果是共有元素 + let unique_index = ndPath; + ndText = ""; + let ndContents = nd.childNodes; + for (let i = 0; i < ndContents.length; i++) { + if (ndContents[i].nodeType === 3) { // if it's a text node + ndText += ndContents[i].textContent.trim(); // add its content to the string + } + } + ndText = ndText.replace(/\s+/g, ''); // remove any whitespace characters + if (ndText != "" || nd == tnode) { + global.outputParameterNodes.push({ + "node": nd, + "unique_index": unique_index, + "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow + }); + nd.style.boxShadow = global.boxShadowColor; + } + console.log("Hash", hash); + if (num == 0) { //从第二个节点开始,只插入那些不在参数列表中的元素,根据xpath进行寻址 + //如果当前节点除了子元素外仍然有其他文字或者该元素是图片/表单项,加入子元素节点 + if (ndText != "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") { + hash[index] = global.outputParameters.length; + if (nd.tagName == "IMG") { //如果元素是图片 + global.outputParameters.push({ + "nodeType": 4, //节点类型 + "contentType": 1, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只选择了子元素没有选中全部的时候,需要判断 + "name": parameterName("参数") + (n++) + parameterName("_图片地址"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), //同理需要判断 + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{ + "num": num, + "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") + }], + "unique_index": unique_index, + }); + } else if (nd.tagName == "A") { //如果元素是超链接 + global.outputParameters.push({ + "nodeType": 1, + "contentType": 0, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_链接文本"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{"num": num, "value": nd.textContent}], //注意这里的ndtext是整个a的文字!!! + "unique_index": unique_index, + }); + global.outputParameters.push({ + "nodeType": 2, + "contentType": 0, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_链接地址"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{ + "num": num, + "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") + }], + "unique_index": unique_index, + }); + } else if (nd.tagName == "INPUT") { //如果元素是输入项 + global.outputParameters.push({ + "nodeType": 3, + "contentType": 1, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_文本"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{ + "num": num, + "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") + }], + "unique_index": unique_index, + }); + } else { //其他所有情况 + global.outputParameters.push({ + "nodeType": 0, + "contentType": 1, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_文本"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{"num": num, "value": ndText}], + "unique_index": unique_index, + }); + } + } + } else //如果元素节点已经存在,则只需要插入值就可以了 + { + try{ + if (ndText != "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") { + if (nd.tagName == "IMG") { //如果元素是图片 + global.outputParameters[hash[index]]["exampleValues"].push({ + "num": num, + "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") + }); + } else if (nd.tagName == "A") { //如果元素是超链接 + global.outputParameters[hash[index]]["exampleValues"].push({ + "num": num, + "value": nd.textContent + }); + global.outputParameters[hash[index] + 1]["exampleValues"].push({ + "num": num, + "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") + }); + } else if (nd.tagName == "INPUT") { //如果元素是输入项 + global.outputParameters[hash[index]]["exampleValues"].push({ + "num": num, + "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") + }); + } else { //其他所有情况 + global.outputParameters[hash[index]]["exampleValues"].push({ + "num": num, + "value": ndText + }); + } + } + } catch (e) { + console.log("Error: -----------------------\n\n", e); + } + + } + } + for (let i = nd.children.length - 1; i >= 0; i--) { + stack.push(nd.children[i]); } } - for (let i = nd.children.length - 1; i >= 0; i--) { - stack.push(nd.children[i]); + } + } + else { + for (let num = 0; num < global.nodeList.length; num++) { + let tnode = global.nodeList[num]["node"]; + let stack = new Array(); //深度优先搜索遍历元素 + stack.push(tnode); //从此节点开始 + while (stack.length > 0) { + let nd = stack.pop(); // 挨个取出元素 + if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") { + continue; //对A标签内的SPAN元素不进行处理,剪枝,此时子元素根本不加入stack,即实现了此功能 + } + ndPath = readXPath(nd, 1, tnode); + ndAllPaths = getElementXPaths(nd, tnode); + let index = -1; + for (let i = 0; i < global.outputParameters.length; i++) { + if (global.outputParameters[i]["relativeXPath"] == ndPath) { + index = i; + break; + } + } + ndText = ""; + let ndContents = nd.childNodes; + for (let i = 0; i < ndContents.length; i++) { + if (ndContents[i].nodeType === 3) { // if it's a text node + ndText += ndContents[i].textContent.trim(); // add its content to the string + } + } + ndText = ndText.replace(/\s+/g, ''); // remove any whitespace characters + let unique_index = ndPath; + if(ndText!= "" || nd == tnode){ + if (mode == 0 || (mode == 1 && (index != -1 || num == 0))) { //如果不是应选尽选,则只添加和第一个元素相同类型的子元素 + global.outputParameterNodes.push({ + "node": nd, + "unique_index": unique_index, + "boxShadow": nd.style.boxShadow == "" || global.boxShadowColor ? "none" : nd.style.boxShadow + }); + nd.style.boxShadow = global.boxShadowColor; + } else if(mode == 1 && nd == tnode){ //最外层元素标记一下 + nd.style.boxShadow = global.boxShadowColor; + } + } + if (index == -1) { //从第二个节点开始,只插入那些不在参数列表中的元素,根据xpath进行寻址 + //如果当前节点除了子元素外仍然有其他文字或者该元素是图片/表单项,加入子元素节点 + if (!(mode == 1 && num > 0)) { //如果不是应选尽选,则只添加和第一个元素相同类型的子元素 + if (ndText!= "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") { + if (nd.tagName == "IMG") { //如果元素是图片 + global.outputParameters.push({ + "nodeType": 4, //节点类型 + "contentType": 1, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只选择了子元素没有选中全部的时候,需要判断 + "name": parameterName("参数") + (n++) + parameterName("_图片地址"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), //同理需要判断 + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{ + "num": num, + "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") + }], + "unique_index": unique_index, + }); + } else if (nd.tagName == "A") { //如果元素是超链接 + global.outputParameters.push({ + "nodeType": 1, + "contentType": 0, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_链接文本"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{"num": num, "value": nd.textContent}], //注意这里的ndtext是整个a的文字!!! + "unique_index": unique_index, + }); + global.outputParameters.push({ + "nodeType": 2, + "contentType": 0, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_链接地址"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{ + "num": num, + "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") + }], + "unique_index": unique_index, + }); + } else if (nd.tagName == "INPUT") { //如果元素是输入项 + global.outputParameters.push({ + "nodeType": 3, + "contentType": 1, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_文本"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{ + "num": num, + "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") + }], + "unique_index": unique_index, + }); + } else { //其他所有情况 + global.outputParameters.push({ + "nodeType": 0, + "contentType": 1, // 内容类型 + "relative": global.nodeList.length > 1 ? true : false, //是否为相对xpath路径 + "name": parameterName("参数") + (n++) + parameterName("_文本"), + "desc": "", //参数描述 + "relativeXPath": global.nodeList.length > 1 ? ndPath : readXPath(nd), + "allXPaths": global.nodeList.length > 1 ? ndAllPaths : getElementXPaths(nd), + "exampleValues": [{"num": num, "value": ndText}], + "unique_index": unique_index, + }); + } + } + } + } else //如果元素节点已经存在,则只需要插入值就可以了 + { + if (nd.tagName == "IMG") { //如果元素是图片 + global.outputParameters[index]["exampleValues"].push({ + "num": num, + "value": nd.getAttribute("src") == null ? "" : nd.getAttribute("src") + }); + } else if (nd.tagName == "A") { //如果元素是超链接 + global.outputParameters[index]["exampleValues"].push({ "num": num, "value": nd.textContent }); + global.outputParameters[index + 1]["exampleValues"].push({ + "num": num, + "value": nd.getAttribute("href") == null ? "" : nd.getAttribute("href") + }); + } else if (nd.tagName == "INPUT") { //如果元素是输入项 + global.outputParameters[index]["exampleValues"].push({ + "num": num, + "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") + }); + } else { //其他所有情况 + global.outputParameters[index]["exampleValues"].push({ "num": num, "value": ndText }); + } + } + for (let i = nd.children.length - 1; i >= 0; i--) { + stack.push(nd.children[i]); + } } } } @@ -665,6 +880,7 @@ export function generateValTable(multiline = true) { paravalues.push(tvalues); } global.app._data.valTable = paravalues; + console.log("生成参数表格", paravalues); } // 选中第一个节点,自动寻找同类节点 @@ -712,7 +928,7 @@ export function findRelated() { //根据path将元素放入readylist中 export function pushToReadyList(path) { let result = document.evaluate(path, document, null, XPathResult.ANY_TYPE, null); - var node = result.iterateNext(); //枚举第一个元素 + let node = result.iterateNext(); //枚举第一个元素 while (node) { //只添加不在已选中列表内的元素 let exist = false; for (let o of global.nodeList) { @@ -763,11 +979,11 @@ export function combineXpath(nameList, indexList) { // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div/div[3]/a export function relatedTest() { let at = new Date().getTime() - var testList = []; - var testpath = ""; + let testList = []; + let testpath = ""; for (let i = 0; i < global.nodeList.length; i++) { - var testnumList = []; //用于比较节点索引号不同 - var tpath = global.nodeList[i]["xpath"].split("/").splice(1); //清理第一个空元素 + let testnumList = []; //用于比较节点索引号不同 + let tpath = global.nodeList[i]["xpath"].split("/").splice(1); //清理第一个空元素 for (let j = 0; j < tpath.length; j++) { if (tpath[j].indexOf("[") >= 0) { //如果存在索引值 testnumList.push(parseInt(tpath[j].split("[")[1].replace("]", ""))); //只留下数字 @@ -785,7 +1001,7 @@ export function relatedTest() { testList.push(testnumList); } testpath = testpath.split("/"); //清理第一个空元素 - var indexList = []; //记录新生成的xpath + let indexList = []; //记录新生成的xpath //如果选中的元素属于同样的序列,则计算出序列的最佳xpath表达式 for (let j = 0; j < testList[0].length; j++) { indexList.push(testList[0][j]); diff --git a/Extension/manifest_v3/src/content-scripts/toolkit.vue b/Extension/manifest_v3/src/content-scripts/toolkit.vue index 524d614..04dc881 100644 --- a/Extension/manifest_v3/src/content-scripts/toolkit.vue +++ b/Extension/manifest_v3/src/content-scripts/toolkit.vue @@ -7,11 +7,12 @@
-

特殊点选模式

+

特殊点选模式

-
采集当前页面的标题
-
采集当前页面的网址
+
采集当前页面的标题
+
采集当前页面的网址
+

● 鼠标移动到笑脸☺查看操作提示。

● 鼠标移动到元素上后,请右键点击或者按F7键选中页面元素。

● 通过鼠标左键进行点击时,页面也会有反应,但左键点击发生的操作不会被记录在任务流程中;同理,如果想输入文本框但并不想将动作记录,可以鼠标移动到文本框,并按键盘的F9进行输入。 @@ -26,37 +27,37 @@ v-if="numOfReady()>0&&tname()!='下一页元素'">同时发现{{ numOfReady() }}个同类元素(如果不全或不准请继续手动选择其余您认为的同类元素),您可以:

选中全部
+ title="">
选中子元素
+ v-on:mousedown="selectDescendents">选中子元素
-
切换下拉选项
-
输入文字
+
切换下拉选项
+
输入文字
采集该{{ tname() }}的文本
-
采集当前选中项的值
-
采集当前选中项的文本
+ title="采集文本">
+
采集当前选中项的值
+
采集当前选中项的文本
采集该{{ tname() }}的地址
+ v-on:mousedown="getLink">采集该{{ tname() }}的地址
点击该{{ tname() }}
+ v-on:mousedown="clickElement">点击该{{ tname() }}
循环点击该{{ tname() }}
-
采集该{{ tname() }}的背景图片地址
+ v-on:mousedown="loopClickSingleElement">循环点击该{{ tname() }} +
采集该{{ tname() }}的背景图片地址
采集该{{ tname() }}的Inner - Html
-
采集该{{ tname() }}的Outer Html
+ Html +
采集该{{ tname() }}的Outer Html
-
鼠标移动到该{{ tname() }}上
+
鼠标移动到该{{ tname() }}上
-
采集数据
+
采集数据
-
确认采集
+
确认采集
@@ -67,8 +68,8 @@
● 已选择了以下元素,您可以:
-
采集数据
-
撤销本次选择
+
采集数据
+
撤销本次选择
@@ -76,15 +77,19 @@ ● 已选择了{{ numOfList() }}个同类元素,另外发现{{ numOfReady() }}个同类元素(如果不全或不准请继续手动选择其余您认为的同类元素),您可以:
-
选中全部
+
选中全部
选中子元素
-
采集数据
+ v-on:mousedown="selectDescendents">选中子元素(应选尽选)
+
选中子元素(相对首选块共同元素)
+
选中子元素(所有块共同元素)
+
采集数据
循环点击每个{{ tname() }}
+ v-on:mousedown="loopClickEveryElement">循环点击每个{{ tname() }}
循环移动到每个{{ tname() }}
-
撤销本次选择
+ v-on:mousedown="loopMouseMove">循环移动到每个{{ tname() }} +
撤销本次选择
@@ -92,8 +97,9 @@
{{ setWidth("290px") }} - + +
{{ i["name"] }}
{{ i["name"] }}
×
删除
@@ -104,6 +110,7 @@ v-on:mousedown="deleteSingleLine">× +
@@ -154,11 +161,12 @@
-

Special click mode

+

Special click mode

-
Collect Title of current page
-
Collect URL of current page
+
Collect Title of current page
+
Collect URL of current page
+

● Mouse move to smiling face ☺ to see operation help.

● When your mouse moves to the element, please right-click your mouse button or press F7 on the keyboard to select it.

● When clicked with the left mouse button, the page will also respond, but this click operation will not be recorded in the task flow. Similarly, if you want to input in a text box but do not want the action to be recorded , you can move the mouse to the text box and press F9 on the keyboard to input.

@@ -173,41 +181,41 @@ can:
Select All
+ title="">
Select child elements
+ v-on:mousedown="selectDescendents">Select child elements
-
Change selection option
-
Input Text +
Change selection option
+
Input Text
Extract {{ tname() | toEng }}'s text
-
Collect selected option value
-
Collect selected option text
+ title="collect text">
+
Collect selected option value
+
Collect selected option text
Collect address of this - {{ tname() | toEng }}
+ {{ tname() | toEng }}
Click - this {{ tname() | toEng }}
+ this {{ tname() | toEng }}
Loop-click this {{ tname() | toEng }} + title="Usually used to loop-click the next-page button">☺
-
Collect background image URL
+
Collect background image URL
Collect Inner Html of - this {{ tname() | toEng }}
-
Collect Outer Html of this element + this {{ tname() | toEng }}
+
Collect Outer Html of this element
-
Move mouse to this element
+
Move mouse to this element
-
Collect Data
+
Collect Data
-
Confirm Collect
+
Confirm Collect
@@ -218,8 +226,8 @@
● Already selected the following element, you can:
-
Collect Data
-
Revoke selection
+
Collect Data
+
Revoke selection
@@ -227,16 +235,20 @@ ● Already selected {{ numOfList() }} similar elements, and we find other{{ numOfReady() }} similar elements (If unsatisfied with auto-detected similar elements, you can continue to manually select the rest of the elements that you think are similar), you can:
-
Select All
+
Select All
Select child elements
-
Collect Data
+ v-on:mousedown="selectDescendents">Select child elements (Greedy)
+
Select child elements (RFSE)
+
Select child elements (RASE)
+
Collect Data
Loop-click every {{ tname() | toEng}} + v-on:mousedown="loopClickEveryElement">Loop-click every {{ tname() | toEng}}
Loop-mouse-move to every {{ tname() | toEng}}
-
Revoke selection
+ v-on:mousedown="loopMouseMove">Loop-mouse-move to every {{ tname() | toEng}} +
Revoke selection
@@ -246,7 +258,7 @@
{{ setWidth("350px") }} - +
{{ i["name"] }}
{{ i["name"] }}
×
Delete
@@ -357,6 +369,7 @@ export default { winHeight: window.outerHeight, optionMode: 0, optionValue: "", + mode: 0, //记录删除字段模式 }, mounted(){ this.$nextTick(() => { @@ -423,7 +436,7 @@ export default { if (global.nodeList.length > 1) { // 如果删到没有就没有其他的操作了 handleElement(); if (this.selectedDescendents) { - handleDescendents(); //如果之前有选中子元素,新加入的节点又则这里也需要重新选择子元素 + handleDescendents(this.mode); //如果之前有选中子元素,新加入的节点又则这里也需要重新选择子元素 } } else { this.valTable = []; @@ -433,6 +446,25 @@ export default { let at2 = parseInt(new Date().getTime()); console.log("delete:", at2, at, at2 - at); }, + removeField: function (event){ + let index = event.target.getAttribute("index"); + let tParameter = global.outputParameters.splice(index, 1)[0]; + if(global.outputParameters.length == 0){ + this.valTable = []; + clearEl(); + } else { //删除对应的列 + console.log("remove:", tParameter, global); + this.valTable.splice(index, 1); + for (let i = global.outputParameterNodes.length - 1; i >= 0; i--) { + let node = global.outputParameterNodes[i]; + if(node["unique_index"] == tParameter["unique_index"]){ + node["node"].style.backgroundColor = ""; + node["node"].style.boxShadow = ""; + global.outputParameterNodes.splice(i, 1); + } + } + } + }, clickElement: function () { //点击元素操作 sendSingleClick(); //先发送数据 @@ -705,8 +737,9 @@ export default { } handleElement(); //每次数组元素有变动,都需要重新处理下 }, - selectDescendents: function () { //选择所有子元素操作 - handleDescendents(); + selectDescendents: function (e, mode = 0) { //选择所有子元素操作 + handleDescendents(mode); + this.mode = mode; } }, } diff --git a/Extension/manifest_v3/src/style/toolkit.css b/Extension/manifest_v3/src/style/toolkit.css index 8cfdc99..254e64e 100644 --- a/Extension/manifest_v3/src/style/toolkit.css +++ b/Extension/manifest_v3/src/style/toolkit.css @@ -134,11 +134,28 @@ font-weight: normal !important; overflow: hidden !important; font-size: 11px !important; - padding-left: 1px !important; - padding-right: 0px !important; + padding-left: 10px !important; + padding-right: 10px !important; padding-top: 0px !important; padding-bottom: 0px !important; vertical-align: middle!important; + white-space: nowrap; +} + +.toolkitcontain th span{ + font-size: 15px; + font-weight: bold; + cursor: pointer; + /*position: absolute;*/ + /*position: ;*/ + margin-right: 10px; + vertical-align: text-bottom!important; +} + +.toolkitcontain th div{ + display: inline-block!important; + overflow: hidden!important; + max-width: 90%!important; } .toolkitcontain .toolkittb2 { diff --git a/Readme.md b/Readme.md index 042e02b..4272ab1 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ ## 请您Star/Please Star -如果您觉得此工具不错,请轻轻点击此页面右上角**Star**按钮增加项目曝光度,谢谢!软件完全免费,只求大家Star和宣传给其他需要的朋友,谢谢! +如果您觉得此工具不错,请轻轻点击此页面右上角**Star**按钮增加项目曝光度,谢谢!软件完全免费(商用除外),只求大家Star和宣传给其他需要的朋友,谢谢! If you think this tool is good, please gently click the **Star** button in the upper right corner at this page to increase the project exposure, thank you! The software is completely free, only ask everyone to Star and promote it to other friends in need, thank you!