page.js 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. let nowFileName = "";
  2. let jsonInfo = {};
  3. let tabId = "";
  4. let nodeObj;
  5. let jslist;
  6. let cssList;
  7. let cssFileInfo = {}
  8. let oldFile; // 记录上一次点击的文件信息
  9. let path;
  10. let targe;
  11. // 记录页卡信息
  12. let pageTab;
  13. chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
  14. if (request.msgToPopup === "发送页面") {
  15. // 结构接收的信息
  16. let { jslist: _jslist, targe: _targe, csslist: _csslist, tabId: _tabId, path: _path } = request.data;
  17. jslist = _jslist;
  18. tabId = _tabId;
  19. cssList = _csslist; // 页面的所有样式信息
  20. path = _path;
  21. targe = _targe;
  22. cssFileInfo = {}
  23. await attach();
  24. await getPathNodeInfo(path);
  25. if (!pageTab) {
  26. // 创建页卡实例
  27. let tabs = [
  28. new Tab({ id: 'tabFirst', isAlived: true, activeEvent: () => { domOptionTab() } }),
  29. new Tab({ id: 'tabSecond', isAlived: true, activeEvent: () => { domPropTab() } }),
  30. new Tab({ id: 'tabThree', isAlived: true, activeEvent: () => { domEventTab() } })
  31. ]
  32. pageTab = new PageTab({ tabs });
  33. }
  34. pageTab.reset();
  35. pageTab.activeTabById("tabFirst");
  36. }
  37. });
  38. function findPropRef(key, json) {
  39. let matchedCSSRules = json.matchedCSSRules;
  40. let retArr = [];
  41. for (const matchedCSSRule of matchedCSSRules) {
  42. let cssProperties = matchedCSSRule.rule.style.cssProperties;
  43. let cssProp = cssProperties.find((m) => m.name == key);
  44. if (!cssProp) continue;
  45. let ret = {
  46. 来源: "CSS",
  47. 选择器: matchedCSSRule.rule.selectorList.text,
  48. 选中选择器: matchedCSSRule.matchingSelectors[0],
  49. 属性值: cssProp.value,
  50. };
  51. retArr.push(ret);
  52. }
  53. return retArr;
  54. }
  55. async function cssCodeToAST(cssCode) {
  56. let curAst = csstree.parse(cssCode, {
  57. parseAtrulePrelude: false,
  58. parseRulePrelude: false,
  59. parseValue: false,
  60. positions: true,
  61. });
  62. let selectors = [];
  63. //https://github.com/csstree/csstree/blob/master/docs/parsing.md#parsesource-options
  64. await csstree.walk(curAst, (node) => {
  65. if (node.type === 'Rule')//node.type === 'Declaration'
  66. {
  67. let curSelector;
  68. if (node.prelude.children) {
  69. curSelector = node.prelude.children.map(child => child.raw);
  70. }
  71. else {
  72. curSelector = node.prelude.value;
  73. }
  74. let loc = node.prelude.loc;
  75. if (curSelector) selectors.push({
  76. //name: curSelector,//curSelector,
  77. name: curSelector.replaceAll("\r\n*", " ").replaceAll("\r\n", " "),
  78. sourceName: cssCode.substring(loc.start.offset, loc.end.offset),
  79. loc: loc,
  80. });
  81. }
  82. });
  83. return selectors;
  84. }
  85. async function getPathNodeInfo(path) {
  86. const doc = await getDocument();
  87. // const htmlInfo = doc.children.find((m) => m.localName === "html");
  88. let info = doc;
  89. for (const index of path) {
  90. info = getpath(info, index);
  91. }
  92. nodeObj = info;
  93. nodeId = info.nodeId;
  94. let cssInfo = await getMatchedStylesForNode(nodeId);
  95. jsonInfo = cssInfo;
  96. function getpath(info, index) {
  97. let children = info.children;
  98. return children[index];
  99. }
  100. }
  101. async function getMatchedStylesForNode(nodeId) {
  102. let ret = await chrome.debugger.sendCommand(
  103. { tabId },
  104. "CSS.getMatchedStylesForNode",
  105. {
  106. nodeId,
  107. }
  108. );
  109. return ret;
  110. }
  111. async function getDocument() {
  112. const doc = await chrome.debugger.sendCommand({ tabId }, "DOM.getDocument", {
  113. pierce: true,
  114. depth: -1,
  115. });
  116. return doc.root;
  117. }
  118. async function attach() {
  119. // const targets = await chrome.debugger.getTargets();
  120. // let target = targets.find((m) => m.tabId == tabId);
  121. // if (target?.attached === true) {
  122. // } else {
  123. // await chrome.debugger.attach({ tabId }, "1.3");
  124. // }
  125. await chrome.debugger.attach({ tabId }, "1.3").catch(() => { });
  126. await chrome.debugger.sendCommand({ tabId }, "DOM.enable");
  127. await chrome.debugger.sendCommand({ tabId }, "CSS.enable");
  128. await chrome.debugger.sendCommand({ tabId }, "Debugger.enable");
  129. }
  130. async function findEvent() {
  131. let winObj = await chrome.debugger.sendCommand(
  132. { tabId },
  133. "Runtime.evaluate",
  134. {
  135. expression: "self",
  136. objectGroup: "",
  137. includeCommandLineAPI: false,
  138. silent: true,
  139. returnByValue: false,
  140. generatePreview: false,
  141. userGesture: false,
  142. awaitPromise: false,
  143. }
  144. );
  145. let winEvents = await chrome.debugger.sendCommand(
  146. { tabId },
  147. "DOMDebugger.getEventListeners",
  148. {
  149. objectId: winObj.result.objectId,
  150. }
  151. );
  152. let documentObj = await chrome.debugger.sendCommand(
  153. { tabId },
  154. "Runtime.evaluate",
  155. {
  156. expression: "document",
  157. objectGroup: "",
  158. includeCommandLineAPI: false,
  159. silent: true,
  160. returnByValue: false,
  161. generatePreview: false,
  162. userGesture: false,
  163. awaitPromise: false,
  164. }
  165. );
  166. let documentEvents = await chrome.debugger.sendCommand(
  167. { tabId },
  168. "DOMDebugger.getEventListeners",
  169. {
  170. objectId: documentObj.result.objectId,
  171. }
  172. );
  173. let selfObj = await chrome.debugger.sendCommand(
  174. { tabId },
  175. "DOM.resolveNode",
  176. {
  177. nodeId: nodeObj.nodeId,
  178. }
  179. );
  180. let selfEvents = await chrome.debugger.sendCommand(
  181. { tabId },
  182. "DOMDebugger.getEventListeners",
  183. {
  184. objectId: selfObj.object.objectId,
  185. }
  186. );
  187. for (const listener of winEvents.listeners) {
  188. await listenerGetFile(listener);
  189. }
  190. for (const listener of documentEvents.listeners) {
  191. await listenerGetFile(listener);
  192. }
  193. for (const listener of selfEvents.listeners) {
  194. await listenerGetFile(listener);
  195. }
  196. return {
  197. winEvents: winEvents.listeners,
  198. documentEvents: documentEvents.listeners,
  199. selfEvents: selfEvents.listeners,
  200. };
  201. }
  202. async function listenerGetFile(listener) {
  203. let ret = await chrome.debugger.sendCommand(
  204. { tabId },
  205. "Debugger.getScriptSource",
  206. {
  207. scriptId: listener.scriptId,
  208. }
  209. );
  210. let js = jslist.find((m) => m.data == ret.scriptSource);
  211. if (js) {
  212. js.scriptId = listener.scriptId;
  213. listener.fileName = js.fileName;
  214. listener.fileUrl = js.url;
  215. listener.fileData = js.data;
  216. }
  217. }
  218. function translateEventData(eventObj) {
  219. let { winEvents, documentEvents, selfEvents } = eventObj;
  220. let result = [];
  221. result.push(
  222. ...winEvents.map((even) => ({
  223. 事件名称: even.type,
  224. 绑定对象: "window",
  225. 事件文件: even.fileName,
  226. }))
  227. );
  228. result.push(
  229. ...documentEvents.map((even) => ({
  230. 事件名称: even.type,
  231. 绑定对象: "document",
  232. 事件文件: even.fileName,
  233. }))
  234. );
  235. result.push(
  236. ...selfEvents.map((even) => ({
  237. 事件名称: even.type,
  238. 绑定对象: "selft",
  239. 事件文件: even.fileName,
  240. }))
  241. );
  242. return result.map((a, index) => ({ ...a, id: index }));
  243. }
  244. window.onload = function () {
  245. InitTabOperation();
  246. };
  247. /**
  248. * 设置操作页卡功能
  249. */
  250. function InitTabOperation() {
  251. document.querySelector(".tabs").addEventListener("click", function (e) {
  252. let target = e.target;
  253. if (target.classList.contains("tab")) {
  254. showContent(target);
  255. }
  256. });
  257. }
  258. function showContent(tab) {
  259. let id = tab.dataset.id; // 获取点击的页卡ID
  260. // 获取选中的页卡元素
  261. let activeTab = document.querySelector(".tabs .active");
  262. if (activeTab !== null) {
  263. if (id === activeTab.dataset.id) return;
  264. activeTab.classList.remove("active");
  265. }
  266. // 获取选中的容器的元素
  267. let activeContent = document.querySelector(".tab-container .active");
  268. if (activeContent !== null) {
  269. if (activeContent.id === id) return;
  270. activeContent.classList.remove("active");
  271. }
  272. // 给新选中的页卡加入选中效果
  273. tab.classList.add("active");
  274. let curActiveContent = document.querySelector(`.tab-container #${id}`);
  275. if (curActiveContent !== null) {
  276. curActiveContent.classList.add("active");
  277. }
  278. }
  279. /**
  280. * 接收转换后有用的json数据
  281. * @param {} jsonData
  282. */
  283. function CssJsonData(jsonData) {
  284. let data = CssJsonRules.parseJson(jsonData);
  285. let { 伪类的样式, 内敛的样式, 匹配的样式, 继承的样式 } = data;
  286. let result = [];
  287. 伪类的样式.forEach((rule) => {
  288. let { matchs, pseudoType } = rule;
  289. matchs.forEach(match => {
  290. result.push({
  291. 文件: match.origin,
  292. 样式属性: match.style,
  293. 选择器: match.text,
  294. 分类: "伪类样式"
  295. })
  296. })
  297. });
  298. if (内敛的样式.length > 0) {
  299. result.push({
  300. 文件: "用户自定义",
  301. 样式属性: 内敛的样式,
  302. 选择器: "",
  303. 分类: "内敛样式"
  304. })
  305. }
  306. 匹配的样式.forEach((rule) => {
  307. result.push({
  308. 文件: rule.origin,
  309. 样式属性: rule.style,
  310. 选择器: rule.text,
  311. 分类: "外敛样式"
  312. })
  313. })
  314. 继承的样式.forEach((rule) => {
  315. result.push({
  316. 文件: rule.origin,
  317. 样式属性: rule.style,
  318. 选择器: rule.text,
  319. 分类: "继承样式"
  320. })
  321. })
  322. return result.map((a, index) => ({ ...a, id: index }));
  323. }
  324. /**
  325. * css基础权重规则
  326. */
  327. class CSSSRule {
  328. static calculateSpecificity(selector) {
  329. // 初始化权重
  330. let ids = 0;
  331. let classes = 0;
  332. let tags = 0;
  333. // 去掉多余的空格
  334. const parts = selector.trim().split(/\s+/);
  335. parts.forEach(part => {
  336. // 检查每个选择器部分
  337. if (part.startsWith('#')) {
  338. ids += 1; // ID选择器
  339. } else if (part.startsWith('.')) {
  340. classes += 1; // 类选择器
  341. } else {
  342. tags += 1; // 标签选择器
  343. }
  344. });
  345. // 返回权重对象
  346. // 计算最终权重值
  347. const specificityValue = (ids * 100) + (classes * 10) + tags;
  348. return specificityValue;
  349. }
  350. static cacheStyle = {}
  351. static getStyleHref(styleId, selectorText) {
  352. if (CSSSRule.cacheStyle[styleId]) return CSSSRule.cacheStyle[styleId];
  353. foo: for (let sheet of cssList) {
  354. try {
  355. let { href, selectorTexts } = sheet;
  356. for (let text of selectorTexts)
  357. if (selectorText === text) {
  358. CSSSRule.cacheStyle[styleId] = href;
  359. break foo;
  360. }
  361. } catch { }
  362. }
  363. return CSSSRule.cacheStyle[styleId]
  364. }
  365. // css属性扩展
  366. static cssExtent = [
  367. {
  368. "property": "margin",
  369. "sub-properties": [
  370. "margin-top",
  371. "margin-right",
  372. "margin-bottom",
  373. "margin-left"
  374. ]
  375. },
  376. {
  377. "property": "padding",
  378. "sub-properties": [
  379. "padding-top",
  380. "padding-right",
  381. "padding-bottom",
  382. "padding-left"
  383. ]
  384. },
  385. {
  386. "property": "border",
  387. "sub-properties": [
  388. "border-width",
  389. "border-style",
  390. "border-color",
  391. "border-top",
  392. "border-right",
  393. "border-bottom",
  394. "border-left"
  395. ]
  396. },
  397. {
  398. "property": "border-style",
  399. "sub-properties": [
  400. "border-top-style",
  401. "border-right-style",
  402. "border-bottom-style",
  403. "border-left-style"
  404. ]
  405. },
  406. {
  407. "property": "border-top",
  408. "sub-properties": [
  409. "border-top-width",
  410. "border-top-style",
  411. "border-top-color"
  412. ]
  413. },
  414. {
  415. "property": "border-bottom",
  416. "sub-properties": [
  417. "border-bottom-width",
  418. "border-bottom-style",
  419. "border-bottom-color"
  420. ]
  421. },
  422. {
  423. "property": "border-left",
  424. "sub-properties": [
  425. "border-left-width",
  426. "border-left-style",
  427. "border-left-color"
  428. ]
  429. },
  430. {
  431. "property": "border-right",
  432. "sub-properties": [
  433. "border-right-width",
  434. "border-right-style",
  435. "border-right-color"
  436. ]
  437. },
  438. {
  439. "property": "border-radius",
  440. "sub-properties": [
  441. "border-top-left-radius",
  442. "border-top-right-radius",
  443. "border-bottom-right-radius",
  444. "border-bottom-left-radius"
  445. ]
  446. },
  447. {
  448. "property": "background",
  449. "sub-properties": [
  450. "background-color",
  451. "background-image",
  452. "background-repeat",
  453. "background-position",
  454. "background-size",
  455. "background-attachment",
  456. "background-clip",
  457. "background-origin"
  458. ]
  459. },
  460. {
  461. "property": "font",
  462. "sub-properties": [
  463. "font-style",
  464. "font-variant",
  465. "font-weight",
  466. "font-size",
  467. "line-height",
  468. "font-family"
  469. ]
  470. },
  471. {
  472. "property": "list-style",
  473. "sub-properties": [
  474. "list-style-type",
  475. "list-style-position",
  476. "list-style-image"
  477. ]
  478. },
  479. {
  480. "property": "text-decoration",
  481. "sub-properties": [
  482. "text-decoration-line",
  483. "text-decoration-color",
  484. "text-decoration-style",
  485. "text-decoration-thickness"
  486. ]
  487. },
  488. {
  489. "property": "transition",
  490. "sub-properties": [
  491. "transition-property",
  492. "transition-duration",
  493. "transition-timing-function",
  494. "transition-delay"
  495. ]
  496. },
  497. {
  498. "property": "animation",
  499. "sub-properties": [
  500. "animation-name",
  501. "animation-duration",
  502. "animation-timing-function",
  503. "animation-delay",
  504. "animation-iteration-count",
  505. "animation-direction",
  506. "animation-fill-mode",
  507. "animation-play-state"
  508. ]
  509. },
  510. {
  511. "property": "grid",
  512. "sub-properties": [
  513. "grid-template-rows",
  514. "grid-template-columns",
  515. "grid-template-areas",
  516. "grid-area",
  517. "grid-column",
  518. "grid-row",
  519. "grid-auto-rows",
  520. "grid-auto-columns",
  521. "grid-auto-flow"
  522. ]
  523. },
  524. {
  525. "property": "flex",
  526. "sub-properties": [
  527. "flex-grow",
  528. "flex-shrink",
  529. "flex-basis"
  530. ]
  531. },
  532. {
  533. "property": "outline",
  534. "sub-properties": [
  535. "outline-width",
  536. "outline-style",
  537. "outline-color"
  538. ]
  539. }
  540. ]
  541. }
  542. /**
  543. * 根据json导出数据
  544. */
  545. class CssJsonRules {
  546. static findExtentInfo(entries) {
  547. return entries.map(e => {
  548. let extentInfo = CSSSRule.cssExtent.find(a => a.property === e.name);
  549. if (extentInfo) {
  550. return extentInfo["sub-properties"];
  551. }
  552. return undefined
  553. }).filter(Boolean).flat();
  554. }
  555. /**
  556. * 解析json数据
  557. * @param {*} jsonData
  558. * @returns
  559. */
  560. static parseJson(jsonData) {
  561. let { inlineStyle, matchedCSSRules, pseudoElements, inherited } = jsonData;
  562. let result = {
  563. "内敛的样式": [],
  564. "匹配的样式": [],
  565. "伪类的样式": [],
  566. "继承的样式": []
  567. }
  568. // 解析json中的内敛样式
  569. result.内敛的样式.push(...inlineStyle.cssProperties.filter(a => a.text !== undefined).map(({ name, value }) => ({ name, value })));
  570. // 解析json中的匹配样式
  571. matchedCSSRules.forEach(cssRule => {
  572. let { rule } = cssRule;
  573. let { shorthandEntries } = rule;
  574. // 按照规律查询出信息
  575. let style = rule.style.cssProperties.filter(a => a.text !== undefined || rule.origin === "user-agent").map(({ name, value }) => ({ name, value }));
  576. if (rule.origin === "user-agent" && shorthandEntries && shorthandEntries.length > 0) {
  577. let sameInfo = CssJsonRules.findExtentInfo(shorthandEntries);
  578. style = style.filter(s => !sameInfo.includes(s.name))
  579. }
  580. let text = rule.selectorList.text;
  581. let obj = {
  582. origin: rule.styleSheetId ? CSSSRule.getStyleHref(rule.styleSheetId, text) : '用户自定义',
  583. text,
  584. style
  585. };
  586. result.匹配的样式.push(obj)
  587. })
  588. // 解析伪类的样式
  589. pseudoElements.forEach(cssRule => {
  590. let { pseudoType, matches } = cssRule;
  591. let obj = {
  592. pseudoType,
  593. matchs: matches.map(({ rule }) => {
  594. let text = rule.selectorList.text;
  595. return {
  596. origin: rule.styleSheetId ? CSSSRule.getStyleHref(rule.styleSheetId, text) : '用户自定义',
  597. text,
  598. style: rule.style.cssProperties.filter(a => a.text !== undefined).map(({ name, value }) => ({ name, value }))
  599. }
  600. })
  601. };
  602. result.伪类的样式.push(obj)
  603. })
  604. // 解析继承的样式
  605. inherited.forEach(inheritedCssRule => {
  606. let matchedCSSRules = inheritedCssRule.matchedCSSRules;
  607. matchedCSSRules.forEach((cssRule) => {
  608. let { rule } = cssRule;
  609. let style = rule.style.cssProperties.filter(a => a.text !== undefined).map(({ name, value }) => ({ name, value }));
  610. if (style.length === 0) return;
  611. let text = rule.selectorList.text;
  612. result.继承的样式.push({
  613. origin: rule.styleSheetId ? CSSSRule.getStyleHref(rule.styleSheetId, text) : '用户自定义',
  614. text,
  615. style
  616. })
  617. })
  618. })
  619. return result;
  620. }
  621. }
  622. /**
  623. * 页卡组件
  624. */
  625. class PageTab {
  626. #tabs = []
  627. #tabContainer
  628. constructor({ tabs }) {
  629. this.#tabs = tabs || [];
  630. this.#tabContainer = document.querySelector('.tabs');
  631. this.#initClickEvent();
  632. }
  633. addTab(tabInfo) {
  634. this.#tabs.push(tabInfo)
  635. }
  636. #initClickEvent() {
  637. this.#tabContainer.addEventListener('click', (e) => {
  638. let target = e.target;
  639. if (target.classList.contains("tab")) {
  640. this.activeTabById(target.dataset.id);
  641. }
  642. })
  643. }
  644. get tabs() {
  645. return this.#tabs;
  646. }
  647. activeTabById(id) {
  648. // 移除已经选中的
  649. let activedTab = this.tabs.find(a => a.isActive);
  650. if (activedTab) {
  651. activedTab.removeActive();
  652. }
  653. // 给设置的选中
  654. let awaitActiveTab = this.tabs.find(a => a.id === id);
  655. if (awaitActiveTab) {
  656. awaitActiveTab.addActive();
  657. }
  658. }
  659. reset() {
  660. this.tabs.forEach(s => { s.changeLived(false) })
  661. }
  662. }
  663. class Tab {
  664. #id // 页卡id
  665. #tabEle // tab页卡元素
  666. #contentEle // tab内容元素
  667. #isAlived // 是否保持持久化(为true表示 页卡激活事件只执行一次,当前内容会一直存在,如果为false,则表示每次点击页卡都会重新执行激活事件)
  668. #event // 激活页卡执行事件
  669. #isActived // 是否选中
  670. #isLived
  671. get id() {
  672. return this.#id;
  673. }
  674. constructor({ id, isAlived, activeEvent }) {
  675. this.#id = id;
  676. this.#tabEle = Tab.getTabEleById(id);
  677. this.#contentEle = Tab.getTabContentById(id);
  678. this.#isAlived = isAlived;
  679. this.#isActived = false;
  680. this.#event = activeEvent;
  681. }
  682. get tabInfo() {
  683. return {
  684. isActive: this.isActive(),
  685. id: this.#id
  686. }
  687. }
  688. /**
  689. * 判断是否为选中的标签
  690. */
  691. isActive() {
  692. return this.#isActived;
  693. }
  694. /**
  695. * 添加选中效果
  696. */
  697. addActive() {
  698. this.#tabEle.classList.add('active');
  699. this.#contentEle.classList.add('active');
  700. this.#isActived = true;
  701. this.activeEvent();
  702. this.#isLived = true;
  703. }
  704. activeEvent() {
  705. if (this.#isAlived === true && this.#isLived === true) {
  706. // 如果开启了持久化,表示只执行一次
  707. return
  708. }
  709. typeof this.#event === 'function' && this.#event();
  710. }
  711. changeLived(val) {
  712. this.#isLived = val
  713. }
  714. /**
  715. * 移除选中效果
  716. */
  717. removeActive() {
  718. this.#tabEle.classList.remove('active');
  719. this.#contentEle.classList.remove('active');
  720. this.#isActived = false;
  721. }
  722. static getTabEleById(id) {
  723. return document.querySelector(`.tabs .tab[data-id="${id}"]`);
  724. }
  725. static getTabContentById(id) {
  726. return document.querySelector(`.tab-container #${id}`);
  727. }
  728. }
  729. /**
  730. * 执行DOM操作页卡事件
  731. */
  732. async function domOptionTab() {
  733. //初始化JS代码编辑器
  734. let jsCode = new CodeEditor();
  735. jsCode.InitEditor($("#jscode")[0], false, false, [], false);
  736. jsCode.dataType = 3;
  737. // 初始化表格
  738. let TabInstance = $("#filterTable")
  739. .dxDataGrid({
  740. dataSource: [],
  741. keyExpr: "resource_detail_id",
  742. remoteOperations: false,
  743. searchPanel: {
  744. visible: true,
  745. highlightCaseSensitive: true,
  746. },
  747. groupPanel: { visible: false },
  748. grouping: {
  749. allowCollapsing: true,
  750. autoExpandAll: true,
  751. expandMode: "rowClick",
  752. },
  753. selection: {
  754. mode: "single",
  755. },
  756. allowColumnReordering: true,
  757. rowAlternationEnabled: false,
  758. noDataText: "无数据",
  759. showBorders: true,
  760. height: "100%",
  761. columns: [
  762. {
  763. dataField: "JS文件",
  764. dataType: "JS文件",
  765. groupIndex: 0
  766. },
  767. {
  768. dataField: "DOM操作分类",
  769. dataType: "DOM操作分类",
  770. },
  771. {
  772. dataField: "过滤器",
  773. caption: "过滤器",
  774. },
  775. {
  776. dataField: "所在位置",
  777. dataType: "所在位置",
  778. },
  779. ],
  780. onContentReady(e) { },
  781. onSelectionChanged: function (selectedItems) {
  782. let data = selectedItems.selectedRowsData[0];
  783. if (data) {
  784. oldFile = "";
  785. if (data.JS文件 !== nowFileName) {
  786. nowFileName = data.JS文件;
  787. let code = window.fileList.find((o) => o.name === data.JS文件).code;
  788. //更改JS代码
  789. jsCode.SetCode(code);
  790. }
  791. //jscode定位
  792. let {
  793. defineNode: {
  794. iid,
  795. loc: { end, start },
  796. },
  797. } = data.source_data;
  798. {
  799. //移出所有高亮
  800. jsCode.RemoveAllHighlight();
  801. //设置高亮
  802. jsCode.CreateHighlight({
  803. startLine: start?.line,
  804. startColumn: start?.column + 1,
  805. endLine: end?.line,
  806. endColumn: end?.column + 1,
  807. });
  808. }
  809. }
  810. },
  811. })
  812. .dxDataGrid("instance");
  813. parseCodeToAST(
  814. jslist.map((item) => {
  815. return {
  816. code: item.data,
  817. name: item.fileName,
  818. };
  819. })
  820. );
  821. let data = window.findSelector();
  822. let id = $(targe).attr("id");
  823. let classList = $(targe).attr("class").split(" ");
  824. data = data.filter((m) => {
  825. let v = m.过滤器;
  826. if (v.includes(id)) return true;
  827. if (classList.length > 0) {
  828. for (const classitem of classList) {
  829. if (v.includes(classitem)) return true;
  830. }
  831. }
  832. });
  833. TabInstance.option({
  834. dataSource: data,
  835. });
  836. }
  837. /**
  838. * 执行DOM属性页卡事件
  839. */
  840. async function domPropTab() {
  841. let cssCode = new CodeEditor();
  842. cssCode.InitEditor($("#csscode")[0], false, false, [], false);
  843. cssCode.dataType = 2;
  844. let CSSTabInstance = $("#cssJson")
  845. .dxDataGrid({
  846. dataSource: [],
  847. keyExpr: "id",
  848. remoteOperations: false,
  849. searchPanel: {
  850. visible: true,
  851. highlightCaseSensitive: true,
  852. },
  853. selection: {
  854. mode: "single",
  855. },
  856. groupPanel: { visible: false },
  857. grouping: {
  858. allowCollapsing: true,
  859. autoExpandAll: true,
  860. expandMode: "rowClick",
  861. },
  862. allowColumnReordering: true,
  863. rowAlternationEnabled: true,
  864. showBorders: true,
  865. height: "100%",
  866. paging: {
  867. enabled: false,
  868. pageSize: 0,
  869. },
  870. columns: [
  871. {
  872. dataField: "文件",
  873. caption: "来源文件",
  874. groupCellTemplate: function (tdom, tdMsg) {
  875. let name = tdMsg.value;
  876. try {
  877. name = (name === "用户自定义" ? name : name.match(/\/([^\/]+\.css)(\?[^ ]*)?$/)[1])
  878. } catch {
  879. }
  880. tdom.text(name)
  881. },
  882. groupIndex: 0
  883. },
  884. {
  885. dataField: "分类",
  886. caption: "分类"
  887. },
  888. {
  889. dataField: "选择器",
  890. caption: "选择器"
  891. }, {
  892. dataField: "样式属性",
  893. caption: "样式属性",
  894. cellTemplate: function (tdom, tdMsg) {
  895. let styles = tdMsg.value;
  896. let html = styles.map(({ name, value }) => `<li>${name}: ${value}</li>`).join('')
  897. tdom.html(`<ul>${html}</ul>`)
  898. }
  899. },
  900. ],
  901. onSelectionChanged: async function (selectedItems) {
  902. let data = selectedItems.selectedRowsData[0];
  903. if (data) {
  904. if (data.文件) {
  905. let fileName = data.文件;
  906. let code = ""
  907. if (!cssFileInfo[fileName]) {
  908. code = fileName === "用户自定义" ? "" : await (await fetch(fileName)).text();
  909. cssFileInfo[fileName] = {
  910. code
  911. }
  912. } else {
  913. code = cssFileInfo[fileName].code
  914. }
  915. // 设置显示css
  916. cssCode.dataType = 2;
  917. if (oldFile !== fileName) {
  918. //更改JS代码
  919. cssCode.SetCode(code);
  920. }
  921. //移出所有高亮
  922. cssCode.RemoveAllHighlight();
  923. oldFile = fileName;
  924. // 定位使用
  925. let selectors = [];
  926. if (!cssFileInfo[fileName].selectors) {
  927. selectors = await cssCodeToAST(cssCode.GetCode());
  928. cssFileInfo[fileName].selectors = selectors
  929. } else {
  930. selectors = cssFileInfo[fileName].selectors
  931. }
  932. let location = selectors.find(a => a.name === data.选择器);
  933. if (location) {
  934. setTimeout(() => {
  935. let { loc: { start, end } } = location;
  936. //设置高亮
  937. cssCode.CreateHighlight({
  938. startLine: start?.line,
  939. startColumn: start?.column,
  940. endLine: end?.line,
  941. endColumn: end?.column,
  942. });
  943. }, 500);
  944. }
  945. }
  946. }
  947. },
  948. })
  949. .dxDataGrid("instance");
  950. let cssInfo = await getMatchedStylesForNode(nodeId);
  951. let cssJsonData = CssJsonData(cssInfo)
  952. CSSTabInstance.option({
  953. dataSource: cssJsonData,
  954. });
  955. }
  956. /**
  957. * 执行冒泡事件页卡事件
  958. */
  959. async function domEventTab() {
  960. let eventTableInstance = $("#eventTable")
  961. .dxDataGrid({
  962. dataSource: [],
  963. keyExpr: "id",
  964. remoteOperations: false,
  965. searchPanel: {
  966. visible: true,
  967. highlightCaseSensitive: true,
  968. },
  969. selection: {
  970. mode: "single",
  971. },
  972. allowColumnReordering: true,
  973. rowAlternationEnabled: false,
  974. noDataText: "无数据",
  975. showBorders: true,
  976. paging: {
  977. enabled: false,
  978. pageSize: 0,
  979. },
  980. height: "100%",
  981. columns: [
  982. {
  983. dataField: "事件名称",
  984. caption: "事件名称",
  985. },
  986. {
  987. dataField: "绑定对象",
  988. caption: "绑定对象",
  989. },
  990. {
  991. dataField: "事件文件",
  992. caption: "事件文件",
  993. },
  994. ],
  995. })
  996. .dxDataGrid("instance");
  997. let events = await findEvent();
  998. eventTableInstance.option({
  999. dataSource: translateEventData(events),
  1000. });
  1001. }