page.js 27 KB

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