From 3646513d5ba358997fb8faccae3c9bd873570092 Mon Sep 17 00:00:00 2001 From: NaiboWang-Alienware Date: Mon, 17 Oct 2022 13:38:26 +0800 Subject: [PATCH] New version --- .gitignore | 7 + ExcuteStage/.gitignore | 2 + ExcuteStage/Readme.md | 2 + ExcuteStage/ServiceWrapper_ExcuteStage.py | 245 ++++--- Extension/ServiceWrapper.crx | Bin 271939 -> 271946 bytes Extension/ServiceWrapper.pem | 52 +- .../ServiceWrapper/scripts/baozhuangscript.js | 2 +- .../ServiceWrapper/scripts/contentscript.js | 360 +++++----- .../scripts/messageInteraction.js | 2 +- LICENSE | 674 ++++++++++++++++++ Readme.md | 144 +++- ServiceGrid/backEnd/.gitignore | 3 + ServiceGrid/backEnd/backEnd/.gitignore | 1 + ServiceGrid/backEnd/backEnd/settings.py | 1 + ServiceGrid/backEnd/backEnd/urls.py | 2 - ServiceGrid/backEnd/backEnd/view.py | 75 +- ServiceGrid/backEnd/db.sqlite3 | Bin 0 -> 131072 bytes ServiceGrid/backEnd/server.conf | 40 ++ ServiceGrid/frontEnd/FlowChart.html | 160 +++-- ServiceGrid/frontEnd/FlowChart.js | 40 +- ServiceGrid/frontEnd/FlowChart_CN.html | 277 +++++++ ServiceGrid/frontEnd/FlowChart_CN.js | 561 +++++++++++++++ ServiceGrid/frontEnd/invokeService.html | 43 +- ServiceGrid/frontEnd/logic.js | 69 +- ServiceGrid/frontEnd/logic_CN.js | 335 +++++++++ ServiceGrid/frontEnd/newService.html | 8 +- ServiceGrid/frontEnd/serviceInfo.html | 65 +- ServiceGrid/frontEnd/serviceList.html | 30 +- ServiceGrid/static/.gitattributes | 1 + ServiceWrapperEntry/.gitignore | 2 + ServiceWrapperEntry/Readme.md | 11 + ServiceWrapperEntry/ServiceWrapperEntry.sln | 25 + .../ServiceWrapperEntry/.gitignore | 1 + .../ServiceWrapperEntry/App.config | 6 + .../ServiceWrapperEntry/Class1.cs | 174 +++++ .../ServiceWrapperEntry/Flow.Designer.cs | 88 +++ .../ServiceWrapperEntry/Flow.cs | 119 ++++ .../ServiceWrapperEntry/Flow.resx | 408 +++++++++++ .../ServiceWrapperEntry/Program.cs | 99 +++ .../Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 63 ++ .../Properties/Resources.resx | 117 +++ .../Properties/Settings.Designer.cs | 26 + .../Properties/Settings.settings | 7 + .../ServiceWrapperEntry/PublicVariable.cs | 32 + .../ServiceWrapperEntry.csproj | 179 +++++ .../ServiceWrapperEntry.csproj.user | 13 + .../ServiceWrapperEntry/Start.Designer.cs | 107 +++ .../ServiceWrapperEntry/Start.cs | 256 +++++++ .../ServiceWrapperEntry/Start.resx | 408 +++++++++++ .../bin/x64/Debug/.gitignore | 2 + .../ServiceWrapperEntry/favicon.ico | Bin 0 -> 16958 bytes .../ServiceWrapperEntry/packages.config | 10 + media/Picture.png | Bin 0 -> 373076 bytes media/Picture1.png | Bin 0 -> 1338862 bytes media/Picture10.png | Bin 0 -> 97268 bytes media/Picture11.png | Bin 0 -> 63687 bytes media/Picture12.png | Bin 0 -> 347441 bytes media/Picture13.png | Bin 0 -> 99669 bytes media/Picture14.png | Bin 0 -> 206835 bytes media/Picture15.png | Bin 0 -> 439296 bytes media/Picture16.png | Bin 0 -> 155751 bytes media/Picture17.png | Bin 0 -> 197611 bytes media/Picture18.png | Bin 0 -> 335581 bytes media/Picture2.png | Bin 0 -> 2966177 bytes media/Picture4.png | Bin 0 -> 208806 bytes media/Picture5.png | Bin 0 -> 1175189 bytes media/Picture6.png | Bin 0 -> 889347 bytes media/Picture7.png | Bin 0 -> 245858 bytes media/Picture8.png | Bin 0 -> 150602 bytes media/Picture9.png | Bin 0 -> 104794 bytes media/Picture90.png | Bin 0 -> 217436 bytes media/Picture91.png | Bin 0 -> 176407 bytes 73 files changed, 4857 insertions(+), 533 deletions(-) create mode 100644 ExcuteStage/Readme.md create mode 100644 LICENSE create mode 100644 ServiceGrid/backEnd/.gitignore create mode 100644 ServiceGrid/backEnd/backEnd/.gitignore create mode 100644 ServiceGrid/backEnd/server.conf create mode 100644 ServiceGrid/frontEnd/FlowChart_CN.html create mode 100644 ServiceGrid/frontEnd/FlowChart_CN.js create mode 100644 ServiceGrid/frontEnd/logic_CN.js create mode 100644 ServiceGrid/static/.gitattributes create mode 100644 ServiceWrapperEntry/.gitignore create mode 100644 ServiceWrapperEntry/Readme.md create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry.sln create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/.gitignore create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/App.config create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Class1.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Flow.Designer.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Flow.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Flow.resx create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Program.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Properties/AssemblyInfo.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Properties/Resources.Designer.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Properties/Resources.resx create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Properties/Settings.Designer.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Properties/Settings.settings create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/PublicVariable.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/ServiceWrapperEntry.csproj create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/ServiceWrapperEntry.csproj.user create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Start.Designer.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Start.cs create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/Start.resx create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/bin/x64/Debug/.gitignore create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/favicon.ico create mode 100644 ServiceWrapperEntry/ServiceWrapperEntry/packages.config create mode 100644 media/Picture.png create mode 100644 media/Picture1.png create mode 100644 media/Picture10.png create mode 100644 media/Picture11.png create mode 100644 media/Picture12.png create mode 100644 media/Picture13.png create mode 100644 media/Picture14.png create mode 100644 media/Picture15.png create mode 100644 media/Picture16.png create mode 100644 media/Picture17.png create mode 100644 media/Picture18.png create mode 100644 media/Picture2.png create mode 100644 media/Picture4.png create mode 100644 media/Picture5.png create mode 100644 media/Picture6.png create mode 100644 media/Picture7.png create mode 100644 media/Picture8.png create mode 100644 media/Picture9.png create mode 100644 media/Picture90.png create mode 100644 media/Picture91.png diff --git a/.gitignore b/.gitignore index 1fe1b00e..577f7839 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ .idea/ node_modules/ +ServiceWrapper/ +Data/ +/GPUCache +*.exe +*.ini +*.7z +*.mp4 \ No newline at end of file diff --git a/ExcuteStage/.gitignore b/ExcuteStage/.gitignore index e037b880..03d53abb 100644 --- a/ExcuteStage/.gitignore +++ b/ExcuteStage/.gitignore @@ -7,3 +7,5 @@ dist/ build/ __pycache__/ *.spec +Chrome/ +Data/ diff --git a/ExcuteStage/Readme.md b/ExcuteStage/Readme.md new file mode 100644 index 00000000..b584185f --- /dev/null +++ b/ExcuteStage/Readme.md @@ -0,0 +1,2 @@ +pyinstaller -F --icon=favicon.ico ServiceWrapper_ExcuteStage.py +出现permission denied请关闭杀毒软件 \ No newline at end of file diff --git a/ExcuteStage/ServiceWrapper_ExcuteStage.py b/ExcuteStage/ServiceWrapper_ExcuteStage.py index 0d049f82..33550bde 100644 --- a/ExcuteStage/ServiceWrapper_ExcuteStage.py +++ b/ExcuteStage/ServiceWrapper_ExcuteStage.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import atexit # 遇到错误退出时应执行的代码 import json import re import sys @@ -17,21 +18,32 @@ from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import TimeoutException from selenium.common.exceptions import StaleElementReferenceException +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities import random import numpy import csv import os +from selenium.webdriver.common.by import By + + +saveName, log, OUTPUT, browser, SAVED = None, "", "", None, False + +desired_capabilities = DesiredCapabilities.CHROME +desired_capabilities["pageLoadStrategy"] = "none" + class Time: - def __init__(self,type1=""): - self.t = int(round(time.time() * 1000)) + def __init__(self, type1=""): + self.t = int(round(time.time() * 1000)) self.type = type1 - + def end(self): at = int(round(time.time() * 1000)) Log(str(self.type)+":"+str(at-self.t)) # 记录log + + def recordLog(str=""): global log log = log + str + "\n" @@ -43,17 +55,48 @@ def Log(text, text2=""): if switch: print(text, text2) +# 屏幕滚动函数 + + +def scrollDown(para, rt=""): + try: + if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 + for i in range(para["scrollCount"]): + time.sleep(1) # 下拉完等1秒 + Log("下拉完等待1秒") + body = browser.find_element_by_css_selector("body") + if para["scrollType"] == 1: + body.send_keys(Keys.PGDN) + else: + body.send_keys(Keys.END) + except TimeoutException: + Log('time out after 10 seconds when scrolling. ') + recordLog('time out after 10 seconds when scrolling') + browser.execute_script('window.stop()') + if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 + for i in range(para["scrollCount"]): + time.sleep(1) # 下拉完等1秒 + Log("下拉完等待1秒") + body = browser.find_element_by_css_selector("body") + if para["scrollType"] == 1: + body.send_keys(Keys.PGDN) + else: + body.send_keys(Keys.END) + if rt != "": + rt.end() + # 执行节点关键函数部分 def excuteNode(nodeId, loopValue="", clickPath="", index=0): node = procedure[nodeId] WebDriverWait(browser, 10).until - (EC.visibility_of_element_located((By.XPATH, node["parameters"]["xpath"]))) # 等待元素出现才进行操作,10秒内未出现则报错 + # 等待元素出现才进行操作,10秒内未出现则报错 + (EC.visibility_of_element_located((By.XPATH, node["parameters"]["xpath"]))) # 根据不同选项执行不同操作 if node["option"] == 0 or node["option"] == 10: # root操作,条件分支操作 for i in node["sequence"]: # 从根节点开始向下读取 - excuteNode(i, loopValue,clickPath,index) + excuteNode(i, loopValue, clickPath, index) elif node["option"] == 1: # 打开网页操作 recordLog("openPage") openPage(node["parameters"], loopValue) @@ -67,10 +110,10 @@ def excuteNode(nodeId, loopValue="", clickPath="", index=0): inputInfo(node["parameters"], loopValue) elif node["option"] == 8: # 循环 recordLog("loop") - loopExcute(node, loopValue,clickPath,index) # 执行循环 + loopExcute(node, loopValue, clickPath, index) # 执行循环 elif node["option"] == 9: # 条件分支 recordLog("judge") - judgeExcute(node, loopValue,clickPath,index) + judgeExcute(node, loopValue, clickPath, index) # 执行完之后进行等待 if node["option"] != 0: @@ -82,7 +125,7 @@ def excuteNode(nodeId, loopValue="", clickPath="", index=0): # 对判断条件的处理 -def judgeExcute(node, loopElement,clickPath="",index=0): +def judgeExcute(node, loopElement, clickPath="", index=0): rt = Time("条件判断") global bodyText # 引入bodyText excuteBranchId = 0 # 要执行的BranchId @@ -101,7 +144,7 @@ def judgeExcute(node, loopElement,clickPath="",index=0): continue elif tType == 2: # 当前页面包含元素 try: - if browser.find_element_by_xpath(cnode["parameters"]["value"]): + if browser.find_element(By.XPATH, cnode["parameters"]["value"]): excuteBranchId = i break except: # 找不到元素或者xpath写错了,下一个条件 @@ -115,22 +158,23 @@ def judgeExcute(node, loopElement,clickPath="",index=0): continue elif tType == 4: # 当前循环元素包括元素 try: - if loopElement.find_element_by_xpath(cnode["parameters"]["value"][1:]): + if loopElement.find_element(By.XPATH, cnode["parameters"]["value"][1:]): excuteBranchId = i break except: # 找不到元素或者xpath写错了,下一个条件 continue rt.end() - excuteNode(excuteBranchId, loopElement,clickPath,index) + excuteNode(excuteBranchId, loopElement, clickPath, index) # 对循环的处理 -def loopExcute(node, loopValue,clickPath="",index=0): +def loopExcute(node, loopValue, clickPath="", index=0): time.sleep(0.1) # 第一次执行循环的时候强制等待1秒 Log("循环执行前等待0.1秒") global history thisHandle = browser.current_window_handle # 记录本次循环内的标签页的ID - thisHistoryLength = browser.execute_script('return history.length') # 记录本次循环内的history的length + thisHistoryLength = browser.execute_script( + 'return history.length') # 记录本次循环内的history的length history["index"] = thisHistoryLength history["handle"] = thisHandle @@ -139,9 +183,10 @@ def loopExcute(node, loopValue,clickPath="",index=0): count = 0 # 执行次数 while True: # do while循环 try: - element = browser.find_element_by_xpath(node["parameters"]["xpath"]) + element = browser.find_element(By.XPATH, + node["parameters"]["xpath"]) for i in node["sequence"]: # 挨个执行操作 - excuteNode(i, element, node["parameters"]["xpath"],0) + excuteNode(i, element, node["parameters"]["xpath"], 0) Log("click: ", node["parameters"]["xpath"]) recordLog("click:" + node["parameters"]["xpath"]) except NoSuchElementException: @@ -153,14 +198,17 @@ def loopExcute(node, loopValue,clickPath="",index=0): count = count + 1 Log("页数:", count) recordLog("页数:" + str(count)) + # print(node["parameters"]["exitCount"], "-------") if node["parameters"]["exitCount"] == count: # 如果达到设置的退出循环条件的话 break elif int(node["parameters"]["loopType"]) == 1: # 不固定元素列表 try: - elements = browser.find_elements_by_xpath(node["parameters"]["xpath"]) + elements = browser.find_elements(By.XPATH, + node["parameters"]["xpath"]) for index in range(len(elements)): for i in node["sequence"]: # 挨个执行操作 - excuteNode(i, elements[index], node["parameters"]["xpath"], index) + excuteNode(i, elements[index], + node["parameters"]["xpath"], index) if browser.current_window_handle != thisHandle: # 如果执行完一次循环之后标签页的位置发生了变化 while True: # 一直关闭窗口直到当前标签页 browser.close() # 关闭使用完的标签页 @@ -168,9 +216,11 @@ def loopExcute(node, loopValue,clickPath="",index=0): if browser.current_window_handle == thisHandle: break if history["index"] != thisHistoryLength and history[ - "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 - difference = thisHistoryLength - history["index"] # 计算历史记录变化差值 - browser.execute_script('history.go(' + str(difference) + ')') # 回退历史记录 + "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 + difference = thisHistoryLength - \ + history["index"] # 计算历史记录变化差值 + browser.execute_script( + 'history.go(' + str(difference) + ')') # 回退历史记录 if node["parameters"]["historyWait"] > 2: # 回退后要等待的时间 time.sleep(node["parameters"]["historyWait"]) else: @@ -186,7 +236,7 @@ def loopExcute(node, loopValue,clickPath="",index=0): elif int(node["parameters"]["loopType"]) == 2: # 固定元素列表 for path in node["parameters"]["pathList"].split("\n"): # 千万不要忘了分割!! try: - element = browser.find_element_by_xpath(path) + element = browser.find_element(By.XPATH, path) for i in node["sequence"]: # 挨个执行操作 excuteNode(i, element, path, 0) if browser.current_window_handle != thisHandle: # 如果执行完一次循环之后标签页的位置发生了变化 @@ -196,9 +246,11 @@ def loopExcute(node, loopValue,clickPath="",index=0): if browser.current_window_handle == thisHandle: break if history["index"] != thisHistoryLength and history[ - "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 - difference = thisHistoryLength - history["index"] # 计算历史记录变化差值 - browser.execute_script('history.go(' + str(difference) + ')') # 回退历史记录 + "handle"] == browser.current_window_handle: # 如果执行完一次循环之后历史记录发生了变化,注意当前页面的判断 + difference = thisHistoryLength - \ + history["index"] # 计算历史记录变化差值 + browser.execute_script( + 'history.go(' + str(difference) + ')') # 回退历史记录 if node["parameters"]["historyWait"] > 2: # 回退后要等待的时间 time.sleep(node["parameters"]["historyWait"]) else: @@ -216,16 +268,18 @@ def loopExcute(node, loopValue,clickPath="",index=0): for text in textList: recordLog("input: " + text) for i in node["sequence"]: # 挨个执行操作 - excuteNode(i, text, "",0) + excuteNode(i, text, "", 0) elif int(node["parameters"]["loopType"]) == 4: # 固定网址列表 pass # 以后再做 history["index"] = thisHistoryLength history["handle"] = browser.current_window_handle + scrollDown(node["parameters"]) # 打开网页事件 def openPage(para, loopValue): rt = Time("打开网页") + time.sleep(2) # 打开网页后强行等待至少2秒 global links global urlId global history @@ -248,18 +302,7 @@ def openPage(para, loopValue): browser.execute_script('window.stop()') history["index"] = browser.execute_script("return history.length") rt.end() - try: - if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 - for i in range(para["scrollCount"]): - time.sleep(1) # 下拉完等1秒 - Log("下拉等待1秒") - body = browser.find_element_by_css_selector("body") - body.send_keys(Keys.END) - except TimeoutException: - Log('time out after 10 seconds when loading page: ' + url) - recordLog('time out after 10 seconds when loading page: ' + url) - browser.execute_script('window.stop()') - rt.end() + scrollDown(para, rt) # 控制屏幕向下滚动 if containJudge: global bodyText # 每次执行点击,输入元素和打开网页操作后,需要更新bodyText try: @@ -285,7 +328,7 @@ def inputInfo(para, loopValue): Log("输入前等待1秒") rt = Time("输入文字") try: - textbox = browser.find_element_by_xpath(para["xpath"]) + textbox = browser.find_element(By.XPATH, para["xpath"]) except: Log("找不到输入框元素:" + para["xpath"] + "请尝试执行前等待") recordLog("找不到输入框元素:" + para["xpath"] + "请尝试执行前等待") @@ -313,7 +356,8 @@ def clickElement(para, loopElement=None, clickPath="", index=0): path = para["xpath"] # 不然使用元素定义的xpath tempHandleNum = len(browser.window_handles) # 记录之前的窗口位置 try: - script = 'var result = document.evaluate(`' + path + '`, document, null, XPathResult.ANY_TYPE, null);for(let i=0;i 0: # 控制屏幕向下滚动 - for i in range(para["scrollCount"]): - time.sleep(1) # 下拉完等1秒 - Log("下拉完等待1秒") - body = browser.find_element_by_css_selector("body") - body.send_keys(Keys.END) - except TimeoutException: - Log('time out after 10 seconds when scrolling. ') - recordLog('time out after 10 seconds when scrolling') - browser.execute_script('window.stop()') - if para["scrollType"] != 0 and para["scrollCount"] > 0: # 控制屏幕向下滚动 - for i in range(para["scrollCount"]): - time.sleep(1) # 下拉完等1秒 - Log("下拉完等待1秒") - body = browser.find_element_by_css_selector("body") - body.send_keys(Keys.END) - rt.end() + scrollDown(para, rt) # 根据参数配置向下滚动 if containJudge: # 有判断语句才执行以下操作 global bodyText # 每次执行点击,输入元素和打开网页操作后,需要更新bodyText try: @@ -393,13 +420,19 @@ def getData(para, loopElement, isInLoop=True): if p["relativeXpath"] == "": # 相对xpath有时候就是元素本身,不需要二次查找 element = loopElement else: - element = loopElement.find_element_by_xpath(p["relativeXpath"][1:]) + element = loopElement.find_element(By.XPATH, + p["relativeXpath"][1:]) else: - element = browser.find_element_by_xpath(p["relativeXpath"]) + element = browser.find_element(By.XPATH, p["relativeXpath"]) except NoSuchElementException: # 找不到元素的时候,使用默认值 - outputParameters[p["name"]] = p["default"] - Log('Element not found,use default') - recordLog('Element not found,use default') + # print(p) + try: + content = p["default"] + except Exception as e: + content = "" + outputParameters[p["name"]] = content + Log('Element %s not found,use default' % p["relativeXpath"]) + recordLog('Element %s not found, use default' % p["relativeXpath"]) continue except TimeoutException: # 超时的时候设置超时值 Log('time out after 10 seconds when getting data') @@ -409,9 +442,10 @@ def getData(para, loopElement, isInLoop=True): if p["relativeXpath"] == "": # 相对xpath有时候就是元素本身,不需要二次查找 element = loopElement else: - element = loopElement.find_element_by_xpath(p["relativeXpath"][1:]) + element = loopElement.find_element(By.XPATH, + p["relativeXpath"][1:]) else: - element = browser.find_element_by_xpath(p["relativeXpath"]) + element = browser.find_element(By.XPATH, p["relativeXpath"]) rt.end() try: if p["contentType"] == 2: @@ -428,7 +462,8 @@ def getData(para, loopElement, isInLoop=True): }\ var str = arr.join(" "); \ return str;' - content = browser.execute_script(command, element).replace("\n", "").replace("\\s+", " ") + content = browser.execute_script(command, element).replace( + "\n", "").replace("\\s+", " ") if p["nodeType"] == 2: if element.get_attribute("href") != None: content = element.get_attribute("href") @@ -461,19 +496,22 @@ def getData(para, loopElement, isInLoop=True): content = element.get_attribute("src") else: content = "" - except StaleElementReferenceException: #发生找不到元素的异常后,等待几秒重新查找 + except StaleElementReferenceException: # 发生找不到元素的异常后,等待几秒重新查找 recordLog('StaleElementReferenceException:'+p["relativeXpath"]) - time.sleep(3) + time.sleep(3) try: if p["relative"]: # 是否相对xpath if p["relativeXpath"] == "": # 相对xpath有时候就是元素本身,不需要二次查找 element = loopElement recordLog('StaleElementReferenceException:loopElement') else: - element = loopElement.find_element_by_xpath(p["relativeXpath"][1:]) - recordLog('StaleElementReferenceException:loopElement+relativeXPath') + element = loopElement.find_element(By.XPATH, + p["relativeXpath"][1:]) + recordLog( + 'StaleElementReferenceException:loopElement+relativeXPath') else: - element = browser.find_element_by_xpath(p["relativeXpath"]) + element = browser.find_element( + By.XPATH, p["relativeXpath"]) recordLog('StaleElementReferenceException:relativeXpath') if p["contentType"] == 2: content = element.get_attribute('innerHTML') @@ -489,7 +527,8 @@ def getData(para, loopElement, isInLoop=True): }\ var str = arr.join(" "); \ return str;' - content = browser.execute_script(command, element).replace("\n", "").replace("\\s+", " ") + content = browser.execute_script(command, element).replace( + "\n", "").replace("\\s+", " ") if p["nodeType"] == 2: if element.get_attribute("href") != None: content = element.get_attribute("href") @@ -524,7 +563,7 @@ def getData(para, loopElement, isInLoop=True): content = "" except StaleElementReferenceException: recordLog('StaleElementReferenceException:'+p["relativeXpath"]) - continue # 再出现类似问题直接跳过 + continue # 再出现类似问题直接跳过 outputParameters[p["name"]] = content global OUTPUT line = [] @@ -541,13 +580,12 @@ def isnull(s): return len(s) != 0 -import atexit #遇到错误退出时应执行的代码 -@atexit.register -def clean(): - global saveName,log, OUTPUT,browser,SAVED +@atexit.register +def clean(): + global saveName, log, OUTPUT, browser, SAVED if not SAVED: print('清理环境保存数据') - with open("Data/"+saveName + '_log.txt', 'w',encoding='utf-8-sig') as file_obj: + with open("Data/"+saveName + '_log.txt', 'w', encoding='utf-8-sig') as file_obj: file_obj.write(log) file_obj.close() with open("Data/"+saveName + '.csv', 'w', encoding='utf-8-sig', newline="") as f: @@ -557,42 +595,53 @@ def clean(): f.close() browser.quit() + if __name__ == '__main__': options = Options() + exe_path = "chromedriver.exe" if os.path.exists(os.getcwd()+"/ServiceWrapper"): - options.binary_location="ServiceWrapper/Chrome/chrome.exe" #指定chrome位置 + options.binary_location = "ServiceWrapper/Chrome/chrome.exe" # 指定chrome位置 + exe_path = "ServiceWrapper/Chrome/chromedriver.exe" elif os.path.exists(os.getcwd()+"/Debug"): - options.binary_location="Debug/Chrome/chrome.exe" #指定chrome位置 - elif os.getcwd().find("ExcuteStage") >= 0: #如果直接执行 - options.binary_location = "../../bin/x64/Debug/Chrome/chrome.exe" # 指定chrome位置 + options.binary_location = "Debug/Chrome/chrome.exe" # 指定chrome位置 + exe_path = "Debug/Chrome/chromedriver.exe" + elif os.getcwd().find("ExcuteStage") >= 0: # 如果直接执行 + options.binary_location = "./Chrome/chrome.exe" # 指定chrome位置 + exe_path = "./Chrome/chromedriver.exe" else: - options.binary_location="chrome.exe" #指定chrome位置 - browser = webdriver.Chrome(chrome_options=options) + options.binary_location = "chrome.exe" # 指定chrome位置 + browser = webdriver.Chrome(options=options, executable_path=exe_path) browser.get('about:blank') - browser.set_page_load_timeout(10) # 加载页面最大超时时间 - if len(sys.argv)>1: - id = int(sys.argv[1]) #taskId这里修改 + browser.set_page_load_timeout(10) # 加载页面最大超时时间 + browser.set_script_timeout(10) + if len(sys.argv) > 1: + id = int(sys.argv[1]) # taskId这里修改 + else: + id = 7 # 设置默认值 + print("id:", id) + if len(sys.argv) > 2: + backEndAddress = sys.argv[2] else: - id = 7 #设置默认值 - print("id:",id) - if len(sys.argv)>2: - saveName = "task_" + str(id) + "_" + sys.argv[2] # 保存文件的名字 + backEndAddress = "http://servicewrapper.naibo.wang" + if len(sys.argv) > 3: + saveName = "task_" + str(id) + "_" + sys.argv[3] # 保存文件的名字 else: - saveName = "task_" + str(id) + "_" + str(random.randint(0, 999999999)) # 保存文件的名字 - content = requests.get("http://183.129.170.180:8041/backEnd/queryTask?id=" + str(id)) + saveName = "task_" + str(id) + "_" + \ + str(random.randint(0, 999999999)) # 保存文件的名字 + content = requests.get(backEndAddress + "/backEnd/queryTask?id=" + str(id)) service = json.loads(content.text) # 加载服务信息 - print("name:",service["name"]) + print("name:", service["name"]) procedure = service["graph"] # 程序执行流程 links = list(filter(isnull, service["links"].split("\n"))) # 要执行的link的列表 OUTPUT = [] # 采集的数据 OUTPUT.append([]) # 添加表头 - containJudge = service["containJudge"] #是否含有判断语句 + containJudge = service["containJudge"] # 是否含有判断语句 bodyText = "" # 记录bodyText tOut = service["outputParameters"] # 生成输出参数对象 outputParameters = {} log = "" # 记下现在总共开了多少个标签页 - history = {"index":0,"handle":None} #记录页面现在所以在的历史记录的位置 - SAVED=False #记录是否已经存储了 + history = {"index": 0, "handle": None} # 记录页面现在所以在的历史记录的位置 + SAVED = False # 记录是否已经存储了 for para in tOut: outputParameters[para["name"]] = "" OUTPUT[0].append(para["name"]) @@ -604,7 +653,7 @@ def clean(): print("执行完成!") recordLog("Done!") # dataPath = os.path.abspath(os.path.join(os.getcwd(), "../Data")) - with open("Data/"+saveName + '_log.txt', 'w',encoding='utf-8-sig') as file_obj: + with open("Data/"+saveName + '_log.txt', 'w', encoding='utf-8-sig') as file_obj: file_obj.write(log) file_obj.close() with open("Data/"+saveName + '.csv', 'w', encoding='utf-8-sig', newline="") as f: @@ -614,4 +663,4 @@ def clean(): f.close() SAVED = True browser.quit() - sys.exit(0) \ No newline at end of file + sys.exit(0) diff --git a/Extension/ServiceWrapper.crx b/Extension/ServiceWrapper.crx index eaa6b45c41e739bebb6425cb9b07dcbc0e7a4221..1e4523abac90e8119dd05de9bd21a6ebc4d913b9 100644 GIT binary patch delta 14786 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 QbX;mkMreCN$iKS(2Umm#vH$=8 diff --git a/Extension/ServiceWrapper.pem b/Extension/ServiceWrapper.pem index 8584fd97..484e41b2 100644 --- a/Extension/ServiceWrapper.pem +++ b/Extension/ServiceWrapper.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCuAZmXoS3RN1dQ -IWpPjMZMX08JQRiR9XF8fPuZ4r/aauafNKncQmC2FEFylAcBMaRVSjAsZlXVqTlE -s1LRtt9PiJEiDv73alerwznzNfuN0Dqrj2YpvKlck/sdFDeJNGi3dAl4kWZOKlE0 -kpHXO6BQBKB9t9Up5qBku0epGYNZ6/foCl82U5bw8bBaZ5p0F2hhE4/PWP0p/MED -HR7yvnr5qO0B6qQia/59q4opxH6sswa6XAc3mgU1Yq8m1cXRctmvxCBt7biU7Gpw -jLB4Qdgikq02hWxn1YUbGAjHRjLrNxOIFBP6Sx2SNllM664bUmzwchnEzsLILyDQ -jzKlafobAgMBAAECggEAGdp7APXIYkbKs56DYSvAECYzuWLxTPPJgimVExmG+o4v -kzAL8iyamfMLK676wMlTaqi0h6RHfztpcDUEn8wqrx0naIa53fZFmGulTJGUvU6s -U5k2y19l+oSyJsaTidCTo85F1KLQhISdFCaYLKvbTIhOIDUUIumf+IIq9OD+4c6C -nhAKhkjspDt1ctyFy9QXE5Wbi6o8AdOuaw9QW+y7msi1kcTWovxFBQkN1Ck4D2m7 -y72M5dDYq3nWEyQDJvioDM2yxvywiX3piHjULBj60Ybt52EEyRNlBMkbafU0puYU -aajlPSv0X0mQjdtYLmd2dEVTH5rNLkVRwtx0i5MYoQKBgQDnJkJY6sj6HzhnFtWo -NiorwAteNG6fIchZPAJjdtAQNu2stakGtnRoQ3WfVi2ePUKntoyqS4WanJE/z4pX -lTMAePB8madwqe+AgSJzl0+YnOqBQCk6DeIrJ9u0O+nb7wlkZMbaULj6Sx8bg+N4 -jM/MSD4uo1eYJdo6bIq6KNHXTQKBgQDAtqJlZ+EweMD9K652htwW0HE0IXD7zxMh -8rODaubAp+glapW77GYltFrJ++Q8ObBdm+jLo8CbWUTus61qpsinv+CV//dIEiYp -vbdf+oFnCEHvFTN1opzsS9ThYZhRnnonuxYmMCWsx55GG7oyAVtYLBUERm6XurLY -FXkceAvzBwKBgD/UB8Qn6SFwV5HX813EvzZfjIQR9G4K1RkXAg3XNDMWB+GGNEt/ -PHvCDQvXrzcf8XUAaq1nt1fvXgiB1dDDiVbbDoVJpLvuoQ0aG5pRsRASXIseXYQD -a23BTuDhHn217yEC1wpX+gxbjBZ7/+c88vCVDl6wijKgWTeC23f2Z6ONAoGAP2CQ -3cqg+2DrDxe7g41sejBI2n0Y/CcowqRftxuEEd3mcc+wuKHRIhtDNGQbtla+krqA -f+A1qqFcEyiSIp4BJXHr+Ui52UDmvhl/YhgvUGQd0vPk/Y3iD7AMraZ8AnOc9s4H -Rb3V1hG8EpBx1potoTy2GLbVDh8/S+Mb9mngfCcCgYEAlL/oEGR6j7K/29gF8KjQ -J6HfNxdisP0b14her/mX0NSSv8WNFX77XO6pjXmpiIQPsm4I1KlzeFEVBy9RetGb -5OKLQdfILNe8aDrYxMKcjbuA9zLqIzYep8siLdrYI1RDSxKt7ZUW6JejPthV0vr5 -408+uF8kwRJscoRPn7+x7Uk= +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCr3o0YTuz2UMy7 +qolwd4CWXet3SjJL0XawKKgp3ZRo78zER1Z/LQ5/rZqc6DISBPCycAoOCBk2EcBZ +IlPKQFxq1PMj10ccyswjjb+4mf4ZRVveeflBqT9I4khrzwk9gymSZiD7TbkDc4fp +oxoHCv2f7VMR5m/IwsnXEsm7QyuRSA6ETbhp2Faa2CIGI4l6r+gtuVYkUSGEN/0H +0dk3kfh6rcjwvroLSzWcGJnhyOLDYxnV1kTHZDUBzGj0UOqL0fdpAuyXxSikadxa +Ec4BZ4s3GHIlFR5N7itrwYN28IInLObxUQXJ3unEdnon/T/XhC5KsdjMp8D6Xb8/ +0dObgaG/AgMBAAECggEAR0xyZQCs/fwECuBS1uTEY9y/h4OwvNI+9hJPvNpurF+6 +pzxe7jBPYPkweGI9D0ucXLHtAegrKUqQ7Ik4kGFF1Y3U9evjVNbue9tciyTbDDnB +RFh+ZlZjagVxfMPtNeb5MoIKsaYLGBrv6aUfcYeGMre96+GYQwVHvWDOblCNvN0k +fBInSGlDcLkx9EJ9vFbCf8IErg4RWtiEdSvSG1s99IPc17j4cJn4qq9OyQrECpdz +y7AnkvsMOiOzQawO+C2I5SgA26eaxNJrz/3ph4r1x1iCdPvY7iD/awkQXCgid6kq +Z0R8ByxTbwqr4NYXtUyHVkwiL9QZSKjnvrOfdmm0iQKBgQDkQ5KRMftmDi9cJfOj +0xY/TwCymsWp+MdMQ6YKAd57oMM+2DzgZ//LFgo4rcYWjTGlErE8tXhT4fM5PEUQ +1CgwozmkPcVnRzLyiNWNpiMCzPVPsXDxzoEQFHL0qLA/Xj7npH5d+Eg6yunDc9JS +D/OlqSEZQ6W4lWFvAkz7Mh5M6QKBgQDAwMFzAMYVXwcRmH0gvzmqA46MqjdfaXyq +NzNADiVCaGgqYpNygeftiHGCBQiXr3QkVVp+XihbY/5KnzVYiS0ICKMCwqxizvRT +gR3N4+UyVaK9FTWLgtSu8VN4JM3E+nigBO/QfD4WI4VZ3tZEsOveV70j97NpfaPN +lhjr2J0wZwKBgG3pj7i0bY5dxDZF3ASFjw/a9cYKuqU2spipdlkZP00eQwWOz/lq +eoQKz88s9dZEFKSc1JUb+J3Djf+AYu1Qiy2oWwgX6mbppMGeW95CIlel43WFRpJY +0lKw1g2y8HMC6Z1W2rZa6ETPEjLYIWz8W2DoiJSGu1SfMXRnkjPelTKJAoGACvbs +Ci6xFeYh8M5Lz+EQ1qr9IONN4w+NF+Gr+KjVVcG6qy6QVKMvHkP0sQC4TGieorJ4 +Q1f307sMbBJCZpbnCN305+NLCxPasiVWHLAqCYL1juv178mxb4IqzVrKmbnlwrSF +L8bhgUDkBQi4B5BI2o0DJVihzA5pkvhG0qOvzWECgYAHITSiOJBF1Lr0hw7JnS5r +Hre8bQdbDeqSd5iRM/+0MJnBvkp7RDgvh0c01Wc90A0gjhhPqBS9z5UNptiJKhJQ +kYDP7iiftM7FsG45IGKf6RrulLk6pCyFEFb/roBlC1xy/6uNCqAHbd+6K6UjQAFB ++c9hCtSDDh3/irZrY34lDw== -----END PRIVATE KEY----- diff --git a/Extension/ServiceWrapper/scripts/baozhuangscript.js b/Extension/ServiceWrapper/scripts/baozhuangscript.js index d674a2e6..f2105e0c 100644 --- a/Extension/ServiceWrapper/scripts/baozhuangscript.js +++ b/Extension/ServiceWrapper/scripts/baozhuangscript.js @@ -1,6 +1,6 @@ //表现层的处理 -if (window.location.href.indexOf("183.129.170.180") >= 0) { +if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) { throw "serviceGrid"; //如果是服务器网页页面,则不执行工具 } diff --git a/Extension/ServiceWrapper/scripts/contentscript.js b/Extension/ServiceWrapper/scripts/contentscript.js index abc3321e..78a14a6d 100644 --- a/Extension/ServiceWrapper/scripts/contentscript.js +++ b/Extension/ServiceWrapper/scripts/contentscript.js @@ -1,50 +1,50 @@ //表现逻辑层的处理 -if (window.location.href.indexOf("183.129.170.180") >= 0) { +if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) { throw "serviceGrid"; //如果是服务器网页页面,则不执行工具 } -//Vue元素 +//Vueelement var app; generateToolkit(); //生成Toolkit function generateToolkit() { $(".tooltips").html(`
-
✍操作提示框(可点此拖动)
+
✍Operation Toolbox (Can drag)
-

特殊点选模式

+

Special click mode

-

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

-

● 如果不小心左键点选了元素导致页面跳转,直接后退或者切换回标签页即可。

+

● When your mouse moves to the element, please use yourright mouse buttonorF7 on the keyboard to select it.

+

● You can click the back button to go back to the page

{{initial()}}
- ● 已选中{{numOfList()}}个{{tname()}},同时发现{{numOfReady()}}个同类元素,您可以: + ● Already selected {{numOfList()}} {{tname()}},, meanwhile we find {{numOfReady()}} element with the same type, you can:
@@ -53,21 +53,21 @@ function generateToolkit() {
- ● 已选择了以下元素,您可以: + ● 已select了以下element,您可以:
- ● 已选择了{{numOfList()}}个同类元素,另外发现{{numOfReady()}}个同类元素,您可以: + ● 已select了{{numOfList()}}个同类element,另外发现{{numOfReady()}}个同类element,您可以:
@@ -91,11 +91,11 @@ function generateToolkit() {
-
{{setWidth("230px")}}
+
{{setWidth("230px")}}
- +

{{lastElementXPath()}}

@@ -116,14 +116,14 @@ function generateToolkit() { data: { option: 0, list: { nl: nodeList, opp: outputParameters }, - valTable: [], // 用来存储转换后的参数列表 - special: false, //是否为特殊选择模式 - selectedDescendents: false, // 标记是否选中了子元素 - selectStatus: false, //标记单个元素是否点击了采集 + valTable: [], // 用来存储转换后的para列表 + special: false, //是否为特殊selection模式 + selectedDescendents: false, // 标记是否选中了子element + selectStatus: false, //标记单个element是否点击了采集 page: 0, //默认页面,1为输入文字页面 text: "", // 记录输入的文字 tNodeName: "", // 记录临时节点列表 - nowPath: "", //现在元素的xpath + nowPath: "", //现在element的xpath }, watch: { nowPath: { //变量发生变化的时候进行一些操作 @@ -133,34 +133,34 @@ function generateToolkit() { } }, methods: { - initial: function() { //每当元素是0的时候,执行值的初始化操作 + initial: function() { //每当element是0的时候,执行值的初始化操作 this.selectedDescendents = false; this.selectStatus = false; this.nowPath = ""; }, - confirmCollectSingle: function() { //单元素确认采集 + confirmCollectSingle: function() { //单element确认采集 collectSingle(); clearEl(); }, - confirmCollectMulti: function() { //无规律多元素确认采集 + confirmCollectMulti: function() { //无规律多element确认采集 collectMultiNoPattern(); clearEl(); }, - confirmCollectMultiAndDescendents: function() { //有规律多元素确认采集 + confirmCollectMultiAndDescendents: function() { //有规律多element确认采集 collectMultiWithPattern(); clearEl(); }, - deleteSingleLine: function(event) { //删除单行元素 + deleteSingleLine: function(event) { //删除单行element let at = new Date().getTime() - //流程图送元素的时候,默认的使用不固定循环列表,但是一旦有删除元素的操作发生,则按照固定元素列表采集元素 + //流程图送element的时候,默认的使用不固定循环列表,但是一旦有删除element的操作发生,则按照固定element列表采集element index = event.target.getAttribute("index"); - let tnode = nodeList.splice(index, 1)[0]; //删掉当前元素 + let tnode = nodeList.splice(index, 1)[0]; //删掉当前element tnode["node"].style.backgroundColor = tnode["bgColor"]; tnode["node"].style.boxShadow = tnode["boxShadow"]; if (nodeList.length > 1) { // 如果删到没有就没有其他的操作了 handleElement(); if (this.selectedDescendents) { - handleDescendents(); //如果之前有选中子元素,新加入的节点又则这里也需要重新选择子元素 + handleDescendents(); //如果之前有选中子element,新加入的节点又则这里也需要重新selection子element } } else { this.valTable = []; @@ -170,25 +170,25 @@ function generateToolkit() { let at2 = parseInt(new Date().getTime()); console.log("delete:", at2, at, at2 - at); }, - clickElement: function() { //点击元素操作 + clickElement: function() { //点击element操作 sendSingleClick(); //先发送数据 - nodeList[0]["node"].focus(); //获得元素焦点 - nodeList[0]["node"].click(); //点击元素 + nodeList[0]["node"].focus(); //获得element焦点 + nodeList[0]["node"].click(); //点击element clearEl(); }, - loopClickSingleElement: function() { //循环点击单个元素 - sendLoopClickSingle(this.tname()); //识别下一页,循环点击单个元素和点击多个元素 - if (this.tname() != "下一页元素") { //下一页元素不进行点击操作 - nodeList[0]["node"].focus(); //获得元素焦点 - nodeList[0]["node"].click(); //点击元素 + loopClickSingleElement: function() { //循环点击单个element + sendLoopClickSingle(this.tname()); //识别下一页,循环点击单个element和点击多个element + if (this.tname() != "Elements in next page") { //Elements in next page不进行点击操作 + nodeList[0]["node"].focus(); //获得element焦点 + nodeList[0]["node"].click(); //点击element } clearEl(); }, - loopClickEveryElement: function() { //循环点击每个元素 - sendLoopClickEvery(); //识别下一页,循环点击单个元素和点击多个元素 - nodeList[0]["node"].focus(); //获得元素焦点 - nodeList[0]["node"].click(); //点击元素 + loopClickEveryElement: function() { //循环点击每个element + sendLoopClickEvery(); //识别下一页,循环点击单个element和点击多个element + nodeList[0]["node"].focus(); //获得element焦点 + nodeList[0]["node"].click(); //点击element clearEl(); }, setInput: function() { //输入文字 @@ -199,7 +199,7 @@ function generateToolkit() { }, getInput: function() { //得到输入的文字 nodeList[0]["node"].focus(); //获得文字焦点 - // nodeList[0]["node"].setAttribute("value", this.text); // 设置输入框内容 + // nodeList[0]["node"].setAttribute("value", this.text); // 设置输入 box内容 input(this.text); // 设置输入 this.text = ""; clearEl(); @@ -207,7 +207,7 @@ function generateToolkit() { cancelInput: function() { this.page = 0; }, - setWidth: function(width) { //根据是否出现表格调整最外框宽度 + setWidth: function(width) { //根据是否出现表格调整最外 box宽度 $(".tooltips").css("width", width); return ""; }, @@ -216,7 +216,7 @@ function generateToolkit() { this.selectStatus = true; clearReady(); }, - getLink: function() { //采集链接地址 + getLink: function() { //采集linkAddress generateParameters(0, false, true); this.selectStatus = true; clearReady(); @@ -243,22 +243,22 @@ function generateToolkit() { return "null"; } else if ($(nodeList[0]["node"]).contents().filter(function() { return this.nodeType === 3; }).text().indexOf("下一页") >= 0) { this.setWidth("250px"); - return "下一页元素"; + return "Elements in next page"; } else if (tag == "A") { - return "链接"; + return "link"; } else if (tag == "IMG") { - return "图片"; + return "Image"; } else if (tag == "BUTTON" || (tag == "INPUT" && (inputType == "button" || inputType == "submit"))) { - return "按钮"; - } else if (tag == "TEXTAREA" || (tag == "INPUT" && (inputType != "checkbox" || inputType != "ratio"))) { //普通输入框 - return "文本框"; + return "Button"; + } else if (tag == "TEXTAREA" || (tag == "INPUT" && (inputType != "checkbox" || inputType != "ratio"))) { //普通输入 box + return "text box"; } else if (tag == "SELECT") { - return "选择框"; + return "selection box"; } else { - return "元素"; + return "element"; } }, - existDescendents: function() { //检测选中的元素是否存在子元素,已经选中了子元素也不要再出现了 + existDescendents: function() { //检测选中的element是否存在子element,已经选中了子element也不要再出现了 return nodeList.length > 0 && nodeList[0]["node"].children.length > 0 && !this.selectedDescendents; }, numOfReady: function() { @@ -267,11 +267,11 @@ function generateToolkit() { numOfList: function() { return nodeList.length; }, - lastElementXPath: function() { //用来显示元素的最大最后5个xpath路劲元素 + lastElementXPath: function() { //用来显示element的最大最后5个xpath路劲element path = nodeList[nodeList.length - 1]["xpath"]; path = path.split("/"); tp = ""; - if (path.length > 5) { //只保留最后五个元素 + if (path.length > 5) { //只保留最后五个element path = path.splice(path.length - 5, 5); tp = ".../" } @@ -285,7 +285,7 @@ function generateToolkit() { cancel: function() { clearEl(); }, - specialSelect: function() { //特殊选择模式 + specialSelect: function() { //特殊selection模式 if (mousemovebind) { tdiv.style.pointerEvents = "none"; this.special = false; @@ -294,10 +294,10 @@ function generateToolkit() { } mousemovebind = !mousemovebind; }, - enlarge: function() { // 扩大选区功能,总是扩大最后一个选中的元素的选区 + enlarge: function() { // 扩大选区功能,总是扩大最后一个选中的element的选区 if (nodeList[nodeList.length - 1]["node"].tagName != "BODY") { - nodeList[nodeList.length - 1]["node"].style.backgroundColor = nodeList[nodeList.length - 1]["bgColor"]; //之前元素恢复原来的背景颜色 - nodeList[nodeList.length - 1]["node"].style.boxShadow = nodeList[nodeList.length - 1]["boxShadow"]; //之前元素恢复原来的背景颜色 + nodeList[nodeList.length - 1]["node"].style.backgroundColor = nodeList[nodeList.length - 1]["bgColor"]; //之前element恢复原来的背景颜色 + nodeList[nodeList.length - 1]["node"].style.boxShadow = nodeList[nodeList.length - 1]["boxShadow"]; //之前element恢复原来的背景颜色 tNode = nodeList[nodeList.length - 1]["node"].parentNode; //向上走一层 if (tNode != NowNode) { //扩大选区之后背景颜色的判断,当前正好选中的颜色应该是不同的 sty = tNode.style.backgroundColor; @@ -307,7 +307,7 @@ function generateToolkit() { nodeList[nodeList.length - 1]["node"] = tNode; nodeList[nodeList.length - 1]["bgColor"] = sty; nodeList[nodeList.length - 1]["xpath"] = readXPath(tNode, 1); - //显示框 + //显示 box var pos = tNode.getBoundingClientRect(); div.style.display = "block"; div.style.height = tNode.offsetHeight + "px"; @@ -316,39 +316,39 @@ function generateToolkit() { div.style.top = pos.top + "px"; div.style.zIndex = 2147483645; div.style.pointerEvents = "none"; - handleElement(); //每次数组元素有变动,都需要重新处理下 + handleElement(); //每次数组element有变动,都需要重新处理下 oe = tNode; tNode.style.backgroundColor = "rgba(0,191,255,0.5)"; this.selectedDescendents = false; } }, - selectAll: function() { //选中全部元素 + selectAll: function() { //选中全部element step++; readyToList(step, false); handleElement(); if (this.selectedDescendents) { - handleDescendents(); //如果之前有选中子元素,新加入的节点又则这里也需要重新选择子元素 + handleDescendents(); //如果之前有选中子element,新加入的节点又则这里也需要重新selection子element } }, - revoke: function() { //撤销选择当前节点 + revoke: function() { //撤销selection当前节点 var tstep = step; step--; //步数-1 - while (tstep == nodeList[nodeList.length - 1]["step"]) //删掉所有当前步数的元素节点 + while (tstep == nodeList[nodeList.length - 1]["step"]) //删掉所有当前步数的element节点 { let node = nodeList.splice(nodeList.length - 1, 1)[0]; //删除数组最后一项 - node["node"].style.backgroundColor = node["bgColor"]; //还原原始属性和边框 + node["node"].style.backgroundColor = node["bgColor"]; //还原原始属性和边 box node["node"].style.boxShadow = node["boxShadow"]; if (NowNode == node["node"]) { style = node["bgColor"]; } - //处理已经有选中子元素的情况 + //处理已经有选中子element的情况 // if (this.selectedDescendents) { clearParameters(); //直接撤销重选 // } } - handleElement(); //每次数组元素有变动,都需要重新处理下 + handleElement(); //每次数组element有变动,都需要重新处理下 }, - selectDescendents: function() { //选择所有子元素操作 + selectDescendents: function() { //selection所有子element操作 handleDescendents(); } }, @@ -358,7 +358,7 @@ function generateToolkit() { if (difference > 0) { $(".tooldrag").css("cssText", "height:" + (26 + difference) + "px!important") } - timer = setInterval(function() { //时刻监测相应元素是否存在(防止出现如百度一样元素消失重写body的情况),如果不存在,添加进来 + timer = setInterval(function() { //时刻监测相应element是否存在(防止出现如百度一样element消失重写body的情况),如果不存在,添加进来 if (document.body != null && document.getElementById("wrapperToolkit") == null) { this.clearInterval(); //先取消原来的计时器,再设置新的计时器 document.body.append(div); //默认如果toolkit不存在则div和tdiv也不存在 @@ -381,10 +381,10 @@ function generateToolkit() { }, 3000); } -//每次对元素进行增删之后需要执行的操作 +//每次对element进行增删之后需要执行的操作 function handleElement() { - clearReady(); //预备元素每次处理都先处理掉 - if (nodeList.length > 1) { //选中了许多元素的情况 + clearReady(); //预备element每次处理都先处理掉 + if (nodeList.length > 1) { //选中了许多element的情况 app._data.option = relatedTest(); if (app._data.option == 100) { generateMultiParameters(); @@ -392,13 +392,13 @@ function handleElement() { generateParameters(0); } } else if (nodeList.length == 1) { - findRelated(); //寻找和元素相关的元素 + findRelated(); //寻找和element相关的element } } -function clearParameters(deal = true) //清空参数列表 +function clearParameters(deal = true) //清空para列表 { - if (deal) //是否取消对选中的子元素进行处理 + if (deal) //是否取消对选中的子element进行处理 { app._data.selectedDescendents = false; } @@ -406,20 +406,20 @@ function clearParameters(deal = true) //清空参数列表 o["node"].style.boxShadow = o["boxShadow"]; } outputParameterNodes.splice(0); - outputParameters.splice(0); //清空原来的参数列表 + outputParameters.splice(0); //清空原来的para列表 app._data.valTable = []; //清空展现数组 app._data.selectStatus = false; } -//根据nodelist列表内的元素生成参数列表 -//适合:nodelist中的元素为同类型元素 -//type:0为全部文本 1为节点内直接的文字 2为innerhtml 3为outerhtml +//根据nodelist列表内的element生成para列表 +//适合:nodelist中的element为同类型element +//type:0为全部text 1为节点内直接的文字 2为innerhtml 3为outerhtml //nodetype:0,对应全type0123 -//nodetype:1 链接,对应type0123 -//nodetype:2 链接地址 对应type0 -//nodetype:3 按钮和输入文本框 对应type -//nodetype:4 按钮和输入文本框 对应type +//nodetype:1 link,对应type0123 +//nodetype:2 linkAddress 对应type0 +//nodetype:3 按钮和输入text box 对应type +//nodetype:4 按钮和输入text box 对应type function generateParameters(type, linktext = true, linkhref = true) { clearParameters(false); @@ -432,23 +432,23 @@ function generateParameters(type, linktext = true, linkhref = true) { ndPath = nodeList[num]["xpath"]; outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow }); nd.style.boxShadow = boxShadowColor; - let pname = "文本"; + let pname = "text"; let ndText = ""; if (type == 0) { ndText = $(nd).text(); - pname = "文本"; + pname = "text"; if (nd.tagName == "IMG") { ndText = nd.getAttribute("src") == null ? "" : $(nd).prop("src"); - pname = "地址"; + pname = "Address"; } else if (nd.tagName == "INPUT") { ndText = nd.getAttribute("value") == null ? "" : nd.getAttribute("value"); } } else if (type == 1) { ndText = $(nd).contents().filter(function() { return this.nodeType === 3; }).text().replace(/\s+/g, ''); - pname = "文本"; + pname = "text"; if (nd.tagName == "IMG") { ndText = nd.getAttribute("src") == null ? "" : $(nd).prop("src"); - pname = "地址"; + pname = "Address"; } else if (nd.tagName == "INPUT") { ndText = nd.getAttribute("value") == null ? "" : nd.getAttribute("value"); } @@ -460,24 +460,24 @@ function generateParameters(type, linktext = true, linkhref = true) { pname = "outerHTML"; } if (num == 0) { //第一个节点新建,后面的增加即可 - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters.push({ "nodeType": 4, //节点类型 "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_图片" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_Image" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link if (linktext) { outputParameters.push({ "nodeType": 1, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_link" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); @@ -487,19 +487,19 @@ function generateParameters(type, linktext = true, linkhref = true) { "nodeType": 2, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linkAddress", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }] }); } - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters.push({ "nodeType": 3, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); @@ -508,19 +508,19 @@ function generateParameters(type, linktext = true, linkhref = true) { "nodeType": 0, "contentType": type, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_" + pname, - "desc": "", //参数描述 + "name": "para" + (n++) + "_" + pname, + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? "" : ndPath, "exampleValues": [{ "num": num, "value": ndText }] }); } - } else { //如果元素节点已经存在,则只需要插入值就可以了 - if (nd.tagName == "IMG") { //如果元素是图片 + } else { //如果element节点已经存在,则只需要插入值就可以了 + if (nd.tagName == "IMG") { //如果element是Image outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); outputParameters[1]["exampleValues"].push({ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); } else { //其他所有情况 outputParameters[0]["exampleValues"].push({ "num": num, "value": ndText }); @@ -536,8 +536,8 @@ function generateParameters(type, linktext = true, linkhref = true) { } -//根据nodelist列表内的元素生成参数列表 -//适合:nodelist中的元素为不同类型元素 +//根据nodelist列表内的element生成para列表 +//适合:nodelist中的element为不同类型element function generateMultiParameters() { clearParameters(false); let n = 1; @@ -550,23 +550,23 @@ function generateMultiParameters() { outputParameterNodes.push({ "node": nd, "boxShadow": nd.style.boxShadow == "" || boxShadowColor ? "none" : nd.style.boxShadow }); nd.style.boxShadow = boxShadowColor; ndText = $(nd).text(); - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters.push({ "nodeType": 4, //节点类型 "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_图片地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_imageAddress", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }] }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters.push({ "nodeType": 1, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linktext", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": ndText }] }); @@ -574,18 +574,18 @@ function generateMultiParameters() { "nodeType": 2, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linkAddress", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }] }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters.push({ "nodeType": 3, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") }] }); @@ -594,8 +594,8 @@ function generateMultiParameters() { "nodeType": 0, "contentType": 0, // 内容类型 "relative": false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": ndPath, "exampleValues": [{ "num": 0, "value": ndText }] }); @@ -610,22 +610,22 @@ function generateMultiParameters() { } -//处理子元素,对于每个块中多出的特殊元素,需要特殊处理 +//处理子element,对于每个块中多出的特殊element,需要特殊处理 function handleDescendents() { let n = 1; chrome.storage.local.get({ parameterNum: 1 }, function(items) { let at = parseInt(new Date().getTime()); n = items.parameterNum; - clearParameters(); //清除原来的参数列表 + clearParameters(); //清除原来的para列表 app._data.selectedDescendents = true; for (let num = 0; num < nodeList.length; num++) { let tnode = nodeList[num]["node"]; - let stack = new Array(); //深度优先搜索遍历元素 + let stack = new Array(); //深度优先搜索遍历element stack.push(tnode); //从此节点开始 while (stack.length > 0) { - let nd = stack.pop(); // 挨个取出元素 + let nd = stack.pop(); // 挨个取出element if (nd.parentNode.tagName == "A" && nd.tagName == "SPAN") { - continue; //对A标签内的SPAN元素不进行处理,剪枝,此时子元素根本不加入stack,即实现了此功能 + continue; //对A标签内的SPANelement不进行处理,剪枝,此时子element根本不加入stack,即实现了此功能 } ndPath = readXPath(nd, 1, tnode); let index = -1; @@ -643,29 +643,29 @@ function handleDescendents() { ndText = $(nd).contents().filter(function() { return this.nodeType === 3; }).text().replace(/\s+/g, ''); - if (index == -1) { //从第二个节点开始,只插入那些不在参数列表中的元素,根据xpath进行寻址 - //如果当前节点除了子元素外仍然有其他文字或者该元素是图片/表单项,加入子元素节点 + if (index == -1) { //从第二个节点开始,只插入那些不在para列表中的element,根据xpath进行寻址 + //如果当前节点除了子element外仍然有其他文字或者该element是Image/表单项,加入子element节点 if (ndText != "" || nd.tagName == "IMG" || nd.tagName == "INPUT" || nd.tagName == "A") { - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters.push({ "nodeType": 4, //节点类型 "contentType": 1, // 内容类型 - "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只选择了子元素没有选中全部的时候,需要判断 - "name": "参数" + (n++) + "_图片地址", - "desc": "", //参数描述 + "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径,注意当只selection了子element没有选中全部的时候,需要判断 + "name": "para" + (n++) + "_imageAddress", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), //同理需要判断 "exampleValues": [{ "num": num, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }] }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters.push({ "nodeType": 1, "contentType": 0, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linktext", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, "value": $(nd).text() }] //注意这里的ndtext是整个a的文字!!! }); @@ -673,21 +673,21 @@ function handleDescendents() { "nodeType": 2, "contentType": 0, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_链接地址", - "desc": "", //参数描述 + "name": "para" + (n++) + "_linkAddress", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }] }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters.push({ "nodeType": 3, "contentType": 1, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, @@ -699,27 +699,27 @@ function handleDescendents() { "nodeType": 0, "contentType": 1, // 内容类型 "relative": nodeList.length > 1 ? true : false, //是否为相对xpath路径 - "name": "参数" + (n++) + "_文本", - "desc": "", //参数描述 + "name": "para" + (n++) + "_text", + "desc": "", //para描述 "relativeXpath": nodeList.length > 1 ? ndPath : readXPath(nd), "exampleValues": [{ "num": num, "value": ndText }] }); } } - } else //如果元素节点已经存在,则只需要插入值就可以了 + } else //如果element节点已经存在,则只需要插入值就可以了 { - if (nd.tagName == "IMG") { //如果元素是图片 + if (nd.tagName == "IMG") { //如果element是Image outputParameters[index]["exampleValues"].push({ "num": num, "value": nd.getAttribute("src") == null ? "" : $(nd).prop("src") }); - } else if (nd.tagName == "A") { //如果元素是超链接 + } else if (nd.tagName == "A") { //如果element是超link outputParameters[index]["exampleValues"].push({ "num": num, "value": $(nd).text() }); outputParameters[index + 1]["exampleValues"].push({ "num": num, "value": nd.getAttribute("href") == null ? "" : $(nd).prop("href") }); - } else if (nd.tagName == "INPUT") { //如果元素是输入项 + } else if (nd.tagName == "INPUT") { //如果element是输入项 outputParameters[index]["exampleValues"].push({ "num": num, "value": nd.getAttribute("value") == null ? "" : nd.getAttribute("value") @@ -734,14 +734,14 @@ function handleDescendents() { } } let at2 = parseInt(new Date().getTime()); - console.log("选中子元素", at2, at, at2 - at); + console.log("选中子element", at2, at, at2 - at); generateValTable(); }); } -//根据参数列表生成可视化参数界面 +//根据para列表生成可视化para界面 function generateValTable(multiline = true) { let paravalues = []; for (let i = 0; i < outputParameters.length; i++) { @@ -764,7 +764,7 @@ function generateValTable(multiline = true) { // 选中第一个节点,自动寻找同类节点 // 方法:/div[1]/div[2]/div[2]/a[1] -// 从倒数第一个节点开始找,看去掉方括号之后是否元素数目变多,如上面的变成/div[1]/div[2]/div[2]/a +// 从倒数第一个节点开始找,看去掉方括号之后是否element数目变多,如上面的变成/div[1]/div[2]/div[2]/a // 如果没有,则恢复原状,然后试试倒数第二个:/div[1]/div[2]/div/a[1] // 直到找到第一个变多的节点或者追溯到根节点为止 function findRelated() { @@ -789,9 +789,9 @@ function findRelated() { tempIndexList[i] = -1; //删除索引值 tempPath = combineXpath(nodeNameList, tempIndexList); //生成新的xpath var result = document.evaluate(tempPath, document, null, XPathResult.ANY_TYPE, null); - result.iterateNext(); //枚举第一个元素 - if (result.iterateNext() != null) { //如果能枚举到第二个元素,说明存在同类元素,选中同类元素,结束循环 - app.$data.nowPath = tempPath; //标记此元素xpath + result.iterateNext(); //枚举第一个element + if (result.iterateNext() != null) { //如果能枚举到第二个element,说明存在同类element,选中同类element,结束循环 + app.$data.nowPath = tempPath; //标记此elementxpath pushToReadyList(tempPath); break; } @@ -801,11 +801,11 @@ function findRelated() { } -//根据path将元素放入readylist中 +//根据path将element放入readylist中 function pushToReadyList(path) { result = document.evaluate(path, document, null, XPathResult.ANY_TYPE, null); - var node = result.iterateNext(); //枚举第一个元素 - while (node) { //只添加不在已选中列表内的元素 + var node = result.iterateNext(); //枚举第一个element + while (node) { //只添加不在已选中列表内的element let exist = false; for (o of nodeList) { if (o["node"] == node) { @@ -817,19 +817,19 @@ function pushToReadyList(path) { readyList.push({ "node": node, "bgColor": node.style.backgroundColor, "boxShadow": node.style.boxShadow == "" || boxShadowColor ? "none" : node.style.boxShadow }); } node.style.boxShadow = boxShadowColor; - node = result.iterateNext(); //枚举下一个元素 + node = result.iterateNext(); //枚举下一个element } } -//将readyList中的元素放入选中节点中 +//将readyList中的element放入选中节点中 function readyToList(step, dealparameters = true) { for (o of readyList) { nodeList.push({ node: o["node"], "step": step, bgColor: o["bgColor"], "boxShadow": o["boxShadow"], xpath: readXPath(o["node"], 1) }); o["node"].style.backgroundColor = selectedColor; } clearReady(); - if (dealparameters) { //防止出现先选中子元素再选中全部失效的问题 - generateParameters(0); //根据nodelist列表内的元素生成参数列表,0代表纯文本 + if (dealparameters) { //防止出现先选中子element再选中全部失效的问题 + generateParameters(0); //根据nodelist列表内的element生成para列表,0代表纯text } } @@ -847,7 +847,7 @@ function combineXpath(nameList, indexList) { return finalPath; } -//专门测试已经选中的这些元素之间有没有相关性 +//专门测试已经选中的这些element之间有没有相关性 // 举例: // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div[1]/div[3]/a[22] // /html/body/div[3]/div[1]/div[1]/div[1]/div[3]/div[2]/div[3]/a[25] @@ -859,7 +859,7 @@ function relatedTest() { var testpath = ""; for (i = 0; i < nodeList.length; i++) { var testnumList = []; //用于比较节点索引号不同 - var tpath = nodeList[i]["xpath"].split("/").splice(1); //清理第一个空元素 + var tpath = nodeList[i]["xpath"].split("/").splice(1); //清理第一个空element for (j = 0; j < tpath.length; j++) { if (tpath[j].indexOf("[") >= 0) { //如果存在索引值 testnumList.push(parseInt(tpath[j].split("[")[1].replace("]", ""))); //只留下数字 @@ -869,16 +869,16 @@ function relatedTest() { tpath[j] = tpath[j].split("[")[0]; } tp = tpath.join("/"); - if (i > 0 && testpath != tp) { //如果去除括号后元素内存在不一致情况,直接返回默认情况代码100 - app.$data.nowPath = ""; //标记此元素xpath + if (i > 0 && testpath != tp) { //如果去除括号后element内存在不一致情况,直接返回默认情况代码100 + app.$data.nowPath = ""; //标记此elementxpath return 100; } testpath = tp; testList.push(testnumList); } - testpath = testpath.split("/"); //清理第一个空元素 + testpath = testpath.split("/"); //清理第一个空element var indexList = []; //记录新生成的xpath - //如果选中的元素属于同样的序列,则计算出序列的最佳xpath表达式 + //如果选中的element属于同样的序列,则计算出序列的最佳xpath表达式 for (j = 0; j < testList[0].length; j++) { indexList.push(testList[0][j]); for (i = 1; i < testList.length; i++) { @@ -889,14 +889,14 @@ function relatedTest() { } } var finalPath = combineXpath(testpath, indexList); - app.$data.nowPath = finalPath; //标记此元素xpath + app.$data.nowPath = finalPath; //标记此elementxpath pushToReadyList(finalPath); let at2 = parseInt(new Date().getTime()); console.log("手动:", at2, at, at2 - at); return 50; //先返回给默认码 } -//实现提示框拖拽功能 +//实现提示 box拖拽功能 $('.tooldrag').mousedown(function(e) { // e.pageX var positionDiv = $(this).offset(); diff --git a/Extension/ServiceWrapper/scripts/messageInteraction.js b/Extension/ServiceWrapper/scripts/messageInteraction.js index 8cd6e867..7e3b0c69 100644 --- a/Extension/ServiceWrapper/scripts/messageInteraction.js +++ b/Extension/ServiceWrapper/scripts/messageInteraction.js @@ -1,6 +1,6 @@ //实现与后台和流程图部分的交互 -if (window.location.href.indexOf("183.129.170.180") >= 0) { +if (window.location.href.indexOf("backEndAddressServiceWrapper") >= 0) { throw "serviceGrid"; //如果是服务器网页页面,则不执行工具 } diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Readme.md b/Readme.md index f214dc27..4f9eed5b 100644 --- a/Readme.md +++ b/Readme.md @@ -1,31 +1,155 @@ +# 请您Star Please 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! + +# 无代码服务可视化Web数据采集爬虫器 Code-Free Visual Web Data Crawler/Spider (Service Wrapper) + +一个可以可视化无代码设计和执行的面向服务架构的爬虫软件(服务包装器)。 +A service oriented architecture GUI visual code-free web crawler/spider (service wrapper). + # 发布版本 -## Windows版本可执行程序: -打开压缩包内的ServiceWrapper.exe即可在Windows10系统执行,无需配置环境。 +## Windows版本可执行程序: +打开压缩包内的ServiceWrapper.exe即可在Windows10/11或以上系统执行,无需配置环境(其余Windows系统需手动安装.net Framework 4.5)。 数据存储后放在Data/文件夹内 -## 视频教程: +## 中文视频教程: + + + + + +# 目录 +注:文档待完善 +- [请您Star Please Star](#请您star-please-star) +- [无代码服务可视化Web数据采集爬虫器 Code-Free Visual Web Data Crawler/Spider (Service Wrapper)](#无代码服务可视化web数据采集爬虫器-code-free-visual-web-data-crawlerspider-service-wrapper) +- [发布版本](#发布版本) + - [Windows版本可执行程序:](#windows版本可执行程序httpsgithubcomnaibowangservicewrapperreleasesdownloadv050servicewrapper7z) + - [中文视频教程:](#中文视频教程httpsgithubcomnaibowangservicewrapperreleasesdownloadv050tutorial_cnmp4) +- [目录](#目录) + - [界面截图](#界面截图) + - [软件界面示例](#软件界面示例) + - [块和子块及表单定义](#块和子块及表单定义) + - [已选中和待选择示例](#已选中和待选择示例) + - [京东商品块选择示例:](#京东商品块选择示例) + - [京东商品标题自动匹配选择示例](#京东商品标题自动匹配选择示例) + - [分块选择所有子元素示例](#分块选择所有子元素示例) + - [同类型元素自动和手动匹配示例](#同类型元素自动和手动匹配示例) + - [四种选择方式示例](#四种选择方式示例) + - [输入文字示例](#输入文字示例) + - [循环点击58同城房屋标题以进入详情页采集示例](#循环点击58同城房屋标题以进入详情页采集示例) + - [采集元素文本示例](#采集元素文本示例) + - [流程图界面介绍](#流程图界面介绍) + - [循环选项示例](#循环选项示例) + - [循环点击下一页示例](#循环点击下一页示例) + - [条件分支示例](#条件分支示例) + - [完整采集流程图示例](#完整采集流程图示例) + - [完整采集流程图转换为常规流程图示例](#完整采集流程图转换为常规流程图示例) + - [服务信息示例](#服务信息示例) + - [服务调用示例](#服务调用示例) + - [58 同城房源信息采集服务部分采集结果展示](#58-同城房源信息采集服务部分采集结果展示) + - [服务包装手动版程序结构](#服务包装手动版程序结构) + - [Chrome插件部分](#chrome插件部分) + - [后台流程图部分](#后台流程图部分) + - [服务展示部分](#服务展示部分) + - [C#部分](#c部分) + - [后台服务页面](#后台服务页面) + - [服务执行](#服务执行) + + + +## 界面截图 + +#### 软件界面示例 + +![pic](media/Picture.png) +#### 块和子块及表单定义 + +![pic](media/Picture2.png) +#### 已选中和待选择示例 + +![pic](media/Picture7.png) +#### 京东商品块选择示例: + +![pic](media/Picture1.png) + + +#### 京东商品标题自动匹配选择示例 + +![pic](media/Picture5.png) +#### 分块选择所有子元素示例 + +![pic](media/Picture6.png) + +#### 同类型元素自动和手动匹配示例 + +![pic](media/Picture8.png) +#### 四种选择方式示例 + +![pic](media/Picture90.png) +#### 输入文字示例 + +![pic](media/Picture10.png) +#### 循环点击58同城房屋标题以进入详情页采集示例 + +![pic](media/Picture12.png) +#### 采集元素文本示例 + +![pic](media/Picture14.png) +#### 流程图界面介绍 + +![pic](media/Picture4.png) +#### 循环选项示例 + +![pic](media/Picture9.png) + +#### 循环点击下一页示例 + +![pic](media/Picture11.png) + +#### 条件分支示例 + +![pic](media/Picture13.png) +#### 完整采集流程图示例 + +![pic](media/Picture16.png) +#### 完整采集流程图转换为常规流程图示例 + +![pic](media/Picture91.png) +#### 服务信息示例 + +![pic](media/Picture15.png) + +#### 服务调用示例 + +![pic](media/Picture17.png) + + +#### 58 同城房源信息采集服务部分采集结果展示 +![pic](media/Picture18.png) -# 服务包装手动版程序结构 -## Chrome插件部分 +## 服务包装手动版程序结构 +### Chrome插件部分 * Extension/app内的文件 -## 后台流程图部分 +### 后台流程图部分 * ServiceGrid/frontEnd/FlowChart.html * ServiceGrid/frontEnd/FlowChart.js * ServiceGrid/frontEnd/FlowChart.css * ServiceGrid/frontEnd/logic.css -## 服务展示部分 +### 服务展示部分 * 服务列表:ServiceGrid/frontEnd/serviceList.html * 服务信息:ServiceGrid/frontEnd/serviceInfo.html * 新服务:ServiceGrid/frontEnd/newService.html * 调用服务:ServiceGrid/frontEnd/invokeService.html -## C#部分 +### C#部分 * C#/内的文件 -## 后台服务页面 +### 后台服务页面 * Django后台:ServiceGrid/backEnd/* -## 服务执行 +### 服务执行 * ExcuteStage/ServiceWrapper_ExcuteStage.py diff --git a/ServiceGrid/backEnd/.gitignore b/ServiceGrid/backEnd/.gitignore new file mode 100644 index 00000000..b6a8c438 --- /dev/null +++ b/ServiceGrid/backEnd/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +nohup.out +*.pid \ No newline at end of file diff --git a/ServiceGrid/backEnd/backEnd/.gitignore b/ServiceGrid/backEnd/backEnd/.gitignore new file mode 100644 index 00000000..d1fa904c --- /dev/null +++ b/ServiceGrid/backEnd/backEnd/.gitignore @@ -0,0 +1 @@ +dbconfig.py \ No newline at end of file diff --git a/ServiceGrid/backEnd/backEnd/settings.py b/ServiceGrid/backEnd/backEnd/settings.py index 59fc86bf..ac211518 100644 --- a/ServiceGrid/backEnd/backEnd/settings.py +++ b/ServiceGrid/backEnd/backEnd/settings.py @@ -38,6 +38,7 @@ 'django.contrib.messages', 'corsheaders', 'django.contrib.staticfiles', + 'sslserver', ] MIDDLEWARE = [ diff --git a/ServiceGrid/backEnd/backEnd/urls.py b/ServiceGrid/backEnd/backEnd/urls.py index 1b968ef8..d904512b 100644 --- a/ServiceGrid/backEnd/backEnd/urls.py +++ b/ServiceGrid/backEnd/backEnd/urls.py @@ -27,6 +27,4 @@ path('backEnd/invokeService',view.invokeService), #调用服务接口 path('backEnd/queryTask',view.queryTask), #调用服务接口 path('backEnd/queryTasks',view.queryTasks), #调用服务接口 - path('backEnd/dongcang/insertInfo',view.insertInfo), - path('backEnd/dongcang/queryInfos',view.queryInfos), ] diff --git a/ServiceGrid/backEnd/backEnd/view.py b/ServiceGrid/backEnd/backEnd/view.py index 1b75baff..076714fc 100644 --- a/ServiceGrid/backEnd/backEnd/view.py +++ b/ServiceGrid/backEnd/backEnd/view.py @@ -3,49 +3,49 @@ import pymongo import json from bson import json_util - +from dbconfig import myclient, mycol, taskcol """ -高分服务器地址:192.168.14.113 -用户名:root -密码:zju.edu.cn -ftp用户:naibowang -密码:qq -程序位置:/root/servicewrapper +uncomment this to deploy database """ +# myclient = pymongo.MongoClient('mongodb://username:password@localhost:27017/') +# mydb = myclient['service'] +# mycol = mydb["services"] +# taskcol = mydb["tasks"] # 生成新任务并返回ID + def hello(request): return HttpResponse("Hello world ! ") -myclient = pymongo.MongoClient('mongodb://localhost:27017/') -mydb = myclient['service'] -mycol = mydb["services"] -taskcol = mydb["tasks"] #生成新任务并返回ID - def queryServices(request): - result = mycol.find({"id" : { "$ne" : -2 }},{ "name": 1, "id": 1, "url": 1, "_id": 0 }) #查询id不为-2的元素 + result = mycol.find({"id": {"$ne": -2}}, {"name": 1, + "id": 1, "url": 1, "_id": 0}) # 查询id不为-2的元素 return HttpResponse(json.dumps(list(result)), content_type="application/json") + def queryTasks(request): - result = taskcol.find({"id" : { "$ne" : -2 }},{ "name": 1, "id": 1, "url": 1, "_id": 0 }) #查询id不为-2的元素 + result = taskcol.find( + {"id": {"$ne": -2}}, {"name": 1, "id": 1, "url": 1, "_id": 0}) # 查询id不为-2的元素 return HttpResponse(json.dumps(list(result)), content_type="application/json") + def queryService(request): if 'id' in request.GET: tid = request.GET['id'] else: tid = "0" - result = mycol.find({"id":int(tid)},{"_id":0}) + result = mycol.find({"id": int(tid)}, {"_id": 0}) r = list(result)[0] return HttpResponse(json.dumps(r), content_type="application/json") + def queryTask(request): if 'id' in request.GET: tid = request.GET['id'] else: tid = "0" - - result = taskcol.find({"id":int(tid)},{"_id":0}) + + result = taskcol.find({"id": int(tid)}, {"_id": 0}) r = list(result)[0] return HttpResponse(json.dumps(r), content_type="application/json") @@ -54,55 +54,68 @@ def manageService(request): data = request.POST['paras'] data = json.loads(data) if int(data["id"]) == -1: - count = mycol.find({}).count() - data["id"] = count #修改id - mycol.insert_one(data) + count = mycol.count_documents({}) + data["id"] = count # 修改id + mycol.insert_one(data) else: - mycol.delete_one({"id":int(data["id"])}) + mycol.delete_one({"id": int(data["id"])}) mycol.insert_one(data) return HttpResponse(data["id"]) + def deleteService(request): if 'id' in request.GET: tid = request.GET['id'] - myquery = { "id": int(tid) } - newvalues = { "$set": { "id": -2 } } #删除就是将服务id变成-2,并没有真正删除 + myquery = {"id": int(tid)} + newvalues = {"$set": {"id": -2}} # 删除就是将服务id变成-2,并没有真正删除 mycol.update_one(myquery, newvalues) return HttpResponse("Done!") # 调用服务 + + def invokeService(request): tid = request.POST['id'] data = json.loads(request.POST['paras']) - result = mycol.find({"id":int(tid)},{"_id":0}) + result = mycol.find({"id": int(tid)}, {"_id": 0}) service = list(result)[0] - for key,value in data.items(): + try: + service["links"] = data["urlList_0"] + except: + pass + for key, value in data.items(): for i in range(len(service["inputParameters"])): - if key == service["inputParameters"][i]["name"]: #能调用 + if key == service["inputParameters"][i]["name"]: # 能调用 nodeId = int(service["inputParameters"][i]["nodeId"]) node = service["graph"][nodeId] if node["option"] == 1: node["parameters"]["links"] = value elif node["option"] == 4: node["parameters"]["value"] = value + elif node["option"] == 8 and node["parameters"]["loopType"] == 0: + # print("loopType 0", value) + node["parameters"]["exitCount"] = int(value) + # print(node) elif node["option"] == 8: node["parameters"]["textList"] = value break - count = taskcol.find({}).count() - service["id"] = count #修改id - taskcol.insert_one(service) + count = taskcol.count_documents({}) + service["id"] = count # 修改id + taskcol.insert_one(service) return HttpResponse(count) + def insertInfo(request): request.GET = request.GET.copy() data = request.GET dbd = myclient['dongcang'] cold = dbd["redirect"] - cold.insert_one(data) + cold.insert_one(data) return HttpResponse("200") + def queryInfos(request): dbd = myclient['dongcang'] cold = dbd["redirect"] result = cold.find() - return HttpResponse(json_util.dumps(result).encode('utf-8').decode('unicode_escape'), content_type="application/json") \ No newline at end of file + return HttpResponse(json_util.dumps(result).encode('utf-8').decode('unicode_escape'), content_type="application/json") diff --git a/ServiceGrid/backEnd/db.sqlite3 b/ServiceGrid/backEnd/db.sqlite3 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cfb0b47398225684b176d732d8b03dd925977f11 100644 GIT binary patch literal 131072 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 literal 0 HcmV?d00001 diff --git a/ServiceGrid/backEnd/server.conf b/ServiceGrid/backEnd/server.conf new file mode 100644 index 00000000..588dfdb6 --- /dev/null +++ b/ServiceGrid/backEnd/server.conf @@ -0,0 +1,40 @@ +server { + listen 443 ssl; + + server_name servicewrapper.naibo.wang; + + #ssl_certificate cert/5640170_naibo.wang.pem; + #ssl_certificate_key cert/5640170_naibo.wang.key; + ssl_certificate /etc/letsencrypt/live/servicewrapper.naibo.wang/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/servicewrapper.naibo.wang/privkey.pem; + + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #表示使用的TLS协议的类型。 + ssl_prefer_server_ciphers on; + + root /home/naibo/server/ServiceWrapper/ServiceGrid/frontEnd; + index serviceList.html; + + + location /backEnd { + include uwsgi_params; + uwsgi_connect_timeout 30; + uwsgi_pass 127.0.0.1:8072; + } + + location /static { + autoindex on; + autoindex_localtime on; + alias /home/naibo/static; + try_files $uri $uri/ =404; + } +} + + +server { + listen 80; + listen [::]:80; + server_name xtra3090.d2.comp.nus.edu.sg naibo.wang gui.naibo.wang servicewrapper.naibo.wang; #需要将yourdomain.com替换成证书绑定的域名。 + return 301 https://$host$request_uri; +} diff --git a/ServiceGrid/frontEnd/FlowChart.html b/ServiceGrid/frontEnd/FlowChart.html index 254be5b2..bea4938d 100644 --- a/ServiceGrid/frontEnd/FlowChart.html +++ b/ServiceGrid/frontEnd/FlowChart.html @@ -16,29 +16,29 @@
-
工具箱
- +
Toolbox
+ - - - - - - - - + + + + + + + +
-----------------
- - - - - -
提示:点击上方操作按钮后点击要放置元素的位置处的箭头,可按取消操作按钮取消。
+ + + + + +
@@ -54,47 +54,47 @@
-->
- +
-

使用循环内的链接

+

Use link inside the Loop

- +
- + - +
-

使用循环内的元素

+

Use element inside the Loop

- + - +
@@ -103,9 +103,9 @@
- - - + + +
字段名示例值操作Field NameExample ValueOperations
@@ -115,22 +115,37 @@
{{paras.parameters[i-1]["exampleValues"][0]["value"]}} - 修改 - 删除 - 上移 - 下移 + Modify + Delete + Up + Down
- -

使用相对循环内的XPATH

+ +

Use relative XPATH

- + + + + + - +
@@ -140,10 +155,10 @@
-

使用循环内的文本

+

Use text inside the Loop

- +
@@ -165,31 +180,40 @@
- +
- +
- +
- + - + + + + + +
@@ -197,22 +221,22 @@
- + - +
- +
- +
@@ -225,20 +249,20 @@ `; @@ -199,7 +199,7 @@ function branchMouseDown(e) { parentId: 0, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -232,7 +232,7 @@ function branchClick(e) { parentId: 0, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -269,7 +269,7 @@ function arrowClick(e) { function addElement(op, para) { option = op; if (option == 1) { //打开网页选项 - title = "打开网页"; + title = "Open Page"; } else { title = $(".options")[option - 2].innerHTML; //获取新增操作名称 } @@ -287,7 +287,7 @@ function toolBoxKernel(e, para = null) { if (nowNode == null) { e.stopPropagation(); //防止冒泡 } else if (nowNode.getAttribute("dataType") > 0) { - alert("循环和判断、条件分支不可复制!"); + alert("Cannot copy loop, if and condition!"); e.stopPropagation(); //防止冒泡 } else { let position = parseInt(nowNode.getAttribute('position')); @@ -309,7 +309,7 @@ function toolBoxKernel(e, para = null) { if (nowNode == null) { e.stopPropagation(); //防止冒泡 } else if ($(nowNode).is(".branch")) { - alert("判断分支不可移动!"); + alert("Cannot move condition branch!"); e.stopPropagation(); //防止冒泡 } else { let position = parseInt(nowNode.getAttribute('position')); @@ -341,7 +341,7 @@ function toolBoxKernel(e, para = null) { app._data.nowArrow = { "position": nodeList[element[0]]["position"], "pId": nodeList[element[0]]["parentId"], "num": 0 }; $("#" + nodeList[element[0]]["id"]).click(); } else { - alert("自己不能移动到自己的节点里!"); + alert("Cannot move inside self!"); } e.stopPropagation(); //防止冒泡 } @@ -371,7 +371,7 @@ function toolBoxKernel(e, para = null) { index: l + 1, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -381,7 +381,7 @@ function toolBoxKernel(e, para = null) { index: l + 2, type: 3, option: 10, - title: "条件分支", + title: "Condition", sequence: [], isInLoop: false, }; @@ -426,7 +426,7 @@ $(".options").mousedown(function() { option = parseInt(this.getAttribute("data")); title = this.innerHTML; if (option >= 10 && option <= 12 && (nowNode == null || nowNode.getAttribute("id") == 0)) { - alert("目前未选中元素"); + alert("No element is selected now!"); } else if (option == 12) { deleteElement(); $(".options")[12].click(); @@ -501,13 +501,13 @@ function refresh(nowArrowReset = true) { function deleteElement() { if (nowNode.getAttribute("id") == 0) { - alert("当前未选中元素!"); //root - return; - } - if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { - alert("打开网页操作不可删除!"); + alert("No element is selected now!"); //root return; } + // if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { + // alert("Cannot delete the element of Open Page!"); + // return; + // } let position = parseInt(nowNode.getAttribute('position')); let pId = nowNode.getAttribute('pId'); let tnode = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 @@ -539,7 +539,7 @@ document.oncontextmenu = function() { //删除元素 document.onkeydown = function(e) { if (nowNode != null && e.keyCode == 46) { - if (confirm("确定要删除元素吗?")) { + if (confirm("Do you really want to delete the selected element?")) { deleteElement(); } } else { //ctrl+s保存服务 diff --git a/ServiceGrid/frontEnd/FlowChart_CN.html b/ServiceGrid/frontEnd/FlowChart_CN.html new file mode 100644 index 00000000..5655d430 --- /dev/null +++ b/ServiceGrid/frontEnd/FlowChart_CN.html @@ -0,0 +1,277 @@ + + + + + + + + + + + + + 设计流程 + + + + + +
+
+
+
工具箱
+ + + + + + + + + + +
-----------------
+ + + + + +
提示:点击上方操作按钮后点击要放置元素的位置处的箭头,可按取消操作按钮取消。
+
+
+
+
+
+
+

+
+
+
+ +
+
+ + +
+ +
+
+ +

使用循环内的链接

+
+
+ + + + +
+ + + + +
+ +
+
+ +

使用循环内的元素

+
+
+ + +
+ + + + + +
+ +
+
+ + + + + + +
字段名示例值操作
+ + + + + + + +
+ {{paras.parameters[i-1]["exampleValues"][0]["value"]}} + 修改 + 删除 + 上移 + 下移 +
+
+
+ +

使用相对循环内的XPATH

+ + + + + + + + + + +
+ +
+ + +
+
+ +

使用循环内的文本

+
+
+ + +
+ + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + + +
+ + +
+
+ + +
+
+ + +
+ + + + + + + + + + +
+ +
+ +
+ +
+ + + + +
+
+ + +
+ + +
+ + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/ServiceGrid/frontEnd/FlowChart_CN.js b/ServiceGrid/frontEnd/FlowChart_CN.js new file mode 100644 index 00000000..cabcb0d6 --- /dev/null +++ b/ServiceGrid/frontEnd/FlowChart_CN.js @@ -0,0 +1,561 @@ +//处理表现层 +var nodeList = Array(); //所有新生成的节点全部存储在这里,并且有唯一索引号,所有的定位均通过index进行,即将图保存下来了 +var root = { + index: 0, //在nodeList中的索引号 + id: 0, + parentId: 0, + type: -1, + option: 0, + title: "root", + sequence: [], + parameters: { + history: 1, + tabIndex: 0, + useLoop: false, //是否使用循环中的元素 + xpath: "", //xpath + wait: 0, + }, + isInLoop: false, //是否处于循环内 +}; +nodeList.push(root); +var queue = new Array(); +var actionSequence = new Array(); //存储图结构,每个元素为在nodelist里面的索引值,下面的id和pid根据此数组进行索引,然后再在nodelist里找 +var nowNode = null; //存储现在所在的节点 +var vueData = { nowNodeIndex: 0 }; //存储目前所在节点的索引号,不能直接使用变量而需要用对象包起来 +var option = 0; //工具箱选项 +var title = ""; +var parameterNum = 1; //记录目前的参数个数 + +//处理逻辑层 +var app = new Vue({ + el: '#app', + data: { + list: { nl: nodeList }, + index: vueData, + nodeType: 0, // 当前元素的类型 + nowNode: null, // 用来临时存储元素的节点 + loopType: -1, //点击循环时候用来循环选项 + useLoop: false, //记录是否使用循环内元素 + nowArrow: { "position": -1, "pId": 0, "num": 0 }, + paras: { "parameters": [] }, //提取数据的参数列表 + TClass: -1, //条件分支的条件类别 + paraIndex: 0, //当前参数的index + }, + watch: { + nowArrow: { //变量发生变化的时候进行一些操作 + deep: true, + handler: function(newVal, oldVal) { + let arrlist = document.getElementsByClassName("arrow"); + if (oldVal != null) { + for (let i = 0; i < arrlist.length; i++) { + if (arrlist[i].getAttribute("position") == oldVal["position"] && + arrlist[i].getAttribute("pid") == oldVal["pId"]) { + arrlist[i].style.backgroundColor = ""; // 时刻指示现在应该插入的节点的位置 + break; + } + } + } + for (let i = 0; i < arrlist.length; i++) { + if (arrlist[i].getAttribute("position") == newVal["position"] && + arrlist[i].getAttribute("pid") == newVal["pId"]) { + arrlist[i].style.backgroundColor = "lavender"; // 时刻指示现在应该插入的节点的位置 + break; + } + } + } + }, + loopType: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["loopType"] = newVal; + } + }, + TClass: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["class"] = newVal; + } + }, + useLoop: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["useLoop"] = newVal; + } + }, + paras: { + handler: function(newVal, oldVal) { + this.nowNode["parameters"]["paras"] = newVal["parameters"]; + } + }, + }, + methods: { + modifyParas: function(i) { //修改第i个参数 + this.paraIndex = i; + }, + deleteParas: function(i) { //删除第i个参数 + this.nowNode["parameters"]["paras"].splice(i, 1); + //如果参数删除完了,就把提取数据也删掉 + if (this.nowNode["parameters"]["paras"].length == 0) { + deleteElement(); + } + }, + upParas: function(i) { //上移第i个参数 + if (i != 0) { + let t = this.nowNode["parameters"]["paras"].splice(i, 1)[0]; + this.nowNode["parameters"]["paras"].splice(i - 1, 0, t); + } + }, + downParas: function(i) { //下移第i个参数 + if (i != this.nowNode["parameters"]["paras"].length - 1) { + let t = this.nowNode["parameters"]["paras"].splice(i, 1)[0]; + this.nowNode["parameters"]["paras"].splice(i + 1, 0, t); + } + }, + + getType: function(nodeType, contentType) { //根据类型得到字段名称 + if (contentType == 2) { + return "InnerHTML"; + } else if (contentType == 3) { + return "OuterHTML"; + } + if (nodeType == 2) { + return "链接地址"; + } else if (nodeType == 1) { + return "链接文本"; + } else if (nodeType == 4) { + return "图片地址"; + } else { + return "文本"; + } + } + } +}) + +//深复制 +function DeepClone(obj) { + if (obj === null || typeof obj !== 'object') return obj; + var cpObj = obj instanceof Array ? [] : {}; + for (var key in obj) cpObj[key] = DeepClone(obj[key]); + return cpObj; +} + +// 根据元素类型返回不同元素的样式 +function newNode(node) { + id = node["id"]; + title = node["title"]; + type = node["type"]; + if (type == 0) //顺序 + { + return `
+
+

${title}

+
+
+

`; + } else if (type == 1) //循环 + { + return `
+

${title}

+

+
+

`; + } else if (type == 2) //判断 + { + return `
+

${title}

+

点击此处在最左边增加条件分支

+
+
+

`; + } else //判断分支 + { + return `
+

${title}

+

+
+
`; + } +} + +function elementMousedown(e) { + if (e.button == 2) //右键点击 + { + if (nowNode != null) { + nowNode.style.borderColor = "skyblue"; + } + nowNode = this; + vueData.nowNodeIndex = actionSequence[this.getAttribute("data")]; + this.style.borderColor = "blue"; + handleElement(); //处理元素 + } + e.stopPropagation(); //防止冒泡 +} + +function branchMouseDown(e) { + if (e.button == 2) //右键点击 + { + let judgeId = this.getAttribute('data'); + var l = nodeList.length; + var t = { + index: l, + id: 0, + parentId: 0, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + addParameters(t) + nodeList.push(t); + nodeList[actionSequence[judgeId]]["sequence"].splice(0, 0, t.index); + refresh(); + app._data.nowArrow = { "position": -1, "pId": t["id"], "num": 0 }; + $("#" + t["id"]).click(); + } + e.stopPropagation(); //防止冒泡 +} + +function arrowMouseDown(e) { + if (e.button == 2) //右键点击 + { + if (option != 0) { + app._data.nowArrow = { "position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0 }; + } + toolBoxKernel(e); + } +} +//增加分支点击事件 +function branchClick(e) { + let judgeId = this.getAttribute('data'); + var l = nodeList.length; + var t = { + index: l, + id: 0, + parentId: 0, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + addParameters(t); + nodeList.push(t); + nodeList[actionSequence[judgeId]]["sequence"].splice(0, 0, t.index); + refresh(); + app._data.nowArrow = { "position": -1, "pId": t["id"], "num": 0 }; + $("#" + t["id"]).click(); + e.stopPropagation(); //防止冒泡 +} + +//元素点击事件 +function elementClick(e) { + if (nowNode != null) { + nowNode.style.borderColor = "skyblue"; + } + nowNode = this; + vueData.nowNodeIndex = actionSequence[this.getAttribute("data")]; + this.style.borderColor = "blue"; + handleElement(); //处理元素 + e.stopPropagation(); //防止冒泡 +} + +//箭头点击事件 +function arrowClick(e) { + if (option != 0) { + app._data.nowArrow = { "position": this.getAttribute('position'), "pId": this.getAttribute('pId'), "num": 0 }; + } + toolBoxKernel(e); +} + +//增加元素函数 +function addElement(op, para) { + option = op; + if (option == 1) { //打开网页选项 + title = "打开网页"; + } else { + title = $(".options")[option - 2].innerHTML; //获取新增操作名称 + } + + toolBoxKernel(null, para); +} + +// 工具箱操作函数 +function toolBoxKernel(e, para = null) { + if (option == 13) { //调整锚点 + // let tarrow = DeepClone(app.$data.nowArrow); + // refresh(); + // app._data.nowArrow =tarrow; + } else if (option == 11) { //复制操作 + if (nowNode == null) { + e.stopPropagation(); //防止冒泡 + } else if (nowNode.getAttribute("dataType") > 0) { + alert("循环和判断、条件分支不可复制!"); + e.stopPropagation(); //防止冒泡 + } else { + let position = parseInt(nowNode.getAttribute('position')); + let pId = nowNode.getAttribute('pId'); + var tt = nodeList[nodeList[actionSequence[pId]]["sequence"][position]]; //在相应位置添加新元素 + t = DeepClone(tt); //浅复制元素 + var l = nodeList.length; + t.index = l; + nodeList.push(t); + var position2 = parseInt(app._data.nowArrow['position']); + var pId2 = app._data.nowArrow['pId']; + nodeList[actionSequence[pId2]]["sequence"].splice(position2 + 1, 0, t.index); //在相应位置添加新元素 + refresh(); //重新渲染页面 + app._data.nowArrow = { "position": t["position"], "pId": t["parentId"], "num": 0 }; + $("#" + t["id"]).click(); //复制后点击复制后的元素 + e.stopPropagation(); //防止冒泡 + } + } else if (option == 10) { //剪切操作 + if (nowNode == null) { + e.stopPropagation(); //防止冒泡 + } else if ($(nowNode).is(".branch")) { + alert("判断分支不可移动!"); + e.stopPropagation(); //防止冒泡 + } else { + let position = parseInt(nowNode.getAttribute('position')); + let pId = nowNode.getAttribute('pId'); + var position2 = parseInt(app._data.nowArrow['position']); + var pId2 = app._data.nowArrow['pId']; + var id = nowNode.getAttribute('data'); + var pidt = pId2; + var move = true; + console.log(pidt, id); + while (pidt != 0) { + if (pidt == id) { + move = false; + break; + } + pidt = nodeList[actionSequence[pidt]]["parentId"]; + } + if (move) //如果自己要移动到自己节点里就不允许移动 + { + let element = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 + if (pId == pId2 && position < position2) //如果要移动的位置属于同一层并且是从前往后移动,注意需要控制数组插入位置向前错位 + { + position2--; + } + console.log(element); + nodeList[actionSequence[pId2]]["sequence"].splice(position2 + 1, 0, element[0]); //在相应位置添加新元素 + refresh(); //重新渲染页面 + console.log(nodeList[element[0]]); + app._data.nowArrow = { "position": nodeList[element[0]]["position"], "pId": nodeList[element[0]]["parentId"], "num": 0 }; + $("#" + nodeList[element[0]]["id"]).click(); + } else { + alert("自己不能移动到自己的节点里!"); + } + e.stopPropagation(); //防止冒泡 + } + } else if (option > 0) { //新增操作 + var l = nodeList.length; + var t = { + id: 0, + index: l, + parentId: 0, + type: 0, + option: option, + title: title, + sequence: [], + isInLoop: false, + }; + nodeList.push(t); + if (option == 8) //循环 + { + t["type"] = 1; + } else if (option == 9) //判断 + { + t["type"] = 2; + // 增加两个分支 + var nt = { + id: 0, + parentId: 0, + index: l + 1, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + var nt2 = { + id: 0, + parentId: 0, + index: l + 2, + type: 3, + option: 10, + title: "条件分支", + sequence: [], + isInLoop: false, + }; + t["sequence"].push(nt.index); + t["sequence"].push(nt2.index); + nodeList.push(nt) + nodeList.push(nt2); + addParameters(nt); //增加选项的默认参数 + addParameters(nt2); //增加选项的默认参数 + } + let position = parseInt(app._data.nowArrow['position']); + let pId = app._data.nowArrow['pId']; + nodeList[actionSequence[pId]]["sequence"].splice(position + 1, 0, t.index); //在相应位置添加新元素 + refresh(); //重新渲染页面 + //下面是确定添加元素之后下一个要插入的节点的位置 + app._data.nowArrow = { "position": t["position"], "pId": t["parentId"], "num": 0 }; + addParameters(t); //增加选项的默认参数 + if (para != null) { + modifyParameters(t, para); + } + if (option == 8) //循环情况下应插入在循环里面 + { + app._data.nowArrow = { "position": -1, "pId": t["id"], "num": 0 }; + $("#" + t["id"]).click(); + } else if (option == 9) //判断插入到第一个判断条件中 + { + app._data.nowArrow = { "position": -1, "pId": nt["id"], "num": 0 }; + $("#" + nt["id"]).click(); + } else { + $("#" + t["id"]).click(); + } + + if (e != null) + e.stopPropagation(); //防止冒泡 + option = 0; + return t; + } + option = 0; +} + +$(".options").mousedown(function() { + option = parseInt(this.getAttribute("data")); + title = this.innerHTML; + if (option >= 10 && option <= 12 && (nowNode == null || nowNode.getAttribute("id") == 0)) { + alert("目前未选中元素"); + } else if (option == 12) { + deleteElement(); + $(".options")[12].click(); + } +}); + +function bindEvents() { + // 清空原来的listener然后再添加新的listener + //以下绑定了左右键的行为 + let rect = document.getElementsByClassName('clk'); + for (let i = 0, rule; rule = rect[i++];) { + rule.removeEventListener('mousedown', elementMousedown); + rule.addEventListener('mousedown', elementMousedown); + rule.removeEventListener('click', elementClick); + rule.addEventListener('click', elementClick); + } + let arr = document.getElementsByClassName('arrow'); + for (let i = 0, rule; rule = arr[i++];) { + rule.removeEventListener('click', arrowClick); + rule.addEventListener('click', arrowClick); + rule.removeEventListener('mousedown', arrowMouseDown); + rule.addEventListener('mousedown', arrowMouseDown); + } + let branch = document.getElementsByClassName('branchAdd'); + for (let i = 0, rule; rule = branch[i++];) { + rule.removeEventListener('click', branchClick); + rule.addEventListener('click', branchClick); + rule.removeEventListener('mousedown', branchMouseDown); + rule.addEventListener('mousedown', branchMouseDown); + } +} + +//重新画图 +function refresh(nowArrowReset = true) { + $("#0").empty(); + $("#0").append(`
+

+
+

`); + actionSequence.splice(0); + queue.splice(0); + var idd = 1; + queue.push(0); + actionSequence.push(0); + while (queue.length != 0) { + var nd = queue.shift(); //取出父元素并建立对子元素的链接 + for (i = 0; i < nodeList[nd].sequence.length; i++) { + nodeList[nodeList[nd].sequence[i]].parentId = nodeList[nd].id; + nodeList[nodeList[nd].sequence[i]]["position"] = i; + nodeList[nodeList[nd].sequence[i]].id = idd++; + //检测元素是否位于循环内 + if (nodeList[nd].option == 8 || nodeList[nd].isInLoop) { + nodeList[nodeList[nd].sequence[i]].isInLoop = true; + } else { + nodeList[nodeList[nd].sequence[i]].isInLoop = false; + } + queue.push(nodeList[nd].sequence[i]); + actionSequence.push(nodeList[nd].sequence[i]); + } + } + if (nowArrowReset) //如果要重置锚点位置 + { + app._data.nowArrow = { "position": -1, "pId": 0, "num": 0 }; //设置默认要添加的位置是元素流程最开头处 + } + //第一个元素不渲染 + for (i = 1; i < actionSequence.length; i++) { + parentId = nodeList[actionSequence[i]]["parentId"]; + $("#" + parentId).append(newNode(nodeList[actionSequence[i]])); + } + bindEvents(); +} + +function deleteElement() { + if (nowNode.getAttribute("id") == 0) { + alert("当前未选中元素!"); //root + return; + } + // if (nodeList[actionSequence[nowNode.getAttribute("data")]]["option"] == 1) { + // alert("打开网页操作不可删除!"); + // return; + // } + let position = parseInt(nowNode.getAttribute('position')); + let pId = nowNode.getAttribute('pId'); + let tnode = nodeList[actionSequence[pId]]["sequence"].splice(position, 1); //在相应位置删除元素 + //循环的标记已经被删除的元素,因为删除循环后,循环内的元素也会 + let queue = new Array(); + queue.push(tnode[0]); + while (queue.length > 0) { + let index = queue.shift(); + nodeList[index]["id"] = -1; //标记服务已被删除 + for (let i = 0; i < nodeList[index]["sequence"].length; i++) { + queue.push(nodeList[index]["sequence"][i]); + } + } + app._data["nowNode"] = null; + app._data["nodeType"] = 0; + vueData.nowNodeIndex = 0; + if (nowNode.getAttribute("datatype") == 3) { //如果删掉的是条件分支的话 + pId = nowNode.parentNode.parentNode.getAttribute('pId'); + position = nowNode.parentNode.parentNode.getAttribute('position'); + } + app.$data.nowArrow = { position: position - 1, "pId": pId, "num": 0 }; //删除元素后锚点跳转到当前元素的上一个节点 + refresh(false); //重新渲染页面 + nowNode = null; //取消选择 +} + +document.oncontextmenu = function() { + return false; + } //屏蔽右键菜单 + //删除元素 +document.onkeydown = function(e) { + if (nowNode != null && e.keyCode == 46) { + if (confirm("确定要删除元素吗?")) { + deleteElement(); + } + } else { //ctrl+s保存服务 + var currKey = 0, + e = e || event || window.event; + currKey = e.keyCode || e.which || e.charCode; + if (currKey == 83 && (e.ctrlKey || e.metaKey)) { + $('#save').click(); + return false; + } + + } +} + +function inputDelete(e) { + if (e.keyCode == 46) { + e.stopPropagation(); //输入框按delete应该正常运行 + } +} \ No newline at end of file diff --git a/ServiceGrid/frontEnd/invokeService.html b/ServiceGrid/frontEnd/invokeService.html index ab31b5ba..2a34ce60 100644 --- a/ServiceGrid/frontEnd/invokeService.html +++ b/ServiceGrid/frontEnd/invokeService.html @@ -8,7 +8,7 @@ - 服务调用 + Service Invoke