contentscript.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. //表现逻辑层的处理
  2. if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) {
  3. throw "serviceGrid"; //如果是服务器网页页面,则不执行工具
  4. }
  5. //Vueelement
  6. var app;
  7. generateToolkit();
  8. //生成Toolkit
  9. function generateToolkit() {
  10. $(".tooltips").html(`
  11. <div id="realcontent">
  12. <div class="tooldrag">✍Operation Toolbox (Can drag)</div>
  13. <div class="realcontent">
  14. <div v-if="page==0">
  15. <input type="checkbox" style="width:15px;height:15px;vertical-align:middle;" v-on:mousedown="specialSelect"> </input>
  16. <p style="margin-bottom:10px;display:inline-block">Special click mode</p>
  17. <div v-if="list.nl.length==0">
  18. <p style="color:black">● When your mouse moves to the element, please use your<strong>right mouse button</strong>or<strong>F7</strong> on the keyboard to select it.</p>
  19. <p style="color:black">● You can click the back button to go back to the page</p>
  20. {{initial()}}
  21. </div>
  22. <div v-if="list.nl.length==1">
  23. <div v-if="tname()!='null'">
  24. ● Already selected {{numOfList()}} {{tname()}},<span v-if="numOfReady()>0&&tname()!='Elements in next page'">, meanwhile we find {{numOfReady()}} element with the same type, </span>you can:
  25. <div class="innercontent">
  26. <div v-if="numOfReady()>0 && !selectStatus"> <a v-on:mousedown="selectAll">Select All</a> <span title="">☺</span></div>
  27. <div v-if="existDescendents()&& !selectStatus &&(tname()=='element' || tname()=='link')"> <a v-on:mousedown="selectDescendents">Select child elements</a> <span title="">☺</span></div>
  28. <div v-if="!selectedDescendents && !selectStatus" id="Single">
  29. <div v-if="tname()=='selection box'"> <a>---------</a><span title="">☺</span></div>
  30. <div v-if="tname()=='text box'"> <a v-on:mousedown="setInput">Input Text</a><span title="">☺</span></div>
  31. <div v-if="tname()!='Image'"> <a v-on:mousedown="getText">Collect {{tname()}}'s text</a><span title="collet text">☺</span></div>
  32. <div v-if="tname()=='selection box'"> <a>采集选中项的text</a><span title="">☺</span></div>
  33. <div v-if="tname()=='link'||tname()=='Image'"> <a v-on:mousedown="getLink">采集该{{tname()}}的Address</a><span title="">☺</span></div>
  34. <div v-if="tname()!='selection box' && tname()!='text box'"> <a v-on:mousedown="clickElement">点击该{{tname()}}</a><span title="">☺</span></div>
  35. <div v-if="tname()!='selection box' && tname()!='text box'"> <a v-on:mousedown="loopClickSingleElement">循环点击该{{tname()}}</a><span title="">☺</span></div>
  36. <div v-if="tname()=='link'||tname()=='element'"> <a v-on:mousedown="getInnerHtml">采集该{{tname()}}的Inner Html</a><span title="">☺</span></div>
  37. <div> <a v-on:mousedown="getOuterHtml">采集该{{tname()}}的Outer Html</a><span title="">☺</span></div>
  38. <div> <a href="#">----{{tname()}}-</a><span title="">☺</span></div>
  39. <div v-if="tname()=='text box'"> <a>-----</a><span title="">☺</span></div>
  40. </div>
  41. <div v-if="selectedDescendents" id="Single">
  42. <div><a v-on:mousedown="confirmCollectSingle">Collect Data</a><span title="">☺</span></div>
  43. </div>
  44. <div v-if="selectStatus" id="Confirm">
  45. <div><a v-on:mousedown="confirmCollectSingle">Confirm Collect</a><span title="">☺</span></div>
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. <div v-if="list.nl.length>1">
  51. <div v-if="option==100">
  52. ● 已select了以下element,您可以:
  53. <div class="innercontent">
  54. <div> <a v-on:mousedown="confirmCollectMulti">采集数据</a><span title="">☺</span> </div>
  55. <div> <a v-on:mousedown="revoke">撤销本次selection</a><span title="">☺</span></div>
  56. </div>
  57. </div>
  58. <div v-if="option!=100">
  59. ● 已select了{{numOfList()}}个同类element,<span v-if="numOfReady()>0">另外发现{{numOfReady()}}个同类element,</span>您可以:
  60. <div class="innercontent">
  61. <div v-if="numOfReady()>0"> <a v-on:mousedown="selectAll">选中全部</a><span title="">☺</span></div>
  62. <div v-if="existDescendents()&&(tname()=='element' || tname()=='link')"> <a v-on:mousedown="selectDescendents">选中子element</a><span title="">☺</span></div>
  63. <div> <a v-on:mousedown="confirmCollectMultiAndDescendents">采集数据</a><span title="">☺</span></div>
  64. <div v-if="tname()!='selection box' && tname()!='text box' && !selectedDescendents"> <a v-on:mousedown="loopClickEveryElement">循环点击每个{{tname()}}</a><span title="">☺</span></div>
  65. <div> <a v-on:mousedown="revoke">撤销本次selection</a><span title="">☺</span></div>
  66. </div>
  67. </div>
  68. </div>
  69. <div v-if="valTable.length>0">
  70. <div class="toolkitcontain">{{setWidth("290px")}}
  71. <table class="toolkittb2" cellspacing="0">
  72. <tbody>
  73. <th v-for="i in list.opp">{{i["name"]}}</th>
  74. <th style="width:40px">删除</td>
  75. </tbody>
  76. </table>
  77. <table class="toolkittb4" cellspacing="0">
  78. <tbody>
  79. <tr v-for="i in valTable[0].length">
  80. <td v-for="j in list.opp.length">{{valTable[j-1][i-1]}}</td>
  81. <td v-on:mousedown="deleteSingleLine" style="font-size: 22px!important;width:40px;cursor:pointer" v-bind:index="i-1">×</td>
  82. </tr>
  83. </table>
  84. </div>
  85. </div>
  86. <div v-if="valTable.length==0&&tname()!='Elements in next page'">{{setWidth("230px")}}</div>
  87. <div v-if="list.nl.length>0" style="bottom:12px;position:absolute;color:black!important;left:17px;font-size:13px">
  88. <div style="margin-bottom:5px">
  89. <button v-on:mousedown="cancel">取消selection</button>
  90. <button v-if="!selectStatus" v-on:mousedown="enlarge">扩大选区</button>
  91. </div>
  92. <p style="margin-left:16px;margin-bottom:0px">{{lastElementXPath()}}</p>
  93. </div>
  94. </div>
  95. <div v-if="page==1">
  96. ● 请输入文字:
  97. <input type="text" v-model="text" autofocus="autofocus" id="WTextBox"></input>
  98. <button v-on:click="getInput" style="margin-left:0px!important;">确定</button>
  99. <button v-on:click="cancelInput" style="margin-left:0px!important;">取消</button>
  100. <div class="innercontent">
  101. </div>
  102. </div>
  103. </div>
  104. `);
  105. app = new Vue({
  106. el: '#realcontent',
  107. data: {
  108. option: 0,
  109. list: { nl: nodeList, opp: outputParameters },
  110. valTable: [], // 用来存储转换后的para列表
  111. special: false, //是否为特殊selection模式
  112. selectedDescendents: false, // 标记是否选中了子element
  113. selectStatus: false, //标记单个element是否点击了采集
  114. page: 0, //默认页面,1为输入文字页面
  115. text: "", // 记录输入的文字
  116. tNodeName: "", // 记录临时节点列表
  117. nowPath: "", //现在element的xpath
  118. },
  119. watch: {
  120. nowPath: { //变量发生变化的时候进行一些操作
  121. handler: function(newVal, oldVal) {
  122. console.log("xpath:", newVal);
  123. }
  124. }
  125. },
  126. methods: {
  127. initial: function() { //每当element是0的时候,执行值的初始化操作
  128. this.selectedDescendents = false;
  129. this.selectStatus = false;
  130. this.nowPath = "";
  131. },
  132. confirmCollectSingle: function() { //单element确认采集
  133. collectSingle();
  134. clearEl();
  135. },
  136. confirmCollectMulti: function() { //无规律多element确认采集
  137. collectMultiNoPattern();
  138. clearEl();
  139. },
  140. confirmCollectMultiAndDescendents: function() { //有规律多element确认采集
  141. collectMultiWithPattern();
  142. clearEl();
  143. },
  144. deleteSingleLine: function(event) { //删除单行element
  145. let at = new Date().getTime()
  146. //流程图送element的时候,默认的使用不固定循环列表,但是一旦有删除element的操作发生,则按照固定element列表采集element
  147. index = event.target.getAttribute("index");
  148. let tnode = nodeList.splice(index, 1)[0]; //删掉当前element
  149. tnode["node"].style.backgroundColor = tnode["bgColor"];
  150. tnode["node"].style.boxShadow = tnode["boxShadow"];
  151. if (nodeList.length > 1) { // 如果删到没有就没有其他的操作了
  152. handleElement();
  153. if (this.selectedDescendents) {
  154. handleDescendents(); //如果之前有选中子element,新加入的节点又则这里也需要重新selection子element
  155. }
  156. } else {
  157. this.valTable = [];
  158. this.selectStatus = false;
  159. clearParameters(); //直接撤销重选
  160. }
  161. let at2 = parseInt(new Date().getTime());
  162. console.log("delete:", at2, at, at2 - at);
  163. },
  164. clickElement: function() { //点击element操作
  165. sendSingleClick();
  166. //先发送数据
  167. nodeList[0]["node"].focus(); //获得element焦点
  168. nodeList[0]["node"].click(); //点击element
  169. clearEl();
  170. },
  171. loopClickSingleElement: function() { //循环点击单个element
  172. sendLoopClickSingle(this.tname()); //识别下一页,循环点击单个element和点击多个element
  173. if (this.tname() != "Elements in next page") { //Elements in next page不进行点击操作
  174. nodeList[0]["node"].focus(); //获得element焦点
  175. nodeList[0]["node"].click(); //点击element
  176. }
  177. clearEl();
  178. },
  179. loopClickEveryElement: function() { //循环点击每个element
  180. sendLoopClickEvery(); //识别下一页,循环点击单个element和点击多个element
  181. nodeList[0]["node"].focus(); //获得element焦点
  182. nodeList[0]["node"].click(); //点击element
  183. clearEl();
  184. },
  185. setInput: function() { //输入文字
  186. this.page = 1;
  187. this.$nextTick(function() { //下一时刻获得焦点
  188. document.getElementById("WTextBox").focus();
  189. })
  190. },
  191. getInput: function() { //得到输入的文字
  192. nodeList[0]["node"].focus(); //获得文字焦点
  193. // nodeList[0]["node"].setAttribute("value", this.text); // 设置输入 box内容
  194. input(this.text); // 设置输入
  195. this.text = "";
  196. clearEl();
  197. },
  198. cancelInput: function() {
  199. this.page = 0;
  200. },
  201. setWidth: function(width) { //根据是否出现表格调整最外 box宽度
  202. $(".tooltips").css("width", width);
  203. return "";
  204. },
  205. getText: function() { //采集文字
  206. generateParameters(0, true, false);
  207. this.selectStatus = true;
  208. clearReady();
  209. },
  210. getLink: function() { //采集linkAddress
  211. generateParameters(0, false, true);
  212. this.selectStatus = true;
  213. clearReady();
  214. },
  215. getOuterHtml: function() { //采集OuterHtml
  216. generateParameters(3, true, false);
  217. this.selectStatus = true;
  218. clearReady();
  219. },
  220. getInnerHtml: function() { //采集InnerHtml
  221. generateParameters(2, true, false);
  222. this.selectStatus = true;
  223. clearReady();
  224. },
  225. tname: function() {
  226. let tag = nodeList.length == 0 ? "" : nodeList[0]["node"].tagName;
  227. let inputType = nodeList.length == 0 ? "" : nodeList[0]["node"].getAttribute("type");
  228. if (inputType != null) { //如果没有type属性,则默认为text
  229. inputType = inputType.toLowerCase();
  230. } else {
  231. inputType = "text";
  232. }
  233. if (tag == "") {
  234. return "null";
  235. } else if ($(nodeList[0]["node"]).contents().filter(function() { return this.nodeType === 3; }).text().indexOf("下一页") >= 0) {
  236. this.setWidth("250px");
  237. return "Elements in next page";
  238. } else if (tag == "A") {
  239. return "link";
  240. } else if (tag == "IMG") {
  241. return "Image";
  242. } else if (tag == "BUTTON" || (tag == "INPUT" && (inputType == "button" || inputType == "submit"))) {
  243. return "Button";
  244. } else if (tag == "TEXTAREA" || (tag == "INPUT" && (inputType != "checkbox" || inputType != "ratio"))) { //普通输入 box
  245. return "text box";
  246. } else if (tag == "SELECT") {
  247. return "selection box";
  248. } else {
  249. return "element";
  250. }
  251. },
  252. existDescendents: function() { //检测选中的element是否存在子element,已经选中了子element也不要再出现了
  253. return nodeList.length > 0 && nodeList[0]["node"].children.length > 0 && !this.selectedDescendents;
  254. },
  255. numOfReady: function() {
  256. return readyList.length;
  257. },
  258. numOfList: function() {
  259. return nodeList.length;
  260. },
  261. lastElementXPath: function() { //用来显示element的最大最后5个xpath路劲element
  262. path = nodeList[nodeList.length - 1]["xpath"];
  263. path = path.split("/");
  264. tp = "";
  265. if (path.length > 5) { //只保留最后五个element
  266. path = path.splice(path.length - 5, 5);
  267. tp = ".../"
  268. }
  269. for (i = 0; i < path.length; i++) {
  270. path[i] = path[i].split("[")[0];
  271. }
  272. path = path.join("/");
  273. path = "Path: " + tp + path;
  274. return path;
  275. },
  276. cancel: function() {
  277. clearEl();
  278. },
  279. specialSelect: function() { //特殊selection模式
  280. if (mousemovebind) {
  281. tdiv.style.pointerEvents = "none";
  282. this.special = false;
  283. } else {
  284. this.special = true;
  285. }
  286. mousemovebind = !mousemovebind;
  287. },
  288. enlarge: function() { // 扩大选区功能,总是扩大最后一个选中的element的选区
  289. if (nodeList[nodeList.length - 1]["node"].tagName != "BODY") {
  290. nodeList[nodeList.length - 1]["node"].style.backgroundColor = nodeList[nodeList.length - 1]["bgColor"]; //之前element恢复原来的背景颜色
  291. nodeList[nodeList.length - 1]["node"].style.boxShadow = nodeList[nodeList.length - 1]["boxShadow"]; //之前element恢复原来的背景颜色
  292. tNode = nodeList[nodeList.length - 1]["node"].parentNode; //向上走一层
  293. if (tNode != NowNode) { //扩大选区之后背景颜色的判断,当前正好选中的颜色应该是不同的
  294. sty = tNode.style.backgroundColor;
  295. } else {
  296. sty = style;
  297. }
  298. nodeList[nodeList.length - 1]["node"] = tNode;
  299. nodeList[nodeList.length - 1]["bgColor"] = sty;
  300. nodeList[nodeList.length - 1]["xpath"] = readXPath(tNode, 1);
  301. //显示 box
  302. var pos = tNode.getBoundingClientRect();
  303. div.style.display = "block";
  304. div.style.height = tNode.offsetHeight + "px";
  305. div.style.width = tNode.offsetWidth + "px";
  306. div.style.left = pos.left + "px";
  307. div.style.top = pos.top + "px";
  308. div.style.zIndex = 2147483645;
  309. div.style.pointerEvents = "none";
  310. handleElement(); //每次数组element有变动,都需要重新处理下
  311. oe = tNode;
  312. tNode.style.backgroundColor = "rgba(0,191,255,0.5)";
  313. this.selectedDescendents = false;
  314. }
  315. },
  316. selectAll: function() { //选中全部element
  317. step++;
  318. readyToList(step, false);
  319. handleElement();
  320. if (this.selectedDescendents) {
  321. handleDescendents(); //如果之前有选中子element,新加入的节点又则这里也需要重新selection子element
  322. }
  323. },
  324. revoke: function() { //撤销selection当前节点
  325. var tstep = step;
  326. step--; //步数-1
  327. while (tstep == nodeList[nodeList.length - 1]["step"]) //删掉所有当前步数的element节点
  328. {
  329. let node = nodeList.splice(nodeList.length - 1, 1)[0]; //删除数组最后一项
  330. node["node"].style.backgroundColor = node["bgColor"]; //还原原始属性和边 box
  331. node["node"].style.boxShadow = node["boxShadow"];
  332. if (NowNode == node["node"]) {
  333. style = node["bgColor"];
  334. }
  335. //处理已经有选中子element的情况
  336. // if (this.selectedDescendents) {
  337. clearParameters(); //直接撤销重选
  338. // }
  339. }
  340. handleElement(); //每次数组element有变动,都需要重新处理下
  341. },
  342. selectDescendents: function() { //selection所有子element操作
  343. handleDescendents();
  344. }
  345. },
  346. });
  347. h = $(".tooldrag").height();
  348. difference = 26 - h; //获得高度值差
  349. if (difference > 0) {
  350. $(".tooldrag").css("cssText", "height:" + (26 + difference) + "px!important")
  351. }
  352. timer = setInterval(function() { //时刻监测相应element是否存在(防止出现如百度一样element消失重写body的情况),如果不存在,添加进来
  353. if (document.body != null && document.getElementById("wrapperToolkit") == null) {
  354. this.clearInterval(); //先取消原来的计时器,再设置新的计时器
  355. document.body.append(div); //默认如果toolkit不存在则div和tdiv也不存在
  356. document.body.append(tdiv);
  357. document.body.append(toolkit);
  358. generateToolkit();
  359. // var list = document.getElementsByTagName("a");
  360. // // 对于没有特殊绑定函数的a标签,使他们在新标签页中打开
  361. // for (var i = 0; i < list.length; i++) {
  362. // if (list[i].href.indexOf("javascript") == -1 && list[i].href.indexOf("void") == -1 && list[i].href.indexOf("#") == -1 && list[i].href) {
  363. // list[i].setAttribute("target", "_blank");
  364. // }
  365. // };
  366. // list = document.getElementsByTagName("form");
  367. // // 对于没有特殊绑定函数的form标签,使他们在新标签页中打开
  368. // for (var i = 0; i < list.length; i++) {
  369. // list[i].setAttribute("target", "_blank");
  370. // };
  371. }
  372. }, 3000);
  373. }
  374. //每次对element进行增删之后需要执行的操作
  375. function handleElement() {
  376. clearReady(); //预备element每次处理都先处理掉
  377. if (nodeList.length > 1) { //选中了许多element的情况
  378. app._data.option = relatedTest();
  379. if (app._data.option == 100) {
  380. generateMultiParameters();
  381. } else {
  382. generateParameters(0);
  383. }
  384. } else if (nodeList.length == 1) {
  385. findRelated(); //寻找和element相关的element
  386. }
  387. }
  388. function clearParameters(deal = true) //清空para列表
  389. {
  390. if (deal) //是否取消对选中的子element进行处理
  391. {
  392. app._data.selectedDescendents = false;
  393. }
  394. for (o of outputParameterNodes) {
  395. o["node"].style.boxShadow = o["boxShadow"];
  396. }
  397. outputParameterNodes.splice(0);
  398. outputParameters.splice(0); //清空原来的para列表
  399. app._data.valTable = []; //清空展现数组
  400. app._data.selectStatus = false;
  401. }
  402. //根据nodelist列表内的element生成para列表
  403. //适合:nodelist中的element为同类型element
  404. //type:0为全部text 1为节点内直接的文字 2为innerhtml 3为outerhtml
  405. //nodetype:0,对应全type0123
  406. //nodetype:1 link,对应type0123
  407. //nodetype:2 linkAddress 对应type0
  408. //nodetype:3 按钮和输入text box 对应type
  409. //nodetype:4 按钮和输入text box 对应type
  410. function generateParameters(type, linktext = true, linkhref = true) {
  411. clearParameters(false);
  412. let n = 1;
  413. chrome.storage.local.get({ parameterNum: 1 }, function(items) {
  414. let at = parseInt(new Date().getTime());
  415. n = items.parameterNum;
  416. for (let num = 0; num < nodeList.length; num++) {
  417. let nd = nodeList[num]["node"];
  418. ndPath = nodeList[num]["xpath"];
  419. outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow });
  420. nd.style.boxShadow = boxShadowColor;
  421. let pname = "text";
  422. let ndText = "";
  423. if (type == 0) {
  424. ndText = $(nd).text();
  425. pname = "text";
  426. if (nd.tagName == "IMG") {
  427. ndText = nd.getAttribute("src") == null ? "" : $(nd).prop("src");
  428. pname = "Address";
  429. } else if (nd.tagName == "INPUT") {
  430. ndText = nd.getAttribute("value") == null ? "" : nd.getAttribute("value");
  431. }
  432. } else if (type == 1) {
  433. ndText = $(nd).contents().filter(function() { return this.nodeType === 3; }).text().replace(/\s+/g, '');
  434. pname = "text";
  435. if (nd.tagName == "IMG") {
  436. ndText = nd.getAttribute("src") == null ? "" : $(nd).prop("src");
  437. pname = "Address";
  438. } else if (nd.tagName == "INPUT") {
  439. ndText = nd.getAttribute("value") == null ? "" : nd.getAttribute("value");
  440. }
  441. } else if (type == 2) {
  442. ndText = $(nd).html();
  443. pname = "Innerhtml";
  444. } else if (type == 3) {
  445. ndText = $(nd).prop("outerHTML");
  446. pname = "outerHTML";
  447. }
  448. if (num == 0) { //第一个节点新建,后面的增加即可
  449. if (nd.tagName == "IMG") { //如果element是Image
  450. outputParameters.push({
  451. "nodeType": 4, //节点类型
  452. "contentType": type, // 内容类型
  453. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  454. "name": "para" + (n++) + "_Image" + pname,
  455. "desc": "", //para描述
  456. "relativeXpath": nodeList.length > 1 ? "" : ndPath,
  457. "exampleValues": [{ "num": num, "value": ndText }]
  458. });
  459. } else if (nd.tagName == "A") { //如果element是超link
  460. if (linktext) {
  461. outputParameters.push({
  462. "nodeType": 1,
  463. "contentType": type, // 内容类型
  464. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  465. "name": "para" + (n++) + "_link" + pname,
  466. "desc": "", //para描述
  467. "relativeXpath": nodeList.length > 1 ? "" : ndPath,
  468. "exampleValues": [{ "num": num, "value": ndText }]
  469. });
  470. }
  471. if (linkhref) {
  472. outputParameters.push({
  473. "nodeType": 2,
  474. "contentType": type, // 内容类型
  475. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  476. "name": "para" + (n++) + "_linkAddress",
  477. "desc": "", //para描述
  478. "relativeXpath": nodeList.length > 1 ? "" : ndPath,
  479. "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }]
  480. });
  481. }
  482. } else if (nd.tagName == "INPUT") { //如果element是输入项
  483. outputParameters.push({
  484. "nodeType": 3,
  485. "contentType": type, // 内容类型
  486. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  487. "name": "para" + (n++) + "_" + pname,
  488. "desc": "", //para描述
  489. "relativeXpath": nodeList.length > 1 ? "" : ndPath,
  490. "exampleValues": [{ "num": num, "value": ndText }]
  491. });
  492. } else { //其他所有情况
  493. outputParameters.push({
  494. "nodeType": 0,
  495. "contentType": type, // 内容类型
  496. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  497. "name": "para" + (n++) + "_" + pname,
  498. "desc": "", //para描述
  499. "relativeXpath": nodeList.length > 1 ? "" : ndPath,
  500. "exampleValues": [{ "num": num, "value": ndText }]
  501. });
  502. }
  503. } else { //如果element节点已经存在,则只需要插入值就可以了
  504. if (nd.tagName == "IMG") { //如果element是Image
  505. outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText });
  506. } else if (nd.tagName == "A") { //如果element是超link
  507. outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText });
  508. outputParameters[1]["exampleValues"].push({ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") });
  509. } else if (nd.tagName == "INPUT") { //如果element是输入项
  510. outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText });
  511. } else { //其他所有情况
  512. outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText });
  513. }
  514. }
  515. }
  516. let at2 = parseInt(new Date().getTime());
  517. console.log("generateParameters:", at2, at, at2 - at);
  518. generateValTable();
  519. console.log(outputParameters);
  520. });
  521. }
  522. //根据nodelist列表内的element生成para列表
  523. //适合:nodelist中的element为不同类型element
  524. function generateMultiParameters() {
  525. clearParameters(false);
  526. let n = 1;
  527. chrome.storage.local.get({ parameterNum: 1 }, function(items) {
  528. let at = parseInt(new Date().getTime());
  529. n = items.parameterNum;
  530. for (let num = 0; num < nodeList.length; num++) {
  531. let nd = nodeList[num]["node"];
  532. ndPath = nodeList[num]["xpath"];
  533. outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow });
  534. nd.style.boxShadow = boxShadowColor;
  535. ndText = $(nd).text();
  536. if (nd.tagName == "IMG") { //如果element是Image
  537. outputParameters.push({
  538. "nodeType": 4, //节点类型
  539. "contentType": 0, // 内容类型
  540. "relative": false, //是否为相对xpath路径
  541. "name": "para" + (n++) + "_imageAddress",
  542. "desc": "", //para描述
  543. "relativeXpath": ndPath,
  544. "exampleValues": [{ "num": 0, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }]
  545. });
  546. } else if (nd.tagName == "A") { //如果element是超link
  547. outputParameters.push({
  548. "nodeType": 1,
  549. "contentType": 0, // 内容类型
  550. "relative": false, //是否为相对xpath路径
  551. "name": "para" + (n++) + "_linktext",
  552. "desc": "", //para描述
  553. "relativeXpath": ndPath,
  554. "exampleValues": [{ "num": 0, "value": ndText }]
  555. });
  556. outputParameters.push({
  557. "nodeType": 2,
  558. "contentType": 0, // 内容类型
  559. "relative": false, //是否为相对xpath路径
  560. "name": "para" + (n++) + "_linkAddress",
  561. "desc": "", //para描述
  562. "relativeXpath": ndPath,
  563. "exampleValues": [{ "num": 0, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }]
  564. });
  565. } else if (nd.tagName == "INPUT") { //如果element是输入项
  566. outputParameters.push({
  567. "nodeType": 3,
  568. "contentType": 0, // 内容类型
  569. "relative": false, //是否为相对xpath路径
  570. "name": "para" + (n++) + "_text",
  571. "desc": "", //para描述
  572. "relativeXpath": ndPath,
  573. "exampleValues": [{ "num": 0, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") }]
  574. });
  575. } else { //其他所有情况
  576. outputParameters.push({
  577. "nodeType": 0,
  578. "contentType": 0, // 内容类型
  579. "relative": false, //是否为相对xpath路径
  580. "name": "para" + (n++) + "_text",
  581. "desc": "", //para描述
  582. "relativeXpath": ndPath,
  583. "exampleValues": [{ "num": 0, "value": ndText }]
  584. });
  585. }
  586. }
  587. // console.log(outputParameters);
  588. let at2 = parseInt(new Date().getTime());
  589. console.log("generateMultiParameters", at2, at, at2 - at);
  590. generateValTable(false);
  591. });
  592. }
  593. //处理子element,对于每个块中多出的特殊element,需要特殊处理
  594. function handleDescendents() {
  595. let n = 1;
  596. chrome.storage.local.get({ parameterNum: 1 }, function(items) {
  597. let at = parseInt(new Date().getTime());
  598. n = items.parameterNum;
  599. clearParameters(); //清除原来的para列表
  600. app._data.selectedDescendents = true;
  601. for (let num = 0; num < nodeList.length; num++) {
  602. let tnode = nodeList[num]["node"];
  603. let stack = new Array(); //深度优先搜索遍历element
  604. stack.push(tnode); //从此节点开始
  605. while (stack.length > 0) {
  606. let nd = stack.pop(); // 挨个取出element
  607. if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") {
  608. continue; //对A标签内的SPANelement不进行处理,剪枝,此时子element根本不加入stack,即实现了此功能
  609. }
  610. ndPath = readXPath(nd, 1, tnode);
  611. let index = -1;
  612. for (let i = 0; i < outputParameters.length; i++) {
  613. if (outputParameters[i]["relativeXpath"] == ndPath) {
  614. index = i;
  615. break;
  616. }
  617. }
  618. outputParameterNodes.push({
  619. "node": nd,
  620. "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow
  621. });
  622. nd.style.boxShadow = boxShadowColor;
  623. ndText = $(nd).contents().filter(function() {
  624. return this.nodeType === 3;
  625. }).text().replace(/\s+/g, '');
  626. if (index == -1) { //从第二个节点开始,只插入那些不在para列表中的element,根据xpath进行寻址
  627. //如果当前节点除了子element外仍然有其他文字或者该element是Image/表单项,加入子element节点
  628. if (ndText != "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") {
  629. if (nd.tagName == "IMG") { //如果element是Image
  630. outputParameters.push({
  631. "nodeType": 4, //节点类型
  632. "contentType": 1, // 内容类型
  633. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只selection了子element没有选中全部的时候,需要判断
  634. "name": "para" + (n++) + "_imageAddress",
  635. "desc": "", //para描述
  636. "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), //同理需要判断
  637. "exampleValues": [{
  638. "num": num,
  639. "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src")
  640. }]
  641. });
  642. } else if (nd.tagName == "A") { //如果element是超link
  643. outputParameters.push({
  644. "nodeType": 1,
  645. "contentType": 0, // 内容类型
  646. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  647. "name": "para" + (n++) + "_linktext",
  648. "desc": "", //para描述
  649. "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd),
  650. "exampleValues": [{ "num": num, "value": $(nd).text() }] //注意这里的ndtext是整个a的文字!!!
  651. });
  652. outputParameters.push({
  653. "nodeType": 2,
  654. "contentType": 0, // 内容类型
  655. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  656. "name": "para" + (n++) + "_linkAddress",
  657. "desc": "", //para描述
  658. "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd),
  659. "exampleValues": [{
  660. "num": num,
  661. "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href")
  662. }]
  663. });
  664. } else if (nd.tagName == "INPUT") { //如果element是输入项
  665. outputParameters.push({
  666. "nodeType": 3,
  667. "contentType": 1, // 内容类型
  668. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  669. "name": "para" + (n++) + "_text",
  670. "desc": "", //para描述
  671. "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd),
  672. "exampleValues": [{
  673. "num": num,
  674. "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value")
  675. }]
  676. });
  677. } else { //其他所有情况
  678. outputParameters.push({
  679. "nodeType": 0,
  680. "contentType": 1, // 内容类型
  681. "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径
  682. "name": "para" + (n++) + "_text",
  683. "desc": "", //para描述
  684. "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd),
  685. "exampleValues": [{ "num": num, "value": ndText }]
  686. });
  687. }
  688. }
  689. } else //如果element节点已经存在,则只需要插入值就可以了
  690. {
  691. if (nd.tagName == "IMG") { //如果element是Image
  692. outputParameters[index]["exampleValues"].push({
  693. "num": num,
  694. "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src")
  695. });
  696. } else if (nd.tagName == "A") { //如果element是超link
  697. outputParameters[index]["exampleValues"].push({ "num": num, "value": $(nd).text() });
  698. outputParameters[index + 1]["exampleValues"].push({
  699. "num": num,
  700. "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href")
  701. });
  702. } else if (nd.tagName == "INPUT") { //如果element是输入项
  703. outputParameters[index]["exampleValues"].push({
  704. "num": num,
  705. "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value")
  706. });
  707. } else { //其他所有情况
  708. outputParameters[index]["exampleValues"].push({ "num": num, "value": ndText });
  709. }
  710. }
  711. for (let i = nd.children.length - 1; i >= 0; i--) {
  712. stack.push(nd.children[i]);
  713. }
  714. }
  715. }
  716. let at2 = parseInt(new Date().getTime());
  717. console.log("选中子element", at2, at, at2 - at);
  718. generateValTable();
  719. });
  720. }
  721. //根据para列表生成可视化para界面
  722. function generateValTable(multiline = true) {
  723. let paravalues = [];
  724. for (let i = 0; i < outputParameters.length; i++) {
  725. let tvalues = [];
  726. let tindex = 0;
  727. let l = multiline ? nodeList.length : 1;
  728. for (let j = 0; j < l; j++) {
  729. //注意第一个循环条件,index超出界限了就不需要再寻找了,其他的全是空
  730. if (tindex < outputParameters[i]["exampleValues"].length && outputParameters[i]["exampleValues"][tindex]["num"] == j) {
  731. tvalues.push(outputParameters[i]["exampleValues"][tindex]["value"]);
  732. tindex++;
  733. } else {
  734. tvalues.push(" ");
  735. }
  736. }
  737. paravalues.push(tvalues);
  738. }
  739. app._data.valTable = paravalues;
  740. }
  741. // 选中第一个节点,自动寻找同类节点
  742. // 方法:/div[1]/div[2]/div[2]/a[1]
  743. // 从倒数第一个节点开始找,看去掉方括号之后是否element数目变多,如上面的变成/div[1]/div[2]/div[2]/a
  744. // 如果没有,则恢复原状,然后试试倒数第二个:/div[1]/div[2]/div/a[1]
  745. // 直到找到第一个变多的节点或者追溯到根节点为止
  746. function findRelated() {
  747. let at = parseInt(new Date().getTime());
  748. let testPath = nodeList[0]["xpath"].split("/").splice(1); //分离xpath成 ["html","body","div[0]"]这样子
  749. let nodeNameList = [];
  750. let nodeIndexList = [];
  751. for (i = 0; i < testPath.length; i++) {
  752. nodeNameList.push(testPath[i].split("[")[0]);
  753. if (testPath[i].indexOf("[") >= 0) { //如果存在索引值
  754. nodeIndexList.push(parseInt(testPath[i].split("[")[1].replace("]", ""))); //只留下数字
  755. } else {
  756. nodeIndexList.push(-1);
  757. }
  758. }
  759. var tempPath = "";
  760. for (let i = nodeIndexList.length - 1; i >= 0; i--) {
  761. if (nodeIndexList[i] == -1) { //没有索引值直接跳过
  762. continue;
  763. }
  764. tempIndexList = [...nodeIndexList]; //复刻一个index数组
  765. tempIndexList[i] = -1; //删除索引值
  766. tempPath = combineXpath(nodeNameList, tempIndexList); //生成新的xpath
  767. var result = document.evaluate(tempPath, document, null, XPathResult.ANY_TYPE, null);
  768. result.iterateNext(); //枚举第一个element
  769. if (result.iterateNext() != null) { //如果能枚举到第二个element,说明存在同类element,选中同类element,结束循环
  770. app.$data.nowPath = tempPath; //标记此elementxpath
  771. pushToReadyList(tempPath);
  772. break;
  773. }
  774. }
  775. let at2 = parseInt(new Date().getTime());
  776. console.log("findRelated:", at2, at, at2 - at);
  777. }
  778. //根据path将element放入readylist中
  779. function pushToReadyList(path) {
  780. result = document.evaluate(path, document, null, XPathResult.ANY_TYPE, null);
  781. var node = result.iterateNext(); //枚举第一个element
  782. while (node) { //只添加不在已选中列表内的element
  783. let exist = false;
  784. for (o of nodeList) {
  785. if (o["node"] == node) {
  786. exist = true;
  787. break;
  788. }
  789. }
  790. if (!exist) {
  791. readyList.push({ "node": node, "bgColor": node.style.backgroundColor, "boxShadow": node.style.boxShadow == "" || boxShadowColor ? "none" : node.style.boxShadow });
  792. }
  793. node.style.boxShadow = boxShadowColor;
  794. node = result.iterateNext(); //枚举下一个element
  795. }
  796. }
  797. //将readyList中的element放入选中节点中
  798. function readyToList(step, dealparameters = true) {
  799. for (o of readyList) {
  800. nodeList.push({ node: o["node"], "step": step, bgColor: o["bgColor"], "boxShadow": o["boxShadow"], xpath: readXPath(o["node"], 1) });
  801. o["node"].style.backgroundColor = selectedColor;
  802. }
  803. clearReady();
  804. if (dealparameters) { //防止出现先选中子element再选中全部失效的问题
  805. generateParameters(0); //根据nodelist列表内的element生成para列表,0代表纯text
  806. }
  807. }
  808. //根据节点列表和索引列表生成XPATH
  809. // 如:["html","body","div"],[-1,-1,2],生成/html/body/div[2]
  810. function combineXpath(nameList, indexList) {
  811. let finalPath = "";
  812. for (i = 0; i < nameList.length; i++) {
  813. finalPath = finalPath + "/" + nameList[i];
  814. if (indexList[i] != -1) {
  815. finalPath = finalPath + "[" + indexList[i] + "]";
  816. }
  817. }
  818. return finalPath;
  819. }
  820. //专门测试已经选中的这些element之间有没有相关性
  821. // 举例:
  822. // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div[1]/div[3]/a[22]
  823. // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div[2]/div[3]/a[25]
  824. // 最终转换为:
  825. // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div/div[3]/a
  826. function relatedTest() {
  827. let at = new Date().getTime()
  828. var testList = [];
  829. var testpath = "";
  830. for (i = 0; i < nodeList.length; i++) {
  831. var testnumList = []; //用于比较节点索引号不同
  832. var tpath = nodeList[i]["xpath"].split("/").splice(1); //清理第一个空element
  833. for (j = 0; j < tpath.length; j++) {
  834. if (tpath[j].indexOf("[") >= 0) { //如果存在索引值
  835. testnumList.push(parseInt(tpath[j].split("[")[1].replace("]", ""))); //只留下数字
  836. } else {
  837. testnumList.push(-1);
  838. }
  839. tpath[j] = tpath[j].split("[")[0];
  840. }
  841. tp = tpath.join("/");
  842. if (i > 0 && testpath != tp) { //如果去除括号后element内存在不一致情况,直接返回默认情况代码100
  843. app.$data.nowPath = ""; //标记此elementxpath
  844. return 100;
  845. }
  846. testpath = tp;
  847. testList.push(testnumList);
  848. }
  849. testpath = testpath.split("/"); //清理第一个空element
  850. var indexList = []; //记录新生成的xpath
  851. //如果选中的element属于同样的序列,则计算出序列的最佳xpath表达式
  852. for (j = 0; j < testList[0].length; j++) {
  853. indexList.push(testList[0][j]);
  854. for (i = 1; i < testList.length; i++) {
  855. if (testList[i][j] != testList[i - 1][j]) {
  856. indexList[j] = -1; //不一致就记录成-1
  857. break;
  858. }
  859. }
  860. }
  861. var finalPath = combineXpath(testpath, indexList);
  862. app.$data.nowPath = finalPath; //标记此elementxpath
  863. pushToReadyList(finalPath);
  864. let at2 = parseInt(new Date().getTime());
  865. console.log("手动:", at2, at, at2 - at);
  866. return 50; //先返回给默认码
  867. }
  868. //实现提示 box拖拽功能
  869. $('.tooldrag').mousedown(function(e) {
  870. // e.pageX
  871. var positionDiv = $(this).offset();
  872. var distanceX = e.pageX - positionDiv.left;
  873. var distanceY = e.pageY - positionDiv.top;
  874. //alert(distanceX)
  875. // alert(positionDiv.left);
  876. $(document).mousemove(function(e) {
  877. var x = e.clientX - distanceX;
  878. var y = e.clientY - distanceY;
  879. if (x < 0) {
  880. x = 0;
  881. } else if (x > window.innerWidth - $('.tooldrag').outerWidth(true)) {
  882. x = window.innerWidth - $('.tooldrag').outerWidth(true);
  883. }
  884. if (y < 0) {
  885. y = 0;
  886. } else if (y > window.innerHeight - $('.tooldrag').outerHeight(true)) {
  887. y = window.innerHeight - $('.tooldrag').outerHeight(true);
  888. }
  889. $('.tooltips').css({
  890. 'right': window.innerWidth - x - $('.tooltips').outerWidth(true) + 'px',
  891. 'bottom': window.innerHeight - y - $('.tooltips').outerHeight(true) + 'px',
  892. });
  893. });
  894. $(document).mouseup(function() {
  895. $(document).off('mousemove');
  896. });
  897. });