套壳油猴的广告拦截脚本

将 ABP 中的元素隐藏规则转换为 CSS 使用

当前为 2023-05-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AdBlock Script for WebView
  3. // @name:zh-CN 套壳油猴的广告拦截脚本
  4. // @author Lemon399
  5. // @version 2.5.4.1
  6. // @description Parse ABP Cosmetic rules to CSS and apply it.
  7. // @description:zh-CN 将 ABP 中的元素隐藏规则转换为 CSS 使用
  8. // @resource jiekouAD https://raw.fastgit.ixmu.net/damengzhu/banad/main/jiekouAD.txt
  9. // @resource CSSRule https://raw.fastgit.ixmu.net/damengzhu/abpmerge/main/CSSRule.txt
  10. // @match https://*/*
  11. // @match http://*/*
  12. // @run-at document-start
  13. // @grant unsafeWindow
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_getValue
  17. // @grant GM_deleteValue
  18. // @grant GM_setValue
  19. // @grant GM_addStyle
  20. // @grant GM_xmlhttpRequest
  21. // @grant GM_getResourceText
  22. // @namespace https://lemon399-bitbucket-io.vercel.app/
  23. // @source https://gitee.com/lemon399/tampermonkey-cli/tree/master/projects/abp_parse
  24. // @source https://bitbucket.org/lemon399/tampermonkey-cli/src/master/projects/abp_parse/
  25. // @connect raw.fastgit.ixmu.net
  26. // @copyright GPL-3.0
  27. // @license GPL-3.0
  28. // ==/UserScript==
  29.  
  30. /* ==UserConfig==
  31. 配置:
  32. css:
  33. title: 隐藏 CSS 规则
  34. description: 隐藏广告使用的 CSS 规则
  35. type: text
  36. default: " {display: none !important;width: 0 !important;height: 0 !important;} "
  37. timeout:
  38. title: 规则下载超时
  39. description: 更新规则时,规则下载超时时间
  40. type: number
  41. default: 10000
  42. min: 0
  43. unit: 毫秒
  44. tryCount:
  45. title: CSS 注入尝试次数
  46. description: 某些框架会重建页面,需要多次注入,只有检测到 CSS 不存在时才会尝试再次注入
  47. type: number
  48. default: 5
  49. min: 0
  50. unit: 次
  51. tryTimeout:
  52. title: CSS 注入尝试间隔
  53. description: 两次注入尝试的间隔时间
  54. type: number
  55. default: 500
  56. min: 100
  57. unit: 毫秒
  58. ==/UserConfig== */
  59.  
  60. /* eslint-disable no-undef */
  61.  
  62. (function (cat) {
  63. "use strict";
  64.  
  65. function __awaiter(thisArg, _arguments, P, generator) {
  66. function adopt(value) {
  67. return value instanceof P
  68. ? value
  69. : new P(function (resolve) {
  70. resolve(value);
  71. });
  72. }
  73. return new (P || (P = Promise))(function (resolve, reject) {
  74. function fulfilled(value) {
  75. try {
  76. step(generator.next(value));
  77. } catch (e) {
  78. reject(e);
  79. }
  80. }
  81. function rejected(value) {
  82. try {
  83. step(generator["throw"](value));
  84. } catch (e) {
  85. reject(e);
  86. }
  87. }
  88. function step(result) {
  89. result.done
  90. ? resolve(result.value)
  91. : adopt(result.value).then(fulfilled, rejected);
  92. }
  93. step((generator = generator.apply(thisArg, _arguments || [])).next());
  94. });
  95. }
  96.  
  97. const userConfig = {
  98. css: " {display: none !important;width: 0 !important;height: 0 !important;} ",
  99. timeout: 10000,
  100. tryCount: 5,
  101. tryTimeout: 500,
  102. },
  103. defaultRules = `
  104. ! 不支持的规则和开头为 ! 的行会忽略
  105. !
  106. ! 由于语法限制,此处规则中
  107. ! 一个反斜杠需要改成两个,像这样 \\
  108.  
  109. `,
  110. onlineRules = [
  111. ...[
  112. {
  113. 标识: "jiekouAD",
  114. 地址: "https://raw.fastgit.ixmu.net/damengzhu/banad/main/jiekouAD.txt",
  115. 在线更新: !!1,
  116. 筛选后存储: !!1,
  117. },
  118. {
  119. 标识: "CSSRule",
  120. 地址: "https://raw.fastgit.ixmu.net/damengzhu/abpmerge/main/CSSRule.txt",
  121. 在线更新: !!1,
  122. 筛选后存储: !!0,
  123. },
  124. ],
  125. ...[],
  126. ];
  127.  
  128. const CRRE =
  129. /^(\[\$domain=)?(~?[\w-]+(?:\.[\w-]+)*(?:\.[\w-]+|\.\*)(?:(?:,|\|)~?[\w-]+(?:\.[\w-]+)*(?:\.[\w-]+|\.\*))*)?(?:])?(#@?\$?\??#)([^\s^+].*)/,
  130. BRRE =
  131. /^(?:@@?)(?:\/(.*[^\\])\/|(\|\|?)?(https?:\/\/)?([^\s"<>`]+?[|^]?))?\$((?:(?:~?[\w-]+(?:=[^$]+)?|_+)(?:[^\\],|$))+)/,
  132. CCRE =
  133. /^\/\*\s(\d)(\|)?(.+?)\s\*\/\s((.+?)\s*{\s*[a-zA-Z-]+\s*:\s*.+})\s*$/,
  134. BROpts = [
  135. "elemhide",
  136. "ehide",
  137. "specifichide",
  138. "shide",
  139. "generichide",
  140. "ghide",
  141. ],
  142. CRFlags = ["##", "#@#", "#?#", "#@?#", "#$#", "#@$#", "#$?#", "#@$?#"],
  143. styleBoxes = ["genHideCss", "genExtraCss", "spcHideCss", "spcExtraCss"],
  144. dataBoxes = ["selectors", "extSelectors", "styles", "extStyles"];
  145. function bRuleSpliter(rule) {
  146. const group = rule.match(BRRE);
  147. if (!group) return null;
  148. const [, regex, pipe, proto, body, option] = group,
  149. options = option.split(","),
  150. sepChar = "[^\\w\\.%-]",
  151. anyChar = '(?:[^\\s"<>`]*)',
  152. eh = hasSome(options, ["elemhide", "ehide"]),
  153. sh = hasSome(options, ["specifichide", "shide"]),
  154. gh = hasSome(options, ["generichide", "ghide"]);
  155. let domains = [];
  156. options.forEach((opt) => {
  157. if (opt.startsWith("domain=")) {
  158. domains = opt.split("=")[1].split("|");
  159. }
  160. });
  161. let match = "";
  162. if (regex) {
  163. match = regex;
  164. } else if (body) {
  165. match += pipe
  166. ? proto
  167. ? `^${proto}`
  168. : `^https?://(?:[\\w-]+\\.)*?`
  169. : `^${anyChar}`;
  170. match += body
  171. .replace(/[-\\$+.()[\]{}]/g, "\\$&")
  172. .replace(/\^/g, "$^")
  173. .replace(/\|$/, "$")
  174. .replace(/\|/g, "\\|")
  175. .replace(/\*$/g, "")
  176. .replace(/\*/g, anyChar)
  177. .replace(/\$\^$/, `(?:${sepChar}.*|$)`)
  178. .replace(/\$\^/g, sepChar);
  179. } else if (domains.length > 0) {
  180. match = domains;
  181. }
  182. return {
  183. rule,
  184. match,
  185. level: eh || (gh && sh) ? 3 : sh ? 2 : gh ? 1 : 0,
  186. };
  187. }
  188. function isBasicRule(rule) {
  189. return BRRE.test(rule) && hasSome(rule, BROpts);
  190. }
  191. function bRuleParser(rule, url = location.href) {
  192. return rule
  193. ? (Array.isArray(rule.match) && domainChecker(rule.match)[0]) ||
  194. (!Array.isArray(rule.match) && new RegExp(rule.match).test(url))
  195. ? rule.level
  196. : 0
  197. : 0;
  198. }
  199. function getEtag(header) {
  200. var _a;
  201. let result = null;
  202. if (!header) return null;
  203. [
  204. /[e|E][t|T]ag:\s(?:W\/)?"(\w+)"/,
  205. // WebMonkey 系
  206. /[e|E][t|T]ag:\s\[(?:W\/)?"(\w+)"\]/,
  207. // 书签地球
  208. /[e|E][t|T]ag=(?:W\/)?"(\w+)"/,
  209. // 海阔世界
  210. /^(?:W\/)?"(\w+)"/,
  211. ].forEach((re) => {
  212. result !== null && result !== void 0
  213. ? result
  214. : (result = header.match(re));
  215. });
  216. return (_a = result === null || result === void 0 ? void 0 : result[1]) !==
  217. null && _a !== void 0
  218. ? _a
  219. : null;
  220. }
  221. function extrEtag(resp) {
  222. var _a, _b, _c;
  223. const etag = getEtag(
  224. typeof (resp === null || resp === void 0 ? void 0 : resp.headers) ==
  225. "object"
  226. ? // 海阔世界
  227. (_b =
  228. (_a = resp === null || resp === void 0 ? void 0 : resp.headers) ===
  229. null || _a === void 0
  230. ? void 0
  231. : _a.etag) === null || _b === void 0
  232. ? void 0
  233. : _b[0]
  234. : typeof (resp === null || resp === void 0
  235. ? void 0
  236. : resp.responseHeaders) == "string"
  237. ? // Tampermonkey
  238. resp === null || resp === void 0
  239. ? void 0
  240. : resp.responseHeaders
  241. : // Appara
  242. (_c =
  243. resp === null || resp === void 0
  244. ? void 0
  245. : resp.getAllResponseHeaders) === null || _c === void 0
  246. ? void 0
  247. : _c.call(resp)
  248. );
  249. return etag;
  250. }
  251. function makeRuleBox() {
  252. return {
  253. black: [],
  254. white: [],
  255. };
  256. }
  257. function domainChecker(domains) {
  258. const results = [],
  259. invResults = [],
  260. currDomain = location.hostname,
  261. urlSuffix = /\.+?[\w-]+$/.exec(currDomain);
  262. let totalResult = [0, false],
  263. black = false,
  264. white = false,
  265. match = false;
  266. domains.forEach((domain) => {
  267. const invert = domain[0] === "~";
  268. if (invert) domain = domain.slice(1);
  269. if (domain.endsWith(".*") && Array.isArray(urlSuffix)) {
  270. domain = domain.replace(".*", urlSuffix[0]);
  271. }
  272. const result = currDomain.endsWith(domain);
  273. if (invert) {
  274. if (result) white = true;
  275. invResults.push([domain.length, !result]);
  276. } else {
  277. if (result) black = true;
  278. results.push([domain.length, result]);
  279. }
  280. });
  281. if (results.length > 0 && !black) {
  282. match = false;
  283. } else if (invResults.length > 0 && !white) {
  284. match = true;
  285. } else {
  286. results.forEach((r) => {
  287. if (r[0] >= totalResult[0] && r[1]) {
  288. totalResult = r;
  289. }
  290. });
  291. invResults.forEach((r) => {
  292. if (r[0] >= totalResult[0] && !r[1]) {
  293. totalResult = r;
  294. }
  295. });
  296. match = totalResult[1];
  297. }
  298. return [match, results.length === 0];
  299. }
  300. function hasSome(str, arr) {
  301. return arr.some((word) => str.includes(word));
  302. }
  303. function ruleSpliter(rule) {
  304. const group = rule.match(CRRE);
  305. if (group) {
  306. const [, isDomain, place = "*", flag, sel] = group,
  307. type = CRFlags.indexOf(flag),
  308. matchResult =
  309. place === "*"
  310. ? [true, true]
  311. : domainChecker(place.split(isDomain ? "|" : ","));
  312. if (sel && matchResult[0]) {
  313. return {
  314. black: type % 2 ? "white" : "black",
  315. type: Math.floor(type / 2),
  316. place: (isDomain ? "|" : "") + place,
  317. generic: matchResult[1],
  318. sel,
  319. };
  320. }
  321. }
  322. }
  323. function ruleLoader(rule) {
  324. if (
  325. hasSome(rule, [
  326. ":matches-path(",
  327. ":min-text-length(",
  328. ":watch-attr(",
  329. ":-abp-properties(",
  330. ":matches-property(",
  331. ])
  332. )
  333. return;
  334. // 去掉开头末尾空格
  335. rule = rule.trim();
  336. // 如果 #$# 不包含 {} 就排除
  337. // 可以尽量排除 Snippet Filters
  338. if (
  339. /(?:\w|\*|]|^)#\$#/.test(rule) &&
  340. !/{\s*[a-zA-Z-]+\s*:\s*.+}\s*$/.test(rule)
  341. )
  342. return;
  343. // ## -> #?#
  344. if (
  345. /(?:\w|\*|]|^)#@?\$?#/.test(rule) &&
  346. hasSome(rule, [
  347. ":has(",
  348. ":-abp-has(",
  349. "[-ext-has=",
  350. ":has-text(",
  351. ":contains(",
  352. ":-abp-contains(",
  353. "[-ext-contains=",
  354. ":matches-css(",
  355. "[-ext-matches-css=",
  356. ":matches-css-before(",
  357. "[-ext-matches-css-before=",
  358. ":matches-css-after(",
  359. "[-ext-matches-css-after=",
  360. ":matches-attr(",
  361. ":nth-ancestor(",
  362. ":upward(",
  363. ":xpath(",
  364. ":remove()",
  365. ":not(",
  366. ])
  367. ) {
  368. rule = rule.replace(/(\w|\*|]|^)#(@?\$?)#/, "$1#$2?#");
  369. }
  370. // :style(...) 转换
  371. // example.com#?##id:style(color: red)
  372. // example.com#$?##id { color: red }
  373. if (rule.includes(":style(")) {
  374. rule = rule
  375. .replace(/(\w|\*|]|^)#(@?)(\??)#/, "$1#$2$$$3#")
  376. .replace(/:style\(\s*/, " { ")
  377. .replace(/\s*\)$/, " }");
  378. }
  379. return ruleSpliter(rule);
  380. }
  381. function ruleToCss(rule, preset) {
  382. var _a, _b;
  383. const isStyle = /}\s*$/.test(rule.sel);
  384. return [
  385. `/* ${rule.type}${rule.place} */ ${
  386. rule.sel + (!isStyle ? preset : "")
  387. } \n`,
  388. isStyle
  389. ? (_b =
  390. (_a = rule.sel.match(/^(.+?)\s*{\s*[a-zA-Z-]+\s*:\s*.+}\s*$/)) ===
  391. null || _a === void 0
  392. ? void 0
  393. : _a[1]) !== null && _b !== void 0
  394. ? _b
  395. : rule.sel
  396. : rule.sel,
  397. ];
  398. }
  399. function cssToAbp(css) {
  400. var _a;
  401. const flags = ["##", "#?#", "#$#", "#$?#"];
  402. const [, typeStr, isDomain, place, sel, style] =
  403. (_a = css.match(CCRE)) !== null && _a !== void 0 ? _a : [];
  404. if (typeStr === undefined) return null;
  405. const type = parseInt(typeStr);
  406. return [
  407. `${place === "*" ? "" : isDomain ? `[$domain=${place}]` : place}${
  408. flags[type]
  409. }${type >= 2 ? sel : style}`,
  410. type,
  411. style,
  412. ];
  413. }
  414. function downUrl(url, name) {
  415. const a = document.createElement("a");
  416. a.href = url;
  417. a.download = name;
  418. Object.assign(a.style, {
  419. position: "fixed",
  420. top: "200%",
  421. });
  422. document.body.appendChild(a);
  423. setTimeout(() => {
  424. a.click();
  425. a.remove();
  426. }, 0);
  427. }
  428.  
  429. var _a, _b, _c;
  430. const emptyStyle = {
  431. needUpdate: true,
  432. genHideCss: "",
  433. genExtraCss: "",
  434. spcHideCss: "",
  435. spcExtraCss: "",
  436. };
  437. const values = {
  438. get black() {
  439. const arrStr = gmValue("get", false, "ajs_disabled_domains", "");
  440. return typeof arrStr == "string" && arrStr.length > 0
  441. ? arrStr.split(",")
  442. : [];
  443. },
  444. set black(v) {
  445. gmValue(
  446. "set",
  447. false,
  448. "ajs_disabled_domains",
  449. v === null || v === void 0 ? void 0 : v.join()
  450. );
  451. },
  452. get rules() {
  453. return gmValue("get", true, "ajs_saved_abprules", {});
  454. },
  455. set rules(v) {
  456. gmValue("set", true, "ajs_saved_abprules", v);
  457. },
  458. get css() {
  459. return gmValue(
  460. "get",
  461. true,
  462. `ajs_saved_styles_${location.hostname}`,
  463. emptyStyle
  464. );
  465. },
  466. set css(v) {
  467. gmValue("set", true, `ajs_saved_styles_${location.hostname}`, v);
  468. },
  469. get hasSave() {
  470. const arrStr = gmValue("get", false, "ajs_hasSave_domains", "");
  471. return typeof arrStr == "string" && arrStr.length > 0
  472. ? arrStr.split(",")
  473. : [];
  474. },
  475. set hasSave(v) {
  476. gmValue(
  477. "set",
  478. false,
  479. "ajs_hasSave_domains",
  480. v === null || v === void 0 ? void 0 : v.join()
  481. );
  482. },
  483. get time() {
  484. return gmValue("get", false, "ajs_rules_ver", "0/0/0 0:0:0");
  485. },
  486. set time(v) {
  487. gmValue("set", false, "ajs_rules_ver", v);
  488. },
  489. get etags() {
  490. return gmValue("get", true, "ajs_rules_etags", {});
  491. },
  492. set etags(v) {
  493. gmValue("set", true, "ajs_rules_etags", v);
  494. },
  495. get brules() {
  496. return gmValue("get", true, "ajs_modifier_rules", []);
  497. },
  498. set brules(v) {
  499. gmValue("set", true, "ajs_modifier_rules", v);
  500. },
  501. get hash() {
  502. return gmValue("get", false, "ajs_custom_hash", "");
  503. },
  504. set hash(v) {
  505. gmValue("set", false, "ajs_custom_hash", v);
  506. },
  507. },
  508. menus = {
  509. disable: {
  510. id: undefined,
  511. get text() {
  512. return `在此域名${data.disabled ? "启用" : "禁用"}拦截`;
  513. },
  514. },
  515. update: {
  516. id: undefined,
  517. get text() {
  518. const time = values.time;
  519. return data.updating
  520. ? "正在更新..."
  521. : `点击更新 ${time.slice(0, 1) === "0" ? "未知时间" : time}`;
  522. },
  523. },
  524. count: {
  525. id: undefined,
  526. get text() {
  527. var _a, _b;
  528. let cssCount = "";
  529. if ((data.appliedLevel & 1) == 0)
  530. cssCount += data.genHideCss + data.genExtraCss;
  531. if ((data.appliedLevel & 2) == 0)
  532. cssCount += data.spcHideCss + data.spcExtraCss;
  533. return data.isClean
  534. ? "已清空,点击刷新重新加载规则"
  535. : `点击清空 ${
  536. data.saved
  537. ? (_b =
  538. "CSS " +
  539. ((_a = cssCount.match(/{/g)) === null || _a === void 0
  540. ? void 0
  541. : _a.length)) !== null && _b !== void 0
  542. ? _b
  543. : "未知"
  544. : "规则 " +
  545. data.appliedCount +
  546. "/" +
  547. data.allRules.split("\n").length
  548. }`;
  549. },
  550. },
  551. export: {
  552. id: undefined,
  553. text: "下载统计报告",
  554. },
  555. };
  556. const data = {
  557. disabled: values.black.includes(location.hostname),
  558. saved: false,
  559. update: true,
  560. updating: false,
  561. receivedRules: "",
  562. customRules:
  563. defaultRules +
  564. ((_c =
  565. "\n" +
  566. ((_b =
  567. (_a =
  568. cat.GM_info === null || cat.GM_info === void 0
  569. ? void 0
  570. : cat.GM_info.script) === null || _a === void 0
  571. ? void 0
  572. : _a.options) === null || _b === void 0
  573. ? void 0
  574. : _b.comment)) !== null && _c !== void 0
  575. ? _c
  576. : ""),
  577. allRules: "",
  578. genHideCss: "",
  579. genExtraCss: "",
  580. spcHideCss: "",
  581. spcExtraCss: "",
  582. selectors: makeRuleBox(),
  583. extSelectors: makeRuleBox(),
  584. styles: makeRuleBox(),
  585. extStyles: makeRuleBox(),
  586. bRules: [],
  587. appliedLevel: 0,
  588. appliedCount: 0,
  589. isFrame: cat.unsafeWindow.self !== cat.unsafeWindow.top,
  590. isClean: false,
  591. mutex: "__lemon__abp__parser__$__",
  592. preset: getUserConfig("css"),
  593. timeout: getUserConfig("timeout"),
  594. xTimeout: 1000,
  595. tryCount: getUserConfig("tryCount"),
  596. tryTimeout: getUserConfig("tryTimeout"),
  597. };
  598. function gmMenu(name, cb) {
  599. var _a;
  600. const id = (_a = menus[name].id) !== null && _a !== void 0 ? _a : undefined;
  601. if (
  602. typeof cat.GM_registerMenuCommand != "function" ||
  603. typeof cat.GM_unregisterMenuCommand != "function" ||
  604. data.isFrame
  605. )
  606. return;
  607. if (typeof id != "undefined") {
  608. cat.GM_unregisterMenuCommand(id);
  609. menus[name].id = undefined;
  610. }
  611. if (typeof cb == "function") {
  612. menus[name].id = cat.GM_registerMenuCommand(menus[name].text, cb);
  613. }
  614. }
  615. function gmValue(action, json, key, value) {
  616. switch (action) {
  617. case "get":
  618. try {
  619. const v = cat.GM_getValue(key, json ? JSON.stringify(value) : value);
  620. return json && typeof v == "string" ? JSON.parse(v) : v;
  621. } catch (error) {
  622. return value;
  623. }
  624. case "set":
  625. try {
  626. value === null || value === undefined
  627. ? cat.GM_deleteValue(key)
  628. : cat.GM_setValue(key, json ? JSON.stringify(value) : value);
  629. } catch (error) {
  630. cat.GM_deleteValue(key);
  631. }
  632. break;
  633. }
  634. }
  635. function getUserConfig(prop) {
  636. var _a;
  637. {
  638. return (_a = gmValue("get", false, `配置.${prop}`)) !== null &&
  639. _a !== void 0
  640. ? _a
  641. : userConfig[prop];
  642. }
  643. }
  644. function addStyle(css, pass = 0) {
  645. let el;
  646. if (pass >= data.tryCount) return;
  647. if (typeof cat.GM_addStyle == "function") {
  648. el = cat.GM_addStyle(css);
  649. } else {
  650. el = document.createElement("style");
  651. el.textContent = css;
  652. document.documentElement.appendChild(el);
  653. }
  654. if (!el || !document.documentElement.contains(el)) {
  655. setTimeout(() => {
  656. addStyle(css, pass + 1);
  657. }, data.tryTimeout);
  658. }
  659. }
  660. function promiseXhr(details) {
  661. return __awaiter(this, void 0, void 0, function* () {
  662. let loaded = false;
  663. return yield new Promise((resolve, reject) => {
  664. cat.GM_xmlhttpRequest(
  665. Object.assign(
  666. {
  667. onload(e) {
  668. loaded = true;
  669. resolve(e);
  670. },
  671. onabort: reject.bind(null, "abort"),
  672. onerror(e) {
  673. reject({
  674. error: "error",
  675. resp: e,
  676. });
  677. },
  678. ontimeout: reject.bind(null, "timeout"),
  679. onreadystatechange(e) {
  680. // X 浏览器超时中断
  681. if (
  682. (e === null || e === void 0 ? void 0 : e.readyState) === 4
  683. ) {
  684. setTimeout(() => {
  685. if (!loaded)
  686. reject({
  687. error: "X timeout",
  688. resp: e,
  689. });
  690. }, data.xTimeout);
  691. }
  692. // Via 浏览器超时中断,不给成功状态...
  693. if (
  694. (e === null || e === void 0 ? void 0 : e.readyState) === 3
  695. ) {
  696. setTimeout(() => {
  697. if (!loaded)
  698. reject({
  699. error: "Via timeout",
  700. resp: e,
  701. });
  702. }, data.timeout);
  703. }
  704. },
  705. timeout: data.timeout,
  706. },
  707. details
  708. )
  709. );
  710. });
  711. });
  712. }
  713. function getRuleFromResource(key) {
  714. try {
  715. return cat.GM_getResourceText(key);
  716. } catch (error) {
  717. return null;
  718. }
  719. }
  720.  
  721. function saveCss() {
  722. var _a, _b;
  723. const styles = {
  724. needUpdate: false,
  725. genHideCss: data.genHideCss,
  726. genExtraCss: data.genExtraCss,
  727. spcHideCss: data.spcHideCss,
  728. spcExtraCss: data.spcExtraCss,
  729. },
  730. has = values.hasSave;
  731. values.css = styles;
  732. if (
  733. ((_b =
  734. (_a = values.css) === null || _a === void 0
  735. ? void 0
  736. : _a.genHideCss) === null || _b === void 0
  737. ? void 0
  738. : _b.length) === data.genHideCss.length &&
  739. !has.includes(location.hostname)
  740. ) {
  741. has.push(location.hostname);
  742. values.hasSave = has;
  743. }
  744. }
  745. function readCss() {
  746. const styles = values.css;
  747. if (!hasSome(Object.keys(styles), styleBoxes)) {
  748. values.css = emptyStyle;
  749. return false;
  750. }
  751. styleBoxes.forEach((sname) => {
  752. var _a;
  753. if (styles[sname].length > 0) {
  754. data.saved = true;
  755. data.update =
  756. (_a = styles.needUpdate) !== null && _a !== void 0 ? _a : true;
  757. data[sname] = styles[sname];
  758. }
  759. });
  760. return data.saved;
  761. }
  762.  
  763. function _defineProperty(obj, key, value) {
  764. if (key in obj) {
  765. Object.defineProperty(obj, key, {
  766. value: value,
  767. enumerable: true,
  768. configurable: true,
  769. writable: true,
  770. });
  771. } else {
  772. obj[key] = value;
  773. }
  774. return obj;
  775. }
  776. const NODE = {
  777. SELECTOR_LIST: "SelectorList",
  778. SELECTOR: "Selector",
  779. REGULAR_SELECTOR: "RegularSelector",
  780. EXTENDED_SELECTOR: "ExtendedSelector",
  781. ABSOLUTE_PSEUDO_CLASS: "AbsolutePseudoClass",
  782. RELATIVE_PSEUDO_CLASS: "RelativePseudoClass",
  783. };
  784. class AnySelectorNode {
  785. constructor(type) {
  786. _defineProperty(this, "children", []);
  787. this.type = type;
  788. }
  789. addChild(child) {
  790. this.children.push(child);
  791. }
  792. }
  793. class RegularSelectorNode extends AnySelectorNode {
  794. constructor(value) {
  795. super(NODE.REGULAR_SELECTOR);
  796. this.value = value;
  797. }
  798. }
  799. class RelativePseudoClassNode extends AnySelectorNode {
  800. constructor(name) {
  801. super(NODE.RELATIVE_PSEUDO_CLASS);
  802. this.name = name;
  803. }
  804. }
  805. class AbsolutePseudoClassNode extends AnySelectorNode {
  806. constructor(name) {
  807. super(NODE.ABSOLUTE_PSEUDO_CLASS);
  808. _defineProperty(this, "value", "");
  809. this.name = name;
  810. }
  811. }
  812. const LEFT_SQUARE_BRACKET = "[";
  813. const RIGHT_SQUARE_BRACKET = "]";
  814. const LEFT_PARENTHESIS = "(";
  815. const RIGHT_PARENTHESIS = ")";
  816. const LEFT_CURLY_BRACKET = "{";
  817. const RIGHT_CURLY_BRACKET = "}";
  818. const BRACKET = {
  819. SQUARE: {
  820. LEFT: LEFT_SQUARE_BRACKET,
  821. RIGHT: RIGHT_SQUARE_BRACKET,
  822. },
  823. PARENTHESES: {
  824. LEFT: LEFT_PARENTHESIS,
  825. RIGHT: RIGHT_PARENTHESIS,
  826. },
  827. CURLY: {
  828. LEFT: LEFT_CURLY_BRACKET,
  829. RIGHT: RIGHT_CURLY_BRACKET,
  830. },
  831. };
  832. const SLASH = "/";
  833. const BACKSLASH = "\\";
  834. const SPACE = " ";
  835. const COMMA = ",";
  836. const DOT = ".";
  837. const SEMICOLON = ";";
  838. const COLON = ":";
  839. const SINGLE_QUOTE = "'";
  840. const DOUBLE_QUOTE = '"';
  841. const CARET = "^";
  842. const DOLLAR_SIGN = "$";
  843. const EQUAL_SIGN = "=";
  844. const TAB = "\t";
  845. const CARRIAGE_RETURN = "\r";
  846. const LINE_FEED = "\n";
  847. const FORM_FEED = "\f";
  848. const WHITE_SPACE_CHARACTERS = [
  849. SPACE,
  850. TAB,
  851. CARRIAGE_RETURN,
  852. LINE_FEED,
  853. FORM_FEED,
  854. ];
  855. const ASTERISK = "*";
  856. const ID_MARKER = "#";
  857. const CLASS_MARKER = DOT;
  858. const DESCENDANT_COMBINATOR = SPACE;
  859. const CHILD_COMBINATOR = ">";
  860. const NEXT_SIBLING_COMBINATOR = "+";
  861. const SUBSEQUENT_SIBLING_COMBINATOR = "~";
  862. const COMBINATORS = [
  863. DESCENDANT_COMBINATOR,
  864. CHILD_COMBINATOR,
  865. NEXT_SIBLING_COMBINATOR,
  866. SUBSEQUENT_SIBLING_COMBINATOR,
  867. ];
  868. const SUPPORTED_SELECTOR_MARKS = [
  869. LEFT_SQUARE_BRACKET,
  870. RIGHT_SQUARE_BRACKET,
  871. LEFT_PARENTHESIS,
  872. RIGHT_PARENTHESIS,
  873. LEFT_CURLY_BRACKET,
  874. RIGHT_CURLY_BRACKET,
  875. SLASH,
  876. BACKSLASH,
  877. SEMICOLON,
  878. COLON,
  879. COMMA,
  880. SINGLE_QUOTE,
  881. DOUBLE_QUOTE,
  882. CARET,
  883. DOLLAR_SIGN,
  884. ASTERISK,
  885. ID_MARKER,
  886. CLASS_MARKER,
  887. DESCENDANT_COMBINATOR,
  888. CHILD_COMBINATOR,
  889. NEXT_SIBLING_COMBINATOR,
  890. SUBSEQUENT_SIBLING_COMBINATOR,
  891. TAB,
  892. CARRIAGE_RETURN,
  893. LINE_FEED,
  894. FORM_FEED,
  895. ];
  896. const SUPPORTED_STYLE_DECLARATION_MARKS = [
  897. COLON,
  898. SEMICOLON,
  899. SINGLE_QUOTE,
  900. DOUBLE_QUOTE,
  901. BACKSLASH,
  902. SPACE,
  903. TAB,
  904. CARRIAGE_RETURN,
  905. LINE_FEED,
  906. FORM_FEED,
  907. ];
  908. const CONTAINS_PSEUDO = "contains";
  909. const HAS_TEXT_PSEUDO = "has-text";
  910. const ABP_CONTAINS_PSEUDO = "-abp-contains";
  911. const MATCHES_CSS_PSEUDO = "matches-css";
  912. const MATCHES_CSS_BEFORE_PSEUDO = "matches-css-before";
  913. const MATCHES_CSS_AFTER_PSEUDO = "matches-css-after";
  914. const MATCHES_ATTR_PSEUDO_CLASS_MARKER = "matches-attr";
  915. const MATCHES_PROPERTY_PSEUDO_CLASS_MARKER = "matches-property";
  916. const XPATH_PSEUDO_CLASS_MARKER = "xpath";
  917. const NTH_ANCESTOR_PSEUDO_CLASS_MARKER = "nth-ancestor";
  918. const CONTAINS_PSEUDO_NAMES = [
  919. CONTAINS_PSEUDO,
  920. HAS_TEXT_PSEUDO,
  921. ABP_CONTAINS_PSEUDO,
  922. ];
  923. const UPWARD_PSEUDO_CLASS_MARKER = "upward";
  924. const REMOVE_PSEUDO_MARKER = "remove";
  925. const HAS_PSEUDO_CLASS_MARKER = "has";
  926. const ABP_HAS_PSEUDO_CLASS_MARKER = "-abp-has";
  927. const HAS_PSEUDO_CLASS_MARKERS = [
  928. HAS_PSEUDO_CLASS_MARKER,
  929. ABP_HAS_PSEUDO_CLASS_MARKER,
  930. ];
  931. const IS_PSEUDO_CLASS_MARKER = "is";
  932. const NOT_PSEUDO_CLASS_MARKER = "not";
  933. const ABSOLUTE_PSEUDO_CLASSES = [
  934. CONTAINS_PSEUDO,
  935. HAS_TEXT_PSEUDO,
  936. ABP_CONTAINS_PSEUDO,
  937. MATCHES_CSS_PSEUDO,
  938. MATCHES_CSS_BEFORE_PSEUDO,
  939. MATCHES_CSS_AFTER_PSEUDO,
  940. MATCHES_ATTR_PSEUDO_CLASS_MARKER,
  941. MATCHES_PROPERTY_PSEUDO_CLASS_MARKER,
  942. XPATH_PSEUDO_CLASS_MARKER,
  943. NTH_ANCESTOR_PSEUDO_CLASS_MARKER,
  944. UPWARD_PSEUDO_CLASS_MARKER,
  945. ];
  946. const RELATIVE_PSEUDO_CLASSES = [
  947. ...HAS_PSEUDO_CLASS_MARKERS,
  948. IS_PSEUDO_CLASS_MARKER,
  949. NOT_PSEUDO_CLASS_MARKER,
  950. ];
  951. const SUPPORTED_PSEUDO_CLASSES = [
  952. ...ABSOLUTE_PSEUDO_CLASSES,
  953. ...RELATIVE_PSEUDO_CLASSES,
  954. ];
  955. const OPTIMIZATION_PSEUDO_CLASSES = [
  956. NOT_PSEUDO_CLASS_MARKER,
  957. IS_PSEUDO_CLASS_MARKER,
  958. ];
  959. const SCOPE_CSS_PSEUDO_CLASS = ":scope";
  960. const REGULAR_PSEUDO_ELEMENTS = {
  961. AFTER: "after",
  962. BACKDROP: "backdrop",
  963. BEFORE: "before",
  964. CUE: "cue",
  965. CUE_REGION: "cue-region",
  966. FIRST_LETTER: "first-letter",
  967. FIRST_LINE: "first-line",
  968. FILE_SELECTION_BUTTON: "file-selector-button",
  969. GRAMMAR_ERROR: "grammar-error",
  970. MARKER: "marker",
  971. PART: "part",
  972. PLACEHOLDER: "placeholder",
  973. SELECTION: "selection",
  974. SLOTTED: "slotted",
  975. SPELLING_ERROR: "spelling-error",
  976. TARGET_TEXT: "target-text",
  977. };
  978. const AT_RULE_MARKER = "@";
  979. const CONTENT_CSS_PROPERTY = "content";
  980. const PSEUDO_PROPERTY_POSITIVE_VALUE = "true";
  981. const DEBUG_PSEUDO_PROPERTY_GLOBAL_VALUE = "global";
  982. const NO_SELECTOR_ERROR_PREFIX = "Selector should be defined";
  983. const STYLE_ERROR_PREFIX = {
  984. NO_STYLE: "No style declaration found",
  985. NO_SELECTOR: `${NO_SELECTOR_ERROR_PREFIX} before style declaration in stylesheet`,
  986. INVALID_STYLE: "Invalid style declaration",
  987. UNCLOSED_STYLE: "Unclosed style declaration",
  988. NO_PROPERTY: "Missing style property in declaration",
  989. NO_VALUE: "Missing style value in declaration",
  990. NO_STYLE_OR_REMOVE:
  991. "Style should be declared or :remove() pseudo-class should used",
  992. NO_COMMENT: "Comments are not supported",
  993. };
  994. const NO_AT_RULE_ERROR_PREFIX = "At-rules are not supported";
  995. const REMOVE_ERROR_PREFIX = {
  996. INVALID_REMOVE: "Invalid :remove() pseudo-class in selector",
  997. NO_TARGET_SELECTOR: `${NO_SELECTOR_ERROR_PREFIX} before :remove() pseudo-class`,
  998. MULTIPLE_USAGE: "Pseudo-class :remove() appears more than once in selector",
  999. INVALID_POSITION: "Pseudo-class :remove() should be at the end of selector",
  1000. };
  1001. const MATCHING_ELEMENT_ERROR_PREFIX = "Error while matching element";
  1002. const MAX_STYLE_PROTECTION_COUNT = 50;
  1003. const REGEXP_VALID_OLD_SYNTAX =
  1004. /\[-(?:ext)-([a-z-_]+)=(["'])((?:(?=(\\?))\4.)*?)\2\]/g;
  1005. const INVALID_OLD_SYNTAX_MARKER = "[-ext-";
  1006. const evaluateMatch = (match, name, quoteChar, rawValue) => {
  1007. const re = new RegExp(`([^\\\\]|^)\\\\${quoteChar}`, "g");
  1008. const value = rawValue.replace(re, `$1${quoteChar}`);
  1009. return `:${name}(${value})`;
  1010. };
  1011. const SCOPE_MARKER_REGEXP = /\(:scope >/g;
  1012. const SCOPE_REPLACER = "(>";
  1013. const MATCHES_CSS_PSEUDO_ELEMENT_REGEXP = /(:matches-css)-(before|after)\(/g;
  1014. const convertMatchesCss = (
  1015. match,
  1016. extendedPseudoClass,
  1017. regularPseudoElement
  1018. ) => {
  1019. return `${extendedPseudoClass}${BRACKET.PARENTHESES.LEFT}${regularPseudoElement}${COMMA}`;
  1020. };
  1021. const normalize = (selector) => {
  1022. const normalizedSelector = selector
  1023. .replace(REGEXP_VALID_OLD_SYNTAX, evaluateMatch)
  1024. .replace(SCOPE_MARKER_REGEXP, SCOPE_REPLACER)
  1025. .replace(MATCHES_CSS_PSEUDO_ELEMENT_REGEXP, convertMatchesCss);
  1026. if (normalizedSelector.includes(INVALID_OLD_SYNTAX_MARKER)) {
  1027. throw new Error(
  1028. `Invalid extended-css old syntax selector: '${selector}'`
  1029. );
  1030. }
  1031. return normalizedSelector;
  1032. };
  1033. const convert = (rawSelector) => {
  1034. const trimmedSelector = rawSelector.trim();
  1035. return normalize(trimmedSelector);
  1036. };
  1037. const TOKEN_TYPE = {
  1038. MARK: "mark",
  1039. WORD: "word",
  1040. };
  1041. const tokenize = (input, supportedMarks) => {
  1042. let wordBuffer = "";
  1043. const tokens = [];
  1044. const selectorSymbols = input.split("");
  1045. selectorSymbols.forEach((symbol) => {
  1046. if (supportedMarks.includes(symbol)) {
  1047. if (wordBuffer.length > 0) {
  1048. tokens.push({
  1049. type: TOKEN_TYPE.WORD,
  1050. value: wordBuffer,
  1051. });
  1052. wordBuffer = "";
  1053. }
  1054. tokens.push({
  1055. type: TOKEN_TYPE.MARK,
  1056. value: symbol,
  1057. });
  1058. return;
  1059. }
  1060. wordBuffer += symbol;
  1061. });
  1062. if (wordBuffer.length > 0) {
  1063. tokens.push({
  1064. type: TOKEN_TYPE.WORD,
  1065. value: wordBuffer,
  1066. });
  1067. }
  1068. return tokens;
  1069. };
  1070. const tokenizeSelector = (rawSelector) => {
  1071. const selector = convert(rawSelector);
  1072. return tokenize(selector, SUPPORTED_SELECTOR_MARKS);
  1073. };
  1074. const tokenizeAttribute = (attribute) => {
  1075. return tokenize(attribute, [...SUPPORTED_SELECTOR_MARKS, EQUAL_SIGN]);
  1076. };
  1077. const flatten = (input) => {
  1078. const stack = [];
  1079. input.forEach((el) => stack.push(el));
  1080. const res = [];
  1081. while (stack.length) {
  1082. const next = stack.pop();
  1083. if (!next) {
  1084. throw new Error("Unable to make array flat");
  1085. }
  1086. if (Array.isArray(next)) {
  1087. next.forEach((el) => stack.push(el));
  1088. } else {
  1089. res.push(next);
  1090. }
  1091. }
  1092. return res.reverse();
  1093. };
  1094. const getFirst = (array) => {
  1095. return array[0];
  1096. };
  1097. const getLast = (array) => {
  1098. return array[array.length - 1];
  1099. };
  1100. const getPrevToLast = (array) => {
  1101. return array[array.length - 2];
  1102. };
  1103. const getItemByIndex = (array, index, errorMessage) => {
  1104. const indexChild = array[index];
  1105. if (!indexChild) {
  1106. throw new Error(errorMessage || `No array item found by index ${index}`);
  1107. }
  1108. return indexChild;
  1109. };
  1110. const NO_REGULAR_SELECTOR_ERROR =
  1111. "At least one of Selector node children should be RegularSelector";
  1112. const isSelectorListNode = (astNode) => {
  1113. return (
  1114. (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
  1115. NODE.SELECTOR_LIST
  1116. );
  1117. };
  1118. const isSelectorNode = (astNode) => {
  1119. return (
  1120. (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
  1121. NODE.SELECTOR
  1122. );
  1123. };
  1124. const isRegularSelectorNode = (astNode) => {
  1125. return (
  1126. (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
  1127. NODE.REGULAR_SELECTOR
  1128. );
  1129. };
  1130. const isExtendedSelectorNode = (astNode) => {
  1131. return astNode.type === NODE.EXTENDED_SELECTOR;
  1132. };
  1133. const isAbsolutePseudoClassNode = (astNode) => {
  1134. return (
  1135. (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
  1136. NODE.ABSOLUTE_PSEUDO_CLASS
  1137. );
  1138. };
  1139. const isRelativePseudoClassNode = (astNode) => {
  1140. return (
  1141. (astNode === null || astNode === void 0 ? void 0 : astNode.type) ===
  1142. NODE.RELATIVE_PSEUDO_CLASS
  1143. );
  1144. };
  1145. const getNodeName = (astNode) => {
  1146. if (astNode === null) {
  1147. throw new Error("Ast node should be defined");
  1148. }
  1149. if (
  1150. !isAbsolutePseudoClassNode(astNode) &&
  1151. !isRelativePseudoClassNode(astNode)
  1152. ) {
  1153. throw new Error(
  1154. "Only AbsolutePseudoClass or RelativePseudoClass ast node can have a name"
  1155. );
  1156. }
  1157. if (!astNode.name) {
  1158. throw new Error("Extended pseudo-class should have a name");
  1159. }
  1160. return astNode.name;
  1161. };
  1162. const getNodeValue = (astNode, errorMessage) => {
  1163. if (astNode === null) {
  1164. throw new Error("Ast node should be defined");
  1165. }
  1166. if (
  1167. !isRegularSelectorNode(astNode) &&
  1168. !isAbsolutePseudoClassNode(astNode)
  1169. ) {
  1170. throw new Error(
  1171. "Only RegularSelector ot AbsolutePseudoClass ast node can have a value"
  1172. );
  1173. }
  1174. if (!astNode.value) {
  1175. throw new Error(
  1176. errorMessage ||
  1177. "Ast RegularSelector ot AbsolutePseudoClass node should have a value"
  1178. );
  1179. }
  1180. return astNode.value;
  1181. };
  1182. const getRegularSelectorNodes = (children) => {
  1183. return children.filter(isRegularSelectorNode);
  1184. };
  1185. const getFirstRegularChild = (children, errorMessage) => {
  1186. const regularSelectorNodes = getRegularSelectorNodes(children);
  1187. const firstRegularSelectorNode = getFirst(regularSelectorNodes);
  1188. if (!firstRegularSelectorNode) {
  1189. throw new Error(errorMessage || NO_REGULAR_SELECTOR_ERROR);
  1190. }
  1191. return firstRegularSelectorNode;
  1192. };
  1193. const getLastRegularChild = (children) => {
  1194. const regularSelectorNodes = getRegularSelectorNodes(children);
  1195. const lastRegularSelectorNode = getLast(regularSelectorNodes);
  1196. if (!lastRegularSelectorNode) {
  1197. throw new Error(NO_REGULAR_SELECTOR_ERROR);
  1198. }
  1199. return lastRegularSelectorNode;
  1200. };
  1201. const getNodeOnlyChild = (node, errorMessage) => {
  1202. if (node.children.length !== 1) {
  1203. throw new Error(errorMessage);
  1204. }
  1205. const onlyChild = getFirst(node.children);
  1206. if (!onlyChild) {
  1207. throw new Error(errorMessage);
  1208. }
  1209. return onlyChild;
  1210. };
  1211. const getPseudoClassNode = (extendedSelectorNode) => {
  1212. return getNodeOnlyChild(
  1213. extendedSelectorNode,
  1214. "Extended selector should be specified"
  1215. );
  1216. };
  1217. const getRelativeSelectorListNode = (pseudoClassNode) => {
  1218. if (!isRelativePseudoClassNode(pseudoClassNode)) {
  1219. throw new Error(
  1220. "Only RelativePseudoClass node can have relative SelectorList node as child"
  1221. );
  1222. }
  1223. return getNodeOnlyChild(
  1224. pseudoClassNode,
  1225. `Missing arg for :${getNodeName(pseudoClassNode)}() pseudo-class`
  1226. );
  1227. };
  1228. const ATTRIBUTE_CASE_INSENSITIVE_FLAG = "i";
  1229. const POSSIBLE_MARKS_BEFORE_REGEXP = {
  1230. COMMON: [
  1231. BRACKET.PARENTHESES.LEFT,
  1232. SINGLE_QUOTE,
  1233. DOUBLE_QUOTE,
  1234. EQUAL_SIGN,
  1235. DOT,
  1236. COLON,
  1237. SPACE,
  1238. ],
  1239. CONTAINS: [BRACKET.PARENTHESES.LEFT, SINGLE_QUOTE, DOUBLE_QUOTE],
  1240. };
  1241. const isSupportedPseudoClass = (tokenValue) => {
  1242. return SUPPORTED_PSEUDO_CLASSES.includes(tokenValue);
  1243. };
  1244. const isOptimizationPseudoClass = (name) => {
  1245. return OPTIMIZATION_PSEUDO_CLASSES.includes(name);
  1246. };
  1247. const doesRegularContinueAfterSpace = (nextTokenType, nextTokenValue) => {
  1248. if (!nextTokenType || !nextTokenValue) {
  1249. return false;
  1250. }
  1251. return (
  1252. COMBINATORS.includes(nextTokenValue) ||
  1253. nextTokenType === TOKEN_TYPE.WORD ||
  1254. nextTokenValue === ASTERISK ||
  1255. nextTokenValue === ID_MARKER ||
  1256. nextTokenValue === CLASS_MARKER ||
  1257. nextTokenValue === COLON ||
  1258. nextTokenValue === SINGLE_QUOTE ||
  1259. nextTokenValue === DOUBLE_QUOTE ||
  1260. nextTokenValue === BRACKET.SQUARE.LEFT
  1261. );
  1262. };
  1263. const isRegexpOpening = (context, prevTokenValue, bufferNodeValue) => {
  1264. const lastExtendedPseudoClassName = getLast(
  1265. context.extendedPseudoNamesStack
  1266. );
  1267. if (!lastExtendedPseudoClassName) {
  1268. throw new Error(
  1269. "Regexp pattern allowed only in arg of extended pseudo-class"
  1270. );
  1271. }
  1272. if (CONTAINS_PSEUDO_NAMES.includes(lastExtendedPseudoClassName)) {
  1273. return POSSIBLE_MARKS_BEFORE_REGEXP.CONTAINS.includes(prevTokenValue);
  1274. }
  1275. if (
  1276. prevTokenValue === SLASH &&
  1277. lastExtendedPseudoClassName !== XPATH_PSEUDO_CLASS_MARKER
  1278. ) {
  1279. const rawArgDesc = bufferNodeValue
  1280. ? `in arg part: '${bufferNodeValue}'`
  1281. : "arg";
  1282. throw new Error(
  1283. `Invalid regexp pattern for :${lastExtendedPseudoClassName}() pseudo-class ${rawArgDesc}`
  1284. );
  1285. }
  1286. return POSSIBLE_MARKS_BEFORE_REGEXP.COMMON.includes(prevTokenValue);
  1287. };
  1288. const isAttributeOpening = (tokenValue, prevTokenValue) => {
  1289. return tokenValue === BRACKET.SQUARE.LEFT && prevTokenValue !== BACKSLASH;
  1290. };
  1291. const isAttributeClosing = (context) => {
  1292. var _getPrevToLast;
  1293. if (!context.isAttributeBracketsOpen) {
  1294. return false;
  1295. }
  1296. const noSpaceAttr = context.attributeBuffer.split(SPACE).join("");
  1297. const attrTokens = tokenizeAttribute(noSpaceAttr);
  1298. const firstAttrToken = getFirst(attrTokens);
  1299. const firstAttrTokenType =
  1300. firstAttrToken === null || firstAttrToken === void 0
  1301. ? void 0
  1302. : firstAttrToken.type;
  1303. const firstAttrTokenValue =
  1304. firstAttrToken === null || firstAttrToken === void 0
  1305. ? void 0
  1306. : firstAttrToken.value;
  1307. if (
  1308. firstAttrTokenType === TOKEN_TYPE.MARK &&
  1309. firstAttrTokenValue !== BACKSLASH
  1310. ) {
  1311. throw new Error(
  1312. `'[${context.attributeBuffer}]' is not a valid attribute due to '${firstAttrTokenValue}' at start of it`
  1313. );
  1314. }
  1315. const lastAttrToken = getLast(attrTokens);
  1316. const lastAttrTokenType =
  1317. lastAttrToken === null || lastAttrToken === void 0
  1318. ? void 0
  1319. : lastAttrToken.type;
  1320. const lastAttrTokenValue =
  1321. lastAttrToken === null || lastAttrToken === void 0
  1322. ? void 0
  1323. : lastAttrToken.value;
  1324. if (lastAttrTokenValue === EQUAL_SIGN) {
  1325. throw new Error(
  1326. `'[${context.attributeBuffer}]' is not a valid attribute due to '${EQUAL_SIGN}'`
  1327. );
  1328. }
  1329. const equalSignIndex = attrTokens.findIndex((token) => {
  1330. return token.type === TOKEN_TYPE.MARK && token.value === EQUAL_SIGN;
  1331. });
  1332. const prevToLastAttrTokenValue =
  1333. (_getPrevToLast = getPrevToLast(attrTokens)) === null ||
  1334. _getPrevToLast === void 0
  1335. ? void 0
  1336. : _getPrevToLast.value;
  1337. if (equalSignIndex === -1) {
  1338. if (lastAttrTokenType === TOKEN_TYPE.WORD) {
  1339. return true;
  1340. }
  1341. return (
  1342. prevToLastAttrTokenValue === BACKSLASH &&
  1343. (lastAttrTokenValue === DOUBLE_QUOTE ||
  1344. lastAttrTokenValue === SINGLE_QUOTE)
  1345. );
  1346. }
  1347. const nextToEqualSignToken = getItemByIndex(attrTokens, equalSignIndex + 1);
  1348. const nextToEqualSignTokenValue = nextToEqualSignToken.value;
  1349. const isAttrValueQuote =
  1350. nextToEqualSignTokenValue === SINGLE_QUOTE ||
  1351. nextToEqualSignTokenValue === DOUBLE_QUOTE;
  1352. if (!isAttrValueQuote) {
  1353. if (lastAttrTokenType === TOKEN_TYPE.WORD) {
  1354. return true;
  1355. }
  1356. throw new Error(
  1357. `'[${context.attributeBuffer}]' is not a valid attribute`
  1358. );
  1359. }
  1360. if (
  1361. lastAttrTokenType === TOKEN_TYPE.WORD &&
  1362. (lastAttrTokenValue === null || lastAttrTokenValue === void 0
  1363. ? void 0
  1364. : lastAttrTokenValue.toLocaleLowerCase()) ===
  1365. ATTRIBUTE_CASE_INSENSITIVE_FLAG
  1366. ) {
  1367. return prevToLastAttrTokenValue === nextToEqualSignTokenValue;
  1368. }
  1369. return lastAttrTokenValue === nextToEqualSignTokenValue;
  1370. };
  1371. const isWhiteSpaceChar = (tokenValue) => {
  1372. if (!tokenValue) {
  1373. return false;
  1374. }
  1375. return WHITE_SPACE_CHARACTERS.includes(tokenValue);
  1376. };
  1377. const isAbsolutePseudoClass = (str) => {
  1378. return ABSOLUTE_PSEUDO_CLASSES.includes(str);
  1379. };
  1380. const isRelativePseudoClass = (str) => {
  1381. return RELATIVE_PSEUDO_CLASSES.includes(str);
  1382. };
  1383. const getBufferNode = (context) => {
  1384. if (context.pathToBufferNode.length === 0) {
  1385. return null;
  1386. }
  1387. return getLast(context.pathToBufferNode) || null;
  1388. };
  1389. const getBufferNodeParent = (context) => {
  1390. if (context.pathToBufferNode.length < 2) {
  1391. return null;
  1392. }
  1393. return getPrevToLast(context.pathToBufferNode) || null;
  1394. };
  1395. const getContextLastRegularSelectorNode = (context) => {
  1396. const bufferNode = getBufferNode(context);
  1397. if (!bufferNode) {
  1398. throw new Error("No bufferNode found");
  1399. }
  1400. if (!isSelectorNode(bufferNode)) {
  1401. throw new Error("Unsupported bufferNode type");
  1402. }
  1403. const lastRegularSelectorNode = getLastRegularChild(bufferNode.children);
  1404. context.pathToBufferNode.push(lastRegularSelectorNode);
  1405. return lastRegularSelectorNode;
  1406. };
  1407. const updateBufferNode = (context, tokenValue) => {
  1408. const bufferNode = getBufferNode(context);
  1409. if (bufferNode === null) {
  1410. throw new Error("No bufferNode to update");
  1411. }
  1412. if (isAbsolutePseudoClassNode(bufferNode)) {
  1413. bufferNode.value += tokenValue;
  1414. } else if (isRegularSelectorNode(bufferNode)) {
  1415. bufferNode.value += tokenValue;
  1416. if (context.isAttributeBracketsOpen) {
  1417. context.attributeBuffer += tokenValue;
  1418. }
  1419. } else {
  1420. throw new Error(
  1421. `${bufferNode.type} node cannot be updated. Only RegularSelector and AbsolutePseudoClass are supported`
  1422. );
  1423. }
  1424. };
  1425. const addSelectorListNode = (context) => {
  1426. const selectorListNode = new AnySelectorNode(NODE.SELECTOR_LIST);
  1427. context.ast = selectorListNode;
  1428. context.pathToBufferNode.push(selectorListNode);
  1429. };
  1430. const addAstNodeByType = function (context, type) {
  1431. let tokenValue =
  1432. arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "";
  1433. const bufferNode = getBufferNode(context);
  1434. if (bufferNode === null) {
  1435. throw new Error("No buffer node");
  1436. }
  1437. let node;
  1438. if (type === NODE.REGULAR_SELECTOR) {
  1439. node = new RegularSelectorNode(tokenValue);
  1440. } else if (type === NODE.ABSOLUTE_PSEUDO_CLASS) {
  1441. node = new AbsolutePseudoClassNode(tokenValue);
  1442. } else if (type === NODE.RELATIVE_PSEUDO_CLASS) {
  1443. node = new RelativePseudoClassNode(tokenValue);
  1444. } else {
  1445. node = new AnySelectorNode(type);
  1446. }
  1447. bufferNode.addChild(node);
  1448. context.pathToBufferNode.push(node);
  1449. };
  1450. const initAst = (context, tokenValue) => {
  1451. addSelectorListNode(context);
  1452. addAstNodeByType(context, NODE.SELECTOR);
  1453. addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  1454. };
  1455. const initRelativeSubtree = function (context) {
  1456. let tokenValue =
  1457. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
  1458. addAstNodeByType(context, NODE.SELECTOR_LIST);
  1459. addAstNodeByType(context, NODE.SELECTOR);
  1460. addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  1461. };
  1462. const upToClosest = (context, parentType) => {
  1463. for (let i = context.pathToBufferNode.length - 1; i >= 0; i -= 1) {
  1464. var _context$pathToBuffer;
  1465. if (
  1466. ((_context$pathToBuffer = context.pathToBufferNode[i]) === null ||
  1467. _context$pathToBuffer === void 0
  1468. ? void 0
  1469. : _context$pathToBuffer.type) === parentType
  1470. ) {
  1471. context.pathToBufferNode = context.pathToBufferNode.slice(0, i + 1);
  1472. break;
  1473. }
  1474. }
  1475. };
  1476. const getUpdatedBufferNode = (context) => {
  1477. const bufferNode = getBufferNode(context);
  1478. if (
  1479. bufferNode &&
  1480. isSelectorListNode(bufferNode) &&
  1481. isRelativePseudoClassNode(getBufferNodeParent(context))
  1482. ) {
  1483. return bufferNode;
  1484. }
  1485. upToClosest(context, NODE.SELECTOR);
  1486. const selectorNode = getBufferNode(context);
  1487. if (!selectorNode) {
  1488. throw new Error(
  1489. "No SelectorNode, impossible to continue selector parsing by ExtendedCss"
  1490. );
  1491. }
  1492. const lastSelectorNodeChild = getLast(selectorNode.children);
  1493. const hasExtended =
  1494. lastSelectorNodeChild &&
  1495. isExtendedSelectorNode(lastSelectorNodeChild) &&
  1496. context.standardPseudoBracketsStack.length === 0;
  1497. const supposedPseudoClassNode =
  1498. hasExtended && getFirst(lastSelectorNodeChild.children);
  1499. let newNeededBufferNode = selectorNode;
  1500. if (supposedPseudoClassNode) {
  1501. const lastExtendedPseudoName =
  1502. hasExtended && supposedPseudoClassNode.name;
  1503. const isLastExtendedNameRelative =
  1504. lastExtendedPseudoName && isRelativePseudoClass(lastExtendedPseudoName);
  1505. const isLastExtendedNameAbsolute =
  1506. lastExtendedPseudoName && isAbsolutePseudoClass(lastExtendedPseudoName);
  1507. const hasRelativeExtended =
  1508. isLastExtendedNameRelative &&
  1509. context.extendedPseudoBracketsStack.length > 0 &&
  1510. context.extendedPseudoBracketsStack.length ===
  1511. context.extendedPseudoNamesStack.length;
  1512. const hasAbsoluteExtended =
  1513. isLastExtendedNameAbsolute &&
  1514. lastExtendedPseudoName === getLast(context.extendedPseudoNamesStack);
  1515. if (hasRelativeExtended) {
  1516. context.pathToBufferNode.push(lastSelectorNodeChild);
  1517. newNeededBufferNode = supposedPseudoClassNode;
  1518. } else if (hasAbsoluteExtended) {
  1519. context.pathToBufferNode.push(lastSelectorNodeChild);
  1520. newNeededBufferNode = supposedPseudoClassNode;
  1521. }
  1522. } else if (hasExtended) {
  1523. newNeededBufferNode = selectorNode;
  1524. } else {
  1525. newNeededBufferNode = getContextLastRegularSelectorNode(context);
  1526. }
  1527. context.pathToBufferNode.push(newNeededBufferNode);
  1528. return newNeededBufferNode;
  1529. };
  1530. const handleNextTokenOnColon = (
  1531. context,
  1532. selector,
  1533. tokenValue,
  1534. nextTokenValue,
  1535. nextToNextTokenValue
  1536. ) => {
  1537. if (!nextTokenValue) {
  1538. throw new Error(
  1539. `Invalid colon ':' at the end of selector: '${selector}'`
  1540. );
  1541. }
  1542. if (!isSupportedPseudoClass(nextTokenValue.toLowerCase())) {
  1543. if (nextTokenValue.toLowerCase() === REMOVE_PSEUDO_MARKER) {
  1544. throw new Error(`${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`);
  1545. }
  1546. updateBufferNode(context, tokenValue);
  1547. if (
  1548. nextToNextTokenValue &&
  1549. nextToNextTokenValue === BRACKET.PARENTHESES.LEFT &&
  1550. !context.isAttributeBracketsOpen
  1551. ) {
  1552. context.standardPseudoNamesStack.push(nextTokenValue);
  1553. }
  1554. } else {
  1555. if (
  1556. HAS_PSEUDO_CLASS_MARKERS.includes(nextTokenValue) &&
  1557. context.standardPseudoNamesStack.length > 0
  1558. ) {
  1559. throw new Error(
  1560. `Usage of :${nextTokenValue}() pseudo-class is not allowed inside regular pseudo: '${getLast(
  1561. context.standardPseudoNamesStack
  1562. )}'`
  1563. );
  1564. } else {
  1565. upToClosest(context, NODE.SELECTOR);
  1566. addAstNodeByType(context, NODE.EXTENDED_SELECTOR);
  1567. }
  1568. }
  1569. };
  1570. const IS_OR_NOT_PSEUDO_SELECTING_ROOT = `html ${ASTERISK}`;
  1571. const hasExtendedSelector = (selectorList) => {
  1572. return selectorList.children.some((selectorNode) => {
  1573. return selectorNode.children.some((selectorNodeChild) => {
  1574. return isExtendedSelectorNode(selectorNodeChild);
  1575. });
  1576. });
  1577. };
  1578. const selectorListOfRegularsToString = (selectorList) => {
  1579. const standardCssSelectors = selectorList.children.map((selectorNode) => {
  1580. const selectorOnlyChild = getNodeOnlyChild(
  1581. selectorNode,
  1582. "Ast Selector node should have RegularSelector node"
  1583. );
  1584. return getNodeValue(selectorOnlyChild);
  1585. });
  1586. return standardCssSelectors.join(`${COMMA}${SPACE}`);
  1587. };
  1588. const updateNodeChildren = (node, newChildren) => {
  1589. node.children = newChildren;
  1590. return node;
  1591. };
  1592. const shouldOptimizeExtendedSelector = (currExtendedSelectorNode) => {
  1593. if (currExtendedSelectorNode === null) {
  1594. return false;
  1595. }
  1596. const extendedPseudoClassNode = getPseudoClassNode(
  1597. currExtendedSelectorNode
  1598. );
  1599. const pseudoName = getNodeName(extendedPseudoClassNode);
  1600. if (isAbsolutePseudoClass(pseudoName)) {
  1601. return false;
  1602. }
  1603. const relativeSelectorList = getRelativeSelectorListNode(
  1604. extendedPseudoClassNode
  1605. );
  1606. const innerSelectorNodes = relativeSelectorList.children;
  1607. if (isOptimizationPseudoClass(pseudoName)) {
  1608. const areAllSelectorNodeChildrenRegular = innerSelectorNodes.every(
  1609. (selectorNode) => {
  1610. try {
  1611. const selectorOnlyChild = getNodeOnlyChild(
  1612. selectorNode,
  1613. "Selector node should have RegularSelector"
  1614. );
  1615. return isRegularSelectorNode(selectorOnlyChild);
  1616. } catch (e) {
  1617. return false;
  1618. }
  1619. }
  1620. );
  1621. if (areAllSelectorNodeChildrenRegular) {
  1622. return true;
  1623. }
  1624. }
  1625. return innerSelectorNodes.some((selectorNode) => {
  1626. return selectorNode.children.some((selectorNodeChild) => {
  1627. if (!isExtendedSelectorNode(selectorNodeChild)) {
  1628. return false;
  1629. }
  1630. return shouldOptimizeExtendedSelector(selectorNodeChild);
  1631. });
  1632. });
  1633. };
  1634. const getOptimizedExtendedSelector = (
  1635. currExtendedSelectorNode,
  1636. prevRegularSelectorNode
  1637. ) => {
  1638. if (!currExtendedSelectorNode) {
  1639. return null;
  1640. }
  1641. const extendedPseudoClassNode = getPseudoClassNode(
  1642. currExtendedSelectorNode
  1643. );
  1644. const relativeSelectorList = getRelativeSelectorListNode(
  1645. extendedPseudoClassNode
  1646. );
  1647. const hasInnerExtendedSelector = hasExtendedSelector(relativeSelectorList);
  1648. if (!hasInnerExtendedSelector) {
  1649. const relativeSelectorListStr =
  1650. selectorListOfRegularsToString(relativeSelectorList);
  1651. const pseudoName = getNodeName(extendedPseudoClassNode);
  1652. const optimizedExtendedStr = `${COLON}${pseudoName}${BRACKET.PARENTHESES.LEFT}${relativeSelectorListStr}${BRACKET.PARENTHESES.RIGHT}`;
  1653. prevRegularSelectorNode.value = `${getNodeValue(
  1654. prevRegularSelectorNode
  1655. )}${optimizedExtendedStr}`;
  1656. return null;
  1657. }
  1658. const optimizedRelativeSelectorList =
  1659. optimizeSelectorListNode(relativeSelectorList);
  1660. const optimizedExtendedPseudoClassNode = updateNodeChildren(
  1661. extendedPseudoClassNode,
  1662. [optimizedRelativeSelectorList]
  1663. );
  1664. return updateNodeChildren(currExtendedSelectorNode, [
  1665. optimizedExtendedPseudoClassNode,
  1666. ]);
  1667. };
  1668. const optimizeCurrentRegularSelector = (current, previous) => {
  1669. previous.value = `${getNodeValue(previous)}${SPACE}${getNodeValue(
  1670. current
  1671. )}`;
  1672. };
  1673. const optimizeSelectorNode = (selectorNode) => {
  1674. const rawSelectorNodeChildren = selectorNode.children;
  1675. const optimizedChildrenList = [];
  1676. let currentIndex = 0;
  1677. while (currentIndex < rawSelectorNodeChildren.length) {
  1678. const currentChild = getItemByIndex(
  1679. rawSelectorNodeChildren,
  1680. currentIndex,
  1681. "currentChild should be specified"
  1682. );
  1683. if (currentIndex === 0) {
  1684. optimizedChildrenList.push(currentChild);
  1685. } else {
  1686. const prevRegularChild = getLastRegularChild(optimizedChildrenList);
  1687. if (isExtendedSelectorNode(currentChild)) {
  1688. let optimizedExtendedSelector = null;
  1689. let isOptimizationNeeded =
  1690. shouldOptimizeExtendedSelector(currentChild);
  1691. optimizedExtendedSelector = currentChild;
  1692. while (isOptimizationNeeded) {
  1693. optimizedExtendedSelector = getOptimizedExtendedSelector(
  1694. optimizedExtendedSelector,
  1695. prevRegularChild
  1696. );
  1697. isOptimizationNeeded = shouldOptimizeExtendedSelector(
  1698. optimizedExtendedSelector
  1699. );
  1700. }
  1701. if (optimizedExtendedSelector !== null) {
  1702. optimizedChildrenList.push(optimizedExtendedSelector);
  1703. const optimizedPseudoClass = getPseudoClassNode(
  1704. optimizedExtendedSelector
  1705. );
  1706. const optimizedPseudoName = getNodeName(optimizedPseudoClass);
  1707. if (
  1708. getNodeValue(prevRegularChild) === ASTERISK &&
  1709. isOptimizationPseudoClass(optimizedPseudoName)
  1710. ) {
  1711. prevRegularChild.value = IS_OR_NOT_PSEUDO_SELECTING_ROOT;
  1712. }
  1713. }
  1714. } else if (isRegularSelectorNode(currentChild)) {
  1715. const lastOptimizedChild = getLast(optimizedChildrenList) || null;
  1716. if (isRegularSelectorNode(lastOptimizedChild)) {
  1717. optimizeCurrentRegularSelector(currentChild, prevRegularChild);
  1718. }
  1719. }
  1720. }
  1721. currentIndex += 1;
  1722. }
  1723. return updateNodeChildren(selectorNode, optimizedChildrenList);
  1724. };
  1725. const optimizeSelectorListNode = (selectorListNode) => {
  1726. return updateNodeChildren(
  1727. selectorListNode,
  1728. selectorListNode.children.map((s) => optimizeSelectorNode(s))
  1729. );
  1730. };
  1731. const optimizeAst = (ast) => {
  1732. return optimizeSelectorListNode(ast);
  1733. };
  1734. const XPATH_PSEUDO_SELECTING_ROOT = "body";
  1735. const NO_WHITESPACE_ERROR_PREFIX =
  1736. "No white space is allowed before or after extended pseudo-class name in selector";
  1737. const parse = (selector) => {
  1738. const tokens = tokenizeSelector(selector);
  1739. const context = {
  1740. ast: null,
  1741. pathToBufferNode: [],
  1742. extendedPseudoNamesStack: [],
  1743. extendedPseudoBracketsStack: [],
  1744. standardPseudoNamesStack: [],
  1745. standardPseudoBracketsStack: [],
  1746. isAttributeBracketsOpen: false,
  1747. attributeBuffer: "",
  1748. isRegexpOpen: false,
  1749. shouldOptimize: false,
  1750. };
  1751. let i = 0;
  1752. while (i < tokens.length) {
  1753. const token = tokens[i];
  1754. if (!token) {
  1755. break;
  1756. }
  1757. const { type: tokenType, value: tokenValue } = token;
  1758. const nextToken = tokens[i + 1];
  1759. const nextTokenType =
  1760. nextToken === null || nextToken === void 0 ? void 0 : nextToken.type;
  1761. const nextTokenValue =
  1762. nextToken === null || nextToken === void 0 ? void 0 : nextToken.value;
  1763. const nextToNextToken = tokens[i + 2];
  1764. const nextToNextTokenValue =
  1765. nextToNextToken === null || nextToNextToken === void 0
  1766. ? void 0
  1767. : nextToNextToken.value;
  1768. const previousToken = tokens[i - 1];
  1769. const prevTokenType =
  1770. previousToken === null || previousToken === void 0
  1771. ? void 0
  1772. : previousToken.type;
  1773. const prevTokenValue =
  1774. previousToken === null || previousToken === void 0
  1775. ? void 0
  1776. : previousToken.value;
  1777. const previousToPreviousToken = tokens[i - 2];
  1778. const prevToPrevTokenValue =
  1779. previousToPreviousToken === null || previousToPreviousToken === void 0
  1780. ? void 0
  1781. : previousToPreviousToken.value;
  1782. let bufferNode = getBufferNode(context);
  1783. switch (tokenType) {
  1784. case TOKEN_TYPE.WORD:
  1785. if (bufferNode === null) {
  1786. initAst(context, tokenValue);
  1787. } else if (isSelectorListNode(bufferNode)) {
  1788. addAstNodeByType(context, NODE.SELECTOR);
  1789. addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  1790. } else if (isRegularSelectorNode(bufferNode)) {
  1791. updateBufferNode(context, tokenValue);
  1792. } else if (isExtendedSelectorNode(bufferNode)) {
  1793. if (
  1794. isWhiteSpaceChar(nextTokenValue) &&
  1795. nextToNextTokenValue === BRACKET.PARENTHESES.LEFT
  1796. ) {
  1797. throw new Error(`${NO_WHITESPACE_ERROR_PREFIX}: '${selector}'`);
  1798. }
  1799. const lowerCaseTokenValue = tokenValue.toLowerCase();
  1800. context.extendedPseudoNamesStack.push(lowerCaseTokenValue);
  1801. if (isAbsolutePseudoClass(lowerCaseTokenValue)) {
  1802. addAstNodeByType(
  1803. context,
  1804. NODE.ABSOLUTE_PSEUDO_CLASS,
  1805. lowerCaseTokenValue
  1806. );
  1807. } else {
  1808. addAstNodeByType(
  1809. context,
  1810. NODE.RELATIVE_PSEUDO_CLASS,
  1811. lowerCaseTokenValue
  1812. );
  1813. if (isOptimizationPseudoClass(lowerCaseTokenValue)) {
  1814. context.shouldOptimize = true;
  1815. }
  1816. }
  1817. } else if (isAbsolutePseudoClassNode(bufferNode)) {
  1818. updateBufferNode(context, tokenValue);
  1819. } else if (isRelativePseudoClassNode(bufferNode)) {
  1820. initRelativeSubtree(context, tokenValue);
  1821. }
  1822. break;
  1823. case TOKEN_TYPE.MARK:
  1824. switch (tokenValue) {
  1825. case COMMA:
  1826. if (
  1827. !bufferNode ||
  1828. (typeof bufferNode !== "undefined" && !nextTokenValue)
  1829. ) {
  1830. throw new Error(`'${selector}' is not a valid selector`);
  1831. } else if (isRegularSelectorNode(bufferNode)) {
  1832. if (context.isAttributeBracketsOpen) {
  1833. updateBufferNode(context, tokenValue);
  1834. } else {
  1835. upToClosest(context, NODE.SELECTOR_LIST);
  1836. }
  1837. } else if (isAbsolutePseudoClassNode(bufferNode)) {
  1838. updateBufferNode(context, tokenValue);
  1839. } else if (isSelectorNode(bufferNode)) {
  1840. upToClosest(context, NODE.SELECTOR_LIST);
  1841. }
  1842. break;
  1843. case SPACE:
  1844. if (
  1845. isRegularSelectorNode(bufferNode) &&
  1846. !context.isAttributeBracketsOpen
  1847. ) {
  1848. bufferNode = getUpdatedBufferNode(context);
  1849. }
  1850. if (isRegularSelectorNode(bufferNode)) {
  1851. if (
  1852. !context.isAttributeBracketsOpen &&
  1853. ((prevTokenValue === COLON &&
  1854. nextTokenType === TOKEN_TYPE.WORD) ||
  1855. (prevTokenType === TOKEN_TYPE.WORD &&
  1856. nextTokenValue === BRACKET.PARENTHESES.LEFT))
  1857. ) {
  1858. throw new Error(`'${selector}' is not a valid selector`);
  1859. }
  1860. if (
  1861. !nextTokenValue ||
  1862. doesRegularContinueAfterSpace(
  1863. nextTokenType,
  1864. nextTokenValue
  1865. ) ||
  1866. context.isAttributeBracketsOpen
  1867. ) {
  1868. updateBufferNode(context, tokenValue);
  1869. }
  1870. }
  1871. if (isAbsolutePseudoClassNode(bufferNode)) {
  1872. updateBufferNode(context, tokenValue);
  1873. }
  1874. if (isRelativePseudoClassNode(bufferNode)) {
  1875. initRelativeSubtree(context);
  1876. }
  1877. if (isSelectorNode(bufferNode)) {
  1878. if (
  1879. doesRegularContinueAfterSpace(nextTokenType, nextTokenValue)
  1880. ) {
  1881. addAstNodeByType(context, NODE.REGULAR_SELECTOR);
  1882. }
  1883. }
  1884. break;
  1885. case DESCENDANT_COMBINATOR:
  1886. case CHILD_COMBINATOR:
  1887. case NEXT_SIBLING_COMBINATOR:
  1888. case SUBSEQUENT_SIBLING_COMBINATOR:
  1889. case SEMICOLON:
  1890. case SLASH:
  1891. case BACKSLASH:
  1892. case SINGLE_QUOTE:
  1893. case DOUBLE_QUOTE:
  1894. case CARET:
  1895. case DOLLAR_SIGN:
  1896. case BRACKET.CURLY.LEFT:
  1897. case BRACKET.CURLY.RIGHT:
  1898. case ASTERISK:
  1899. case ID_MARKER:
  1900. case CLASS_MARKER:
  1901. case BRACKET.SQUARE.LEFT:
  1902. if (COMBINATORS.includes(tokenValue)) {
  1903. if (bufferNode === null) {
  1904. throw new Error(`'${selector}' is not a valid selector`);
  1905. }
  1906. bufferNode = getUpdatedBufferNode(context);
  1907. }
  1908. if (bufferNode === null) {
  1909. initAst(context, tokenValue);
  1910. if (isAttributeOpening(tokenValue, prevTokenValue)) {
  1911. context.isAttributeBracketsOpen = true;
  1912. }
  1913. } else if (isRegularSelectorNode(bufferNode)) {
  1914. if (
  1915. tokenValue === BRACKET.CURLY.LEFT &&
  1916. !(context.isAttributeBracketsOpen || context.isRegexpOpen)
  1917. ) {
  1918. throw new Error(`'${selector}' is not a valid selector`);
  1919. }
  1920. updateBufferNode(context, tokenValue);
  1921. if (isAttributeOpening(tokenValue, prevTokenValue)) {
  1922. context.isAttributeBracketsOpen = true;
  1923. }
  1924. } else if (isAbsolutePseudoClassNode(bufferNode)) {
  1925. updateBufferNode(context, tokenValue);
  1926. if (
  1927. tokenValue === SLASH &&
  1928. context.extendedPseudoNamesStack.length > 0
  1929. ) {
  1930. if (
  1931. prevTokenValue === SLASH &&
  1932. prevToPrevTokenValue === BACKSLASH
  1933. ) {
  1934. context.isRegexpOpen = false;
  1935. } else if (prevTokenValue && prevTokenValue !== BACKSLASH) {
  1936. if (
  1937. isRegexpOpening(
  1938. context,
  1939. prevTokenValue,
  1940. getNodeValue(bufferNode)
  1941. )
  1942. ) {
  1943. context.isRegexpOpen = !context.isRegexpOpen;
  1944. } else {
  1945. context.isRegexpOpen = false;
  1946. }
  1947. }
  1948. }
  1949. } else if (isRelativePseudoClassNode(bufferNode)) {
  1950. initRelativeSubtree(context, tokenValue);
  1951. if (isAttributeOpening(tokenValue, prevTokenValue)) {
  1952. context.isAttributeBracketsOpen = true;
  1953. }
  1954. } else if (isSelectorNode(bufferNode)) {
  1955. if (COMBINATORS.includes(tokenValue)) {
  1956. addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  1957. } else if (!context.isRegexpOpen) {
  1958. bufferNode = getContextLastRegularSelectorNode(context);
  1959. updateBufferNode(context, tokenValue);
  1960. if (isAttributeOpening(tokenValue, prevTokenValue)) {
  1961. context.isAttributeBracketsOpen = true;
  1962. }
  1963. }
  1964. } else if (isSelectorListNode(bufferNode)) {
  1965. addAstNodeByType(context, NODE.SELECTOR);
  1966. addAstNodeByType(context, NODE.REGULAR_SELECTOR, tokenValue);
  1967. if (isAttributeOpening(tokenValue, prevTokenValue)) {
  1968. context.isAttributeBracketsOpen = true;
  1969. }
  1970. }
  1971. break;
  1972. case BRACKET.SQUARE.RIGHT:
  1973. if (isRegularSelectorNode(bufferNode)) {
  1974. if (
  1975. !context.isAttributeBracketsOpen &&
  1976. prevTokenValue !== BACKSLASH
  1977. ) {
  1978. throw new Error(
  1979. `'${selector}' is not a valid selector due to '${tokenValue}' after '${getNodeValue(
  1980. bufferNode
  1981. )}'`
  1982. );
  1983. }
  1984. if (isAttributeClosing(context)) {
  1985. context.isAttributeBracketsOpen = false;
  1986. context.attributeBuffer = "";
  1987. }
  1988. updateBufferNode(context, tokenValue);
  1989. }
  1990. if (isAbsolutePseudoClassNode(bufferNode)) {
  1991. updateBufferNode(context, tokenValue);
  1992. }
  1993. break;
  1994. case COLON:
  1995. if (
  1996. isWhiteSpaceChar(nextTokenValue) &&
  1997. nextToNextTokenValue &&
  1998. SUPPORTED_PSEUDO_CLASSES.includes(nextToNextTokenValue)
  1999. ) {
  2000. throw new Error(`${NO_WHITESPACE_ERROR_PREFIX}: '${selector}'`);
  2001. }
  2002. if (bufferNode === null) {
  2003. if (nextTokenValue === XPATH_PSEUDO_CLASS_MARKER) {
  2004. initAst(context, XPATH_PSEUDO_SELECTING_ROOT);
  2005. } else if (
  2006. nextTokenValue === UPWARD_PSEUDO_CLASS_MARKER ||
  2007. nextTokenValue === NTH_ANCESTOR_PSEUDO_CLASS_MARKER
  2008. ) {
  2009. throw new Error(
  2010. `${NO_SELECTOR_ERROR_PREFIX} before :${nextTokenValue}() pseudo-class`
  2011. );
  2012. } else {
  2013. initAst(context, ASTERISK);
  2014. }
  2015. bufferNode = getBufferNode(context);
  2016. }
  2017. if (isSelectorListNode(bufferNode)) {
  2018. addAstNodeByType(context, NODE.SELECTOR);
  2019. addAstNodeByType(context, NODE.REGULAR_SELECTOR);
  2020. bufferNode = getBufferNode(context);
  2021. }
  2022. if (isRegularSelectorNode(bufferNode)) {
  2023. if (
  2024. (prevTokenValue && COMBINATORS.includes(prevTokenValue)) ||
  2025. prevTokenValue === COMMA
  2026. ) {
  2027. updateBufferNode(context, ASTERISK);
  2028. }
  2029. handleNextTokenOnColon(
  2030. context,
  2031. selector,
  2032. tokenValue,
  2033. nextTokenValue,
  2034. nextToNextTokenValue
  2035. );
  2036. }
  2037. if (isSelectorNode(bufferNode)) {
  2038. if (!nextTokenValue) {
  2039. throw new Error(
  2040. `Invalid colon ':' at the end of selector: '${selector}'`
  2041. );
  2042. }
  2043. if (isSupportedPseudoClass(nextTokenValue.toLowerCase())) {
  2044. addAstNodeByType(context, NODE.EXTENDED_SELECTOR);
  2045. } else if (
  2046. nextTokenValue.toLowerCase() === REMOVE_PSEUDO_MARKER
  2047. ) {
  2048. throw new Error(
  2049. `${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`
  2050. );
  2051. } else {
  2052. bufferNode = getContextLastRegularSelectorNode(context);
  2053. handleNextTokenOnColon(
  2054. context,
  2055. selector,
  2056. tokenValue,
  2057. nextTokenType,
  2058. nextToNextTokenValue
  2059. );
  2060. }
  2061. }
  2062. if (isAbsolutePseudoClassNode(bufferNode)) {
  2063. if (
  2064. getNodeName(bufferNode) === XPATH_PSEUDO_CLASS_MARKER &&
  2065. nextTokenValue &&
  2066. SUPPORTED_PSEUDO_CLASSES.includes(nextTokenValue) &&
  2067. nextToNextTokenValue === BRACKET.PARENTHESES.LEFT
  2068. ) {
  2069. throw new Error(
  2070. `:xpath() pseudo-class should be the last in selector: '${selector}'`
  2071. );
  2072. }
  2073. updateBufferNode(context, tokenValue);
  2074. }
  2075. if (isRelativePseudoClassNode(bufferNode)) {
  2076. if (!nextTokenValue) {
  2077. throw new Error(
  2078. `Invalid pseudo-class arg at the end of selector: '${selector}'`
  2079. );
  2080. }
  2081. initRelativeSubtree(context, ASTERISK);
  2082. if (!isSupportedPseudoClass(nextTokenValue.toLowerCase())) {
  2083. updateBufferNode(context, tokenValue);
  2084. if (nextToNextTokenValue === BRACKET.PARENTHESES.LEFT) {
  2085. context.standardPseudoNamesStack.push(nextTokenValue);
  2086. }
  2087. } else {
  2088. upToClosest(context, NODE.SELECTOR);
  2089. addAstNodeByType(context, NODE.EXTENDED_SELECTOR);
  2090. }
  2091. }
  2092. break;
  2093. case BRACKET.PARENTHESES.LEFT:
  2094. if (isAbsolutePseudoClassNode(bufferNode)) {
  2095. if (
  2096. getNodeName(bufferNode) !== XPATH_PSEUDO_CLASS_MARKER &&
  2097. context.isRegexpOpen
  2098. ) {
  2099. updateBufferNode(context, tokenValue);
  2100. } else {
  2101. context.extendedPseudoBracketsStack.push(tokenValue);
  2102. if (
  2103. context.extendedPseudoBracketsStack.length >
  2104. context.extendedPseudoNamesStack.length
  2105. ) {
  2106. updateBufferNode(context, tokenValue);
  2107. }
  2108. }
  2109. }
  2110. if (isRegularSelectorNode(bufferNode)) {
  2111. if (context.standardPseudoNamesStack.length > 0) {
  2112. updateBufferNode(context, tokenValue);
  2113. context.standardPseudoBracketsStack.push(tokenValue);
  2114. }
  2115. if (context.isAttributeBracketsOpen) {
  2116. updateBufferNode(context, tokenValue);
  2117. }
  2118. }
  2119. if (isRelativePseudoClassNode(bufferNode)) {
  2120. context.extendedPseudoBracketsStack.push(tokenValue);
  2121. }
  2122. break;
  2123. case BRACKET.PARENTHESES.RIGHT:
  2124. if (isAbsolutePseudoClassNode(bufferNode)) {
  2125. if (
  2126. getNodeName(bufferNode) !== XPATH_PSEUDO_CLASS_MARKER &&
  2127. context.isRegexpOpen
  2128. ) {
  2129. updateBufferNode(context, tokenValue);
  2130. } else {
  2131. context.extendedPseudoBracketsStack.pop();
  2132. if (getNodeName(bufferNode) !== XPATH_PSEUDO_CLASS_MARKER) {
  2133. context.extendedPseudoNamesStack.pop();
  2134. if (
  2135. context.extendedPseudoBracketsStack.length >
  2136. context.extendedPseudoNamesStack.length
  2137. ) {
  2138. updateBufferNode(context, tokenValue);
  2139. } else if (
  2140. context.extendedPseudoBracketsStack.length >= 0 &&
  2141. context.extendedPseudoNamesStack.length >= 0
  2142. ) {
  2143. upToClosest(context, NODE.SELECTOR);
  2144. }
  2145. } else {
  2146. if (
  2147. context.extendedPseudoBracketsStack.length <
  2148. context.extendedPseudoNamesStack.length
  2149. ) {
  2150. context.extendedPseudoNamesStack.pop();
  2151. } else {
  2152. updateBufferNode(context, tokenValue);
  2153. }
  2154. }
  2155. }
  2156. }
  2157. if (isRegularSelectorNode(bufferNode)) {
  2158. if (context.isAttributeBracketsOpen) {
  2159. updateBufferNode(context, tokenValue);
  2160. } else if (
  2161. context.standardPseudoNamesStack.length > 0 &&
  2162. context.standardPseudoBracketsStack.length > 0
  2163. ) {
  2164. updateBufferNode(context, tokenValue);
  2165. context.standardPseudoBracketsStack.pop();
  2166. const lastStandardPseudo =
  2167. context.standardPseudoNamesStack.pop();
  2168. if (!lastStandardPseudo) {
  2169. throw new Error(
  2170. `Parsing error. Invalid selector: ${selector}`
  2171. );
  2172. }
  2173. if (
  2174. Object.values(REGULAR_PSEUDO_ELEMENTS).includes(
  2175. lastStandardPseudo
  2176. ) &&
  2177. nextTokenValue === COLON &&
  2178. nextToNextTokenValue &&
  2179. HAS_PSEUDO_CLASS_MARKERS.includes(nextToNextTokenValue)
  2180. ) {
  2181. throw new Error(
  2182. `Usage of :${nextToNextTokenValue}() pseudo-class is not allowed after any regular pseudo-element: '${lastStandardPseudo}'`
  2183. );
  2184. }
  2185. } else {
  2186. context.extendedPseudoBracketsStack.pop();
  2187. context.extendedPseudoNamesStack.pop();
  2188. upToClosest(context, NODE.EXTENDED_SELECTOR);
  2189. upToClosest(context, NODE.SELECTOR);
  2190. }
  2191. }
  2192. if (isSelectorNode(bufferNode)) {
  2193. context.extendedPseudoBracketsStack.pop();
  2194. context.extendedPseudoNamesStack.pop();
  2195. upToClosest(context, NODE.EXTENDED_SELECTOR);
  2196. upToClosest(context, NODE.SELECTOR);
  2197. }
  2198. if (isRelativePseudoClassNode(bufferNode)) {
  2199. if (
  2200. context.extendedPseudoNamesStack.length > 0 &&
  2201. context.extendedPseudoBracketsStack.length > 0
  2202. ) {
  2203. context.extendedPseudoBracketsStack.pop();
  2204. context.extendedPseudoNamesStack.pop();
  2205. }
  2206. }
  2207. break;
  2208. case LINE_FEED:
  2209. case FORM_FEED:
  2210. case CARRIAGE_RETURN:
  2211. throw new Error(`'${selector}' is not a valid selector`);
  2212. case TAB:
  2213. if (
  2214. isRegularSelectorNode(bufferNode) &&
  2215. context.isAttributeBracketsOpen
  2216. ) {
  2217. updateBufferNode(context, tokenValue);
  2218. } else {
  2219. throw new Error(`'${selector}' is not a valid selector`);
  2220. }
  2221. }
  2222. break;
  2223. default:
  2224. throw new Error(`Unknown type of token: '${tokenValue}'`);
  2225. }
  2226. i += 1;
  2227. }
  2228. if (context.ast === null) {
  2229. throw new Error(`'${selector}' is not a valid selector`);
  2230. }
  2231. if (
  2232. context.extendedPseudoNamesStack.length > 0 ||
  2233. context.extendedPseudoBracketsStack.length > 0
  2234. ) {
  2235. throw new Error(
  2236. `Unbalanced brackets for extended pseudo-class: '${getLast(
  2237. context.extendedPseudoNamesStack
  2238. )}'`
  2239. );
  2240. }
  2241. if (context.isAttributeBracketsOpen) {
  2242. throw new Error(
  2243. `Unbalanced attribute brackets in selector: '${selector}'`
  2244. );
  2245. }
  2246. return context.shouldOptimize ? optimizeAst(context.ast) : context.ast;
  2247. };
  2248. const natives = {
  2249. MutationObserver: window.MutationObserver || window.WebKitMutationObserver,
  2250. };
  2251. class NativeTextContent {
  2252. constructor() {
  2253. this.nativeNode = window.Node || Node;
  2254. }
  2255. setGetter() {
  2256. var _Object$getOwnPropert;
  2257. this.getter =
  2258. (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(
  2259. this.nativeNode.prototype,
  2260. "textContent"
  2261. )) === null || _Object$getOwnPropert === void 0
  2262. ? void 0
  2263. : _Object$getOwnPropert.get;
  2264. }
  2265. }
  2266. const nativeTextContent = new NativeTextContent();
  2267. const getNodeTextContent = (domElement) => {
  2268. if (nativeTextContent.getter) {
  2269. return nativeTextContent.getter.apply(domElement);
  2270. }
  2271. return domElement.textContent || "";
  2272. };
  2273. const getElementSelectorDesc = (element) => {
  2274. let selectorText = element.tagName.toLowerCase();
  2275. selectorText += Array.from(element.attributes)
  2276. .map((attr) => {
  2277. return `[${attr.name}="${element.getAttribute(attr.name)}"]`;
  2278. })
  2279. .join("");
  2280. return selectorText;
  2281. };
  2282. const getElementSelectorPath = (inputEl) => {
  2283. if (!(inputEl instanceof Element)) {
  2284. throw new Error("Function received argument with wrong type");
  2285. }
  2286. let el;
  2287. el = inputEl;
  2288. const path = [];
  2289. while (!!el && el.nodeType === Node.ELEMENT_NODE) {
  2290. let selector = el.nodeName.toLowerCase();
  2291. if (el.id && typeof el.id === "string") {
  2292. selector += `#${el.id}`;
  2293. path.unshift(selector);
  2294. break;
  2295. }
  2296. let sibling = el;
  2297. let nth = 1;
  2298. while (sibling.previousElementSibling) {
  2299. sibling = sibling.previousElementSibling;
  2300. if (
  2301. sibling.nodeType === Node.ELEMENT_NODE &&
  2302. sibling.nodeName.toLowerCase() === selector
  2303. ) {
  2304. nth += 1;
  2305. }
  2306. }
  2307. if (nth !== 1) {
  2308. selector += `:nth-of-type(${nth})`;
  2309. }
  2310. path.unshift(selector);
  2311. el = el.parentElement;
  2312. }
  2313. return path.join(" > ");
  2314. };
  2315. const isHtmlElement = (element) => {
  2316. return element instanceof HTMLElement;
  2317. };
  2318. const getParent = (element, errorMessage) => {
  2319. const { parentElement } = element;
  2320. if (!parentElement) {
  2321. throw new Error(errorMessage || "Element does no have parent element");
  2322. }
  2323. return parentElement;
  2324. };
  2325. const isErrorWithMessage = (error) => {
  2326. return (
  2327. typeof error === "object" &&
  2328. error !== null &&
  2329. "message" in error &&
  2330. typeof error.message === "string"
  2331. );
  2332. };
  2333. const toErrorWithMessage = (maybeError) => {
  2334. if (isErrorWithMessage(maybeError)) {
  2335. return maybeError;
  2336. }
  2337. try {
  2338. return new Error(JSON.stringify(maybeError));
  2339. } catch {
  2340. return new Error(String(maybeError));
  2341. }
  2342. };
  2343. const getErrorMessage = (error) => {
  2344. return toErrorWithMessage(error).message;
  2345. };
  2346. const logger = {
  2347. error:
  2348. typeof console !== "undefined" && console.error && console.error.bind
  2349. ? console.error.bind(window.console)
  2350. : console.error,
  2351. info:
  2352. typeof console !== "undefined" && console.info && console.info.bind
  2353. ? console.info.bind(window.console)
  2354. : console.info,
  2355. };
  2356. const removeSuffix = (str, suffix) => {
  2357. const index = str.indexOf(suffix, str.length - suffix.length);
  2358. if (index >= 0) {
  2359. return str.substring(0, index);
  2360. }
  2361. return str;
  2362. };
  2363. const replaceAll = (input, pattern, replacement) => {
  2364. if (!input) {
  2365. return input;
  2366. }
  2367. return input.split(pattern).join(replacement);
  2368. };
  2369. const toRegExp = (str) => {
  2370. if (str.startsWith(SLASH) && str.endsWith(SLASH)) {
  2371. return new RegExp(str.slice(1, -1));
  2372. }
  2373. const escaped = str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  2374. return new RegExp(escaped);
  2375. };
  2376. const convertTypeIntoString = (value) => {
  2377. let output;
  2378. switch (value) {
  2379. case undefined:
  2380. output = "undefined";
  2381. break;
  2382. case null:
  2383. output = "null";
  2384. break;
  2385. default:
  2386. output = value.toString();
  2387. }
  2388. return output;
  2389. };
  2390. const convertTypeFromString = (value) => {
  2391. const numValue = Number(value);
  2392. let output;
  2393. if (!Number.isNaN(numValue)) {
  2394. output = numValue;
  2395. } else {
  2396. switch (value) {
  2397. case "undefined":
  2398. output = undefined;
  2399. break;
  2400. case "null":
  2401. output = null;
  2402. break;
  2403. case "true":
  2404. output = true;
  2405. break;
  2406. case "false":
  2407. output = false;
  2408. break;
  2409. default:
  2410. output = value;
  2411. }
  2412. }
  2413. return output;
  2414. };
  2415. const SAFARI_USER_AGENT_REGEXP = /\sVersion\/(\d{2}\.\d)(.+\s|\s)(Safari)\//;
  2416. const isSafariBrowser = SAFARI_USER_AGENT_REGEXP.test(navigator.userAgent);
  2417. const isUserAgentSupported = (userAgent) => {
  2418. if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) {
  2419. return false;
  2420. }
  2421. return true;
  2422. };
  2423. const isBrowserSupported = () => {
  2424. return isUserAgentSupported(navigator.userAgent);
  2425. };
  2426. const CSS_PROPERTY = {
  2427. BACKGROUND: "background",
  2428. BACKGROUND_IMAGE: "background-image",
  2429. CONTENT: "content",
  2430. OPACITY: "opacity",
  2431. };
  2432. const REGEXP_ANY_SYMBOL = ".*";
  2433. const REGEXP_WITH_FLAGS_REGEXP = /^\s*\/.*\/[gmisuy]*\s*$/;
  2434. const removeContentQuotes = (str) => {
  2435. return str.replace(/^(["'])([\s\S]*)\1$/, "$2");
  2436. };
  2437. const addUrlPropertyQuotes = (str) => {
  2438. if (!str.includes('url("')) {
  2439. const re = /url\((.*?)\)/g;
  2440. return str.replace(re, 'url("$1")');
  2441. }
  2442. return str;
  2443. };
  2444. const addUrlQuotesTo = {
  2445. regexpArg: (str) => {
  2446. const re = /(\^)?url(\\)?\\\((\w|\[\w)/g;
  2447. return str.replace(re, '$1url$2\\(\\"?$3');
  2448. },
  2449. noneRegexpArg: addUrlPropertyQuotes,
  2450. };
  2451. const escapeRegExp = (str) => {
  2452. const specials = [
  2453. ".",
  2454. "+",
  2455. "?",
  2456. "$",
  2457. "{",
  2458. "}",
  2459. "(",
  2460. ")",
  2461. "[",
  2462. "]",
  2463. "\\",
  2464. "/",
  2465. ];
  2466. const specialsRegex = new RegExp(`[${specials.join("\\")}]`, "g");
  2467. return str.replace(specialsRegex, "\\$&");
  2468. };
  2469. const convertStyleMatchValueToRegexp = (rawValue) => {
  2470. let value;
  2471. if (rawValue.startsWith(SLASH) && rawValue.endsWith(SLASH)) {
  2472. value = addUrlQuotesTo.regexpArg(rawValue);
  2473. value = value.slice(1, -1);
  2474. } else {
  2475. value = addUrlQuotesTo.noneRegexpArg(rawValue);
  2476. value = value.replace(/\\([\\()[\]"])/g, "$1");
  2477. value = escapeRegExp(value);
  2478. value = replaceAll(value, ASTERISK, REGEXP_ANY_SYMBOL);
  2479. }
  2480. return new RegExp(value, "i");
  2481. };
  2482. const normalizePropertyValue = (propertyName, propertyValue) => {
  2483. let normalized = "";
  2484. switch (propertyName) {
  2485. case CSS_PROPERTY.BACKGROUND:
  2486. case CSS_PROPERTY.BACKGROUND_IMAGE:
  2487. normalized = addUrlPropertyQuotes(propertyValue);
  2488. break;
  2489. case CSS_PROPERTY.CONTENT:
  2490. normalized = removeContentQuotes(propertyValue);
  2491. break;
  2492. case CSS_PROPERTY.OPACITY:
  2493. normalized = isSafariBrowser
  2494. ? (Math.round(parseFloat(propertyValue) * 100) / 100).toString()
  2495. : propertyValue;
  2496. break;
  2497. default:
  2498. normalized = propertyValue;
  2499. }
  2500. return normalized;
  2501. };
  2502. const getComputedStylePropertyValue = (
  2503. domElement,
  2504. propertyName,
  2505. regularPseudoElement
  2506. ) => {
  2507. const style = window.getComputedStyle(domElement, regularPseudoElement);
  2508. const propertyValue = style.getPropertyValue(propertyName);
  2509. return normalizePropertyValue(propertyName, propertyValue);
  2510. };
  2511. const getPseudoArgData = (pseudoArg, separator) => {
  2512. const index = pseudoArg.indexOf(separator);
  2513. let name;
  2514. let value;
  2515. if (index > -1) {
  2516. name = pseudoArg.substring(0, index).trim();
  2517. value = pseudoArg.substring(index + 1).trim();
  2518. } else {
  2519. name = pseudoArg;
  2520. }
  2521. return {
  2522. name,
  2523. value,
  2524. };
  2525. };
  2526. const parseStyleMatchArg = (pseudoName, rawArg) => {
  2527. const { name, value } = getPseudoArgData(rawArg, COMMA);
  2528. let regularPseudoElement = name;
  2529. let styleMatchArg = value;
  2530. if (!Object.values(REGULAR_PSEUDO_ELEMENTS).includes(name)) {
  2531. regularPseudoElement = null;
  2532. styleMatchArg = rawArg;
  2533. }
  2534. if (!styleMatchArg) {
  2535. throw new Error(
  2536. `Required style property argument part is missing in :${pseudoName}() arg: '${rawArg}'`
  2537. );
  2538. }
  2539. if (regularPseudoElement) {
  2540. regularPseudoElement = `${COLON}${COLON}${regularPseudoElement}`;
  2541. }
  2542. return {
  2543. regularPseudoElement,
  2544. styleMatchArg,
  2545. };
  2546. };
  2547. const isStyleMatched = (argsData) => {
  2548. const { pseudoName, pseudoArg, domElement } = argsData;
  2549. const { regularPseudoElement, styleMatchArg } = parseStyleMatchArg(
  2550. pseudoName,
  2551. pseudoArg
  2552. );
  2553. const { name: matchName, value: matchValue } = getPseudoArgData(
  2554. styleMatchArg,
  2555. COLON
  2556. );
  2557. if (!matchName || !matchValue) {
  2558. throw new Error(
  2559. `Required property name or value is missing in :${pseudoName}() arg: '${styleMatchArg}'`
  2560. );
  2561. }
  2562. let valueRegexp;
  2563. try {
  2564. valueRegexp = convertStyleMatchValueToRegexp(matchValue);
  2565. } catch (e) {
  2566. logger.error(getErrorMessage(e));
  2567. throw new Error(
  2568. `Invalid argument of :${pseudoName}() pseudo-class: '${styleMatchArg}'`
  2569. );
  2570. }
  2571. const value = getComputedStylePropertyValue(
  2572. domElement,
  2573. matchName,
  2574. regularPseudoElement
  2575. );
  2576. return valueRegexp && valueRegexp.test(value);
  2577. };
  2578. const validateStrMatcherArg = (arg) => {
  2579. if (arg.includes(SLASH)) {
  2580. return false;
  2581. }
  2582. if (!/^[\w-]+$/.test(arg)) {
  2583. return false;
  2584. }
  2585. return true;
  2586. };
  2587. const getValidMatcherArg = function (rawArg) {
  2588. let isWildcardAllowed =
  2589. arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  2590. let arg;
  2591. if (
  2592. rawArg.length > 1 &&
  2593. rawArg.startsWith(DOUBLE_QUOTE) &&
  2594. rawArg.endsWith(DOUBLE_QUOTE)
  2595. ) {
  2596. rawArg = rawArg.slice(1, -1);
  2597. }
  2598. if (rawArg === "") {
  2599. throw new Error("Argument should be specified. Empty arg is invalid.");
  2600. }
  2601. if (rawArg.startsWith(SLASH) && rawArg.endsWith(SLASH)) {
  2602. if (rawArg.length > 2) {
  2603. arg = toRegExp(rawArg);
  2604. } else {
  2605. throw new Error(`Invalid regexp: '${rawArg}'`);
  2606. }
  2607. } else if (rawArg.includes(ASTERISK)) {
  2608. if (rawArg === ASTERISK && !isWildcardAllowed) {
  2609. throw new Error(`Argument should be more specific than ${rawArg}`);
  2610. }
  2611. arg = replaceAll(rawArg, ASTERISK, REGEXP_ANY_SYMBOL);
  2612. arg = new RegExp(arg);
  2613. } else {
  2614. if (!validateStrMatcherArg(rawArg)) {
  2615. throw new Error(`Invalid argument: '${rawArg}'`);
  2616. }
  2617. arg = rawArg;
  2618. }
  2619. return arg;
  2620. };
  2621. const getRawMatchingData = (pseudoName, pseudoArg) => {
  2622. const { name: rawName, value: rawValue } = getPseudoArgData(
  2623. pseudoArg,
  2624. EQUAL_SIGN
  2625. );
  2626. if (!rawName) {
  2627. throw new Error(
  2628. `Required attribute name is missing in :${pseudoName} arg: ${pseudoArg}`
  2629. );
  2630. }
  2631. return {
  2632. rawName,
  2633. rawValue,
  2634. };
  2635. };
  2636. const isAttributeMatched = (argsData) => {
  2637. const { pseudoName, pseudoArg, domElement } = argsData;
  2638. const elementAttributes = domElement.attributes;
  2639. if (elementAttributes.length === 0) {
  2640. return false;
  2641. }
  2642. const { rawName: rawAttrName, rawValue: rawAttrValue } = getRawMatchingData(
  2643. pseudoName,
  2644. pseudoArg
  2645. );
  2646. let attrNameMatch;
  2647. try {
  2648. attrNameMatch = getValidMatcherArg(rawAttrName);
  2649. } catch (e) {
  2650. const errorMessage = getErrorMessage(e);
  2651. logger.error(errorMessage);
  2652. throw new SyntaxError(errorMessage);
  2653. }
  2654. let isMatched = false;
  2655. let i = 0;
  2656. while (i < elementAttributes.length && !isMatched) {
  2657. const attr = elementAttributes[i];
  2658. if (!attr) {
  2659. break;
  2660. }
  2661. const isNameMatched =
  2662. attrNameMatch instanceof RegExp
  2663. ? attrNameMatch.test(attr.name)
  2664. : attrNameMatch === attr.name;
  2665. if (!rawAttrValue) {
  2666. isMatched = isNameMatched;
  2667. } else {
  2668. let attrValueMatch;
  2669. try {
  2670. attrValueMatch = getValidMatcherArg(rawAttrValue);
  2671. } catch (e) {
  2672. const errorMessage = getErrorMessage(e);
  2673. logger.error(errorMessage);
  2674. throw new SyntaxError(errorMessage);
  2675. }
  2676. const isValueMatched =
  2677. attrValueMatch instanceof RegExp
  2678. ? attrValueMatch.test(attr.value)
  2679. : attrValueMatch === attr.value;
  2680. isMatched = isNameMatched && isValueMatched;
  2681. }
  2682. i += 1;
  2683. }
  2684. return isMatched;
  2685. };
  2686. const parseRawPropChain = (input) => {
  2687. if (
  2688. input.length > 1 &&
  2689. input.startsWith(DOUBLE_QUOTE) &&
  2690. input.endsWith(DOUBLE_QUOTE)
  2691. ) {
  2692. input = input.slice(1, -1);
  2693. }
  2694. const chainChunks = input.split(DOT);
  2695. const chainPatterns = [];
  2696. let patternBuffer = "";
  2697. let isRegexpPattern = false;
  2698. let i = 0;
  2699. while (i < chainChunks.length) {
  2700. const chunk = getItemByIndex(
  2701. chainChunks,
  2702. i,
  2703. `Invalid pseudo-class arg: '${input}'`
  2704. );
  2705. if (
  2706. chunk.startsWith(SLASH) &&
  2707. chunk.endsWith(SLASH) &&
  2708. chunk.length > 2
  2709. ) {
  2710. chainPatterns.push(chunk);
  2711. } else if (chunk.startsWith(SLASH)) {
  2712. isRegexpPattern = true;
  2713. patternBuffer += chunk;
  2714. } else if (chunk.endsWith(SLASH)) {
  2715. isRegexpPattern = false;
  2716. patternBuffer += `.${chunk}`;
  2717. chainPatterns.push(patternBuffer);
  2718. patternBuffer = "";
  2719. } else {
  2720. if (isRegexpPattern) {
  2721. patternBuffer += chunk;
  2722. } else {
  2723. chainPatterns.push(chunk);
  2724. }
  2725. }
  2726. i += 1;
  2727. }
  2728. if (patternBuffer.length > 0) {
  2729. throw new Error(`Invalid regexp property pattern '${input}'`);
  2730. }
  2731. const chainMatchPatterns = chainPatterns.map((pattern) => {
  2732. if (pattern.length === 0) {
  2733. throw new Error(
  2734. `Empty pattern '${pattern}' is invalid in chain '${input}'`
  2735. );
  2736. }
  2737. let validPattern;
  2738. try {
  2739. validPattern = getValidMatcherArg(pattern, true);
  2740. } catch (e) {
  2741. logger.error(getErrorMessage(e));
  2742. throw new Error(
  2743. `Invalid property pattern '${pattern}' in property chain '${input}'`
  2744. );
  2745. }
  2746. return validPattern;
  2747. });
  2748. return chainMatchPatterns;
  2749. };
  2750. const filterRootsByRegexpChain = function (base, chain) {
  2751. let output =
  2752. arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
  2753. const tempProp = getFirst(chain);
  2754. if (chain.length === 1) {
  2755. let key;
  2756. for (key in base) {
  2757. if (tempProp instanceof RegExp) {
  2758. if (tempProp.test(key)) {
  2759. output.push({
  2760. base,
  2761. prop: key,
  2762. value: base[key],
  2763. });
  2764. }
  2765. } else if (tempProp === key) {
  2766. output.push({
  2767. base,
  2768. prop: tempProp,
  2769. value: base[key],
  2770. });
  2771. }
  2772. }
  2773. return output;
  2774. }
  2775. if (tempProp instanceof RegExp) {
  2776. const nextProp = chain.slice(1);
  2777. const baseKeys = [];
  2778. for (const key in base) {
  2779. if (tempProp.test(key)) {
  2780. baseKeys.push(key);
  2781. }
  2782. }
  2783. baseKeys.forEach((key) => {
  2784. var _Object$getOwnPropert;
  2785. const item =
  2786. (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(
  2787. base,
  2788. key
  2789. )) === null || _Object$getOwnPropert === void 0
  2790. ? void 0
  2791. : _Object$getOwnPropert.value;
  2792. filterRootsByRegexpChain(item, nextProp, output);
  2793. });
  2794. }
  2795. if (base && typeof tempProp === "string") {
  2796. var _Object$getOwnPropert2;
  2797. const nextBase =
  2798. (_Object$getOwnPropert2 = Object.getOwnPropertyDescriptor(
  2799. base,
  2800. tempProp
  2801. )) === null || _Object$getOwnPropert2 === void 0
  2802. ? void 0
  2803. : _Object$getOwnPropert2.value;
  2804. chain = chain.slice(1);
  2805. if (nextBase !== undefined) {
  2806. filterRootsByRegexpChain(nextBase, chain, output);
  2807. }
  2808. }
  2809. return output;
  2810. };
  2811. const isPropertyMatched = (argsData) => {
  2812. const { pseudoName, pseudoArg, domElement } = argsData;
  2813. const { rawName: rawPropertyName, rawValue: rawPropertyValue } =
  2814. getRawMatchingData(pseudoName, pseudoArg);
  2815. if (rawPropertyName.includes("\\/") || rawPropertyName.includes("\\.")) {
  2816. throw new Error(
  2817. `Invalid :${pseudoName} name pattern: ${rawPropertyName}`
  2818. );
  2819. }
  2820. let propChainMatches;
  2821. try {
  2822. propChainMatches = parseRawPropChain(rawPropertyName);
  2823. } catch (e) {
  2824. const errorMessage = getErrorMessage(e);
  2825. logger.error(errorMessage);
  2826. throw new SyntaxError(errorMessage);
  2827. }
  2828. const ownerObjArr = filterRootsByRegexpChain(domElement, propChainMatches);
  2829. if (ownerObjArr.length === 0) {
  2830. return false;
  2831. }
  2832. let isMatched = true;
  2833. if (rawPropertyValue) {
  2834. let propValueMatch;
  2835. try {
  2836. propValueMatch = getValidMatcherArg(rawPropertyValue);
  2837. } catch (e) {
  2838. const errorMessage = getErrorMessage(e);
  2839. logger.error(errorMessage);
  2840. throw new SyntaxError(errorMessage);
  2841. }
  2842. if (propValueMatch) {
  2843. for (let i = 0; i < ownerObjArr.length; i += 1) {
  2844. var _ownerObjArr$i;
  2845. const realValue =
  2846. (_ownerObjArr$i = ownerObjArr[i]) === null ||
  2847. _ownerObjArr$i === void 0
  2848. ? void 0
  2849. : _ownerObjArr$i.value;
  2850. if (propValueMatch instanceof RegExp) {
  2851. isMatched = propValueMatch.test(convertTypeIntoString(realValue));
  2852. } else {
  2853. if (realValue === "null" || realValue === "undefined") {
  2854. isMatched = propValueMatch === realValue;
  2855. break;
  2856. }
  2857. isMatched = convertTypeFromString(propValueMatch) === realValue;
  2858. }
  2859. if (isMatched) {
  2860. break;
  2861. }
  2862. }
  2863. }
  2864. }
  2865. return isMatched;
  2866. };
  2867. const isTextMatched = (argsData) => {
  2868. const { pseudoName, pseudoArg, domElement } = argsData;
  2869. const textContent = getNodeTextContent(domElement);
  2870. let isTextContentMatched;
  2871. let pseudoArgToMatch = pseudoArg;
  2872. if (
  2873. pseudoArgToMatch.startsWith(SLASH) &&
  2874. REGEXP_WITH_FLAGS_REGEXP.test(pseudoArgToMatch)
  2875. ) {
  2876. const flagsIndex = pseudoArgToMatch.lastIndexOf("/");
  2877. const flagsStr = pseudoArgToMatch.substring(flagsIndex + 1);
  2878. pseudoArgToMatch = pseudoArgToMatch
  2879. .substring(0, flagsIndex + 1)
  2880. .slice(1, -1)
  2881. .replace(/\\([\\"])/g, "$1");
  2882. let regex;
  2883. try {
  2884. regex = new RegExp(pseudoArgToMatch, flagsStr);
  2885. } catch (e) {
  2886. throw new Error(
  2887. `Invalid argument of :${pseudoName}() pseudo-class: ${pseudoArg}`
  2888. );
  2889. }
  2890. isTextContentMatched = regex.test(textContent);
  2891. } else {
  2892. pseudoArgToMatch = pseudoArgToMatch.replace(/\\([\\()[\]"])/g, "$1");
  2893. isTextContentMatched = textContent.includes(pseudoArgToMatch);
  2894. }
  2895. return isTextContentMatched;
  2896. };
  2897. const getValidNumberAncestorArg = (rawArg, pseudoName) => {
  2898. const deep = Number(rawArg);
  2899. if (Number.isNaN(deep) || deep < 1 || deep >= 256) {
  2900. throw new Error(
  2901. `Invalid argument of :${pseudoName} pseudo-class: '${rawArg}'`
  2902. );
  2903. }
  2904. return deep;
  2905. };
  2906. const getNthAncestor = (domElement, nth, pseudoName) => {
  2907. let ancestor = null;
  2908. let i = 0;
  2909. while (i < nth) {
  2910. ancestor = domElement.parentElement;
  2911. if (!ancestor) {
  2912. throw new Error(
  2913. `Out of DOM: Argument of :${pseudoName}() pseudo-class is too big '${nth}'.`
  2914. );
  2915. }
  2916. domElement = ancestor;
  2917. i += 1;
  2918. }
  2919. return ancestor;
  2920. };
  2921. const validateStandardSelector = (selector) => {
  2922. let isValid;
  2923. try {
  2924. document.querySelectorAll(selector);
  2925. isValid = true;
  2926. } catch (e) {
  2927. isValid = false;
  2928. }
  2929. return isValid;
  2930. };
  2931. const matcherWrapper = (callback, argsData, errorMessage) => {
  2932. let isMatched;
  2933. try {
  2934. isMatched = callback(argsData);
  2935. } catch (e) {
  2936. logger.error(getErrorMessage(e));
  2937. throw new Error(errorMessage);
  2938. }
  2939. return isMatched;
  2940. };
  2941. const getAbsolutePseudoError = (propDesc, pseudoName, pseudoArg) => {
  2942. return `${MATCHING_ELEMENT_ERROR_PREFIX} ${propDesc}, may be invalid :${pseudoName}() pseudo-class arg: '${pseudoArg}'`;
  2943. };
  2944. const isMatchedByAbsolutePseudo = (domElement, pseudoName, pseudoArg) => {
  2945. let argsData;
  2946. let errorMessage;
  2947. let callback;
  2948. switch (pseudoName) {
  2949. case CONTAINS_PSEUDO:
  2950. case HAS_TEXT_PSEUDO:
  2951. case ABP_CONTAINS_PSEUDO:
  2952. callback = isTextMatched;
  2953. argsData = {
  2954. pseudoName,
  2955. pseudoArg,
  2956. domElement,
  2957. };
  2958. errorMessage = getAbsolutePseudoError(
  2959. "text content",
  2960. pseudoName,
  2961. pseudoArg
  2962. );
  2963. break;
  2964. case MATCHES_CSS_PSEUDO:
  2965. case MATCHES_CSS_AFTER_PSEUDO:
  2966. case MATCHES_CSS_BEFORE_PSEUDO:
  2967. callback = isStyleMatched;
  2968. argsData = {
  2969. pseudoName,
  2970. pseudoArg,
  2971. domElement,
  2972. };
  2973. errorMessage = getAbsolutePseudoError("style", pseudoName, pseudoArg);
  2974. break;
  2975. case MATCHES_ATTR_PSEUDO_CLASS_MARKER:
  2976. callback = isAttributeMatched;
  2977. argsData = {
  2978. domElement,
  2979. pseudoName,
  2980. pseudoArg,
  2981. };
  2982. errorMessage = getAbsolutePseudoError(
  2983. "attributes",
  2984. pseudoName,
  2985. pseudoArg
  2986. );
  2987. break;
  2988. case MATCHES_PROPERTY_PSEUDO_CLASS_MARKER:
  2989. callback = isPropertyMatched;
  2990. argsData = {
  2991. domElement,
  2992. pseudoName,
  2993. pseudoArg,
  2994. };
  2995. errorMessage = getAbsolutePseudoError(
  2996. "properties",
  2997. pseudoName,
  2998. pseudoArg
  2999. );
  3000. break;
  3001. default:
  3002. throw new Error(`Unknown absolute pseudo-class :${pseudoName}()`);
  3003. }
  3004. return matcherWrapper(callback, argsData, errorMessage);
  3005. };
  3006. const findByAbsolutePseudoPseudo = {
  3007. nthAncestor: (domElements, rawPseudoArg, pseudoName) => {
  3008. const deep = getValidNumberAncestorArg(rawPseudoArg, pseudoName);
  3009. const ancestors = domElements
  3010. .map((domElement) => {
  3011. let ancestor = null;
  3012. try {
  3013. ancestor = getNthAncestor(domElement, deep, pseudoName);
  3014. } catch (e) {
  3015. logger.error(getErrorMessage(e));
  3016. }
  3017. return ancestor;
  3018. })
  3019. .filter(isHtmlElement);
  3020. return ancestors;
  3021. },
  3022. xpath: (domElements, rawPseudoArg) => {
  3023. const foundElements = domElements.map((domElement) => {
  3024. const result = [];
  3025. let xpathResult;
  3026. try {
  3027. xpathResult = document.evaluate(
  3028. rawPseudoArg,
  3029. domElement,
  3030. null,
  3031. window.XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
  3032. null
  3033. );
  3034. } catch (e) {
  3035. logger.error(getErrorMessage(e));
  3036. throw new Error(
  3037. `Invalid argument of :xpath() pseudo-class: '${rawPseudoArg}'`
  3038. );
  3039. }
  3040. let node = xpathResult.iterateNext();
  3041. while (node) {
  3042. if (isHtmlElement(node)) {
  3043. result.push(node);
  3044. }
  3045. node = xpathResult.iterateNext();
  3046. }
  3047. return result;
  3048. });
  3049. return flatten(foundElements);
  3050. },
  3051. upward: (domElements, rawPseudoArg) => {
  3052. if (!validateStandardSelector(rawPseudoArg)) {
  3053. throw new Error(
  3054. `Invalid argument of :upward pseudo-class: '${rawPseudoArg}'`
  3055. );
  3056. }
  3057. const closestAncestors = domElements
  3058. .map((domElement) => {
  3059. const parent = domElement.parentElement;
  3060. if (!parent) {
  3061. return null;
  3062. }
  3063. return parent.closest(rawPseudoArg);
  3064. })
  3065. .filter(isHtmlElement);
  3066. return closestAncestors;
  3067. },
  3068. };
  3069. const scopeDirectChildren = `${SCOPE_CSS_PSEUDO_CLASS}${CHILD_COMBINATOR}`;
  3070. const scopeAnyChildren = `${SCOPE_CSS_PSEUDO_CLASS}${DESCENDANT_COMBINATOR}`;
  3071. const getFirstInnerRegularChild = (selectorNode, pseudoName) => {
  3072. return getFirstRegularChild(
  3073. selectorNode.children,
  3074. `RegularSelector is missing for :${pseudoName}() pseudo-class`
  3075. );
  3076. };
  3077. const hasRelativesBySelectorList = (argsData) => {
  3078. const { element, relativeSelectorList, pseudoName } = argsData;
  3079. return relativeSelectorList.children.every((selectorNode) => {
  3080. const relativeRegularSelector = getFirstInnerRegularChild(
  3081. selectorNode,
  3082. pseudoName
  3083. );
  3084. let specifiedSelector = "";
  3085. let rootElement = null;
  3086. const regularSelector = getNodeValue(relativeRegularSelector);
  3087. if (
  3088. regularSelector.startsWith(NEXT_SIBLING_COMBINATOR) ||
  3089. regularSelector.startsWith(SUBSEQUENT_SIBLING_COMBINATOR)
  3090. ) {
  3091. rootElement = element.parentElement;
  3092. const elementSelectorText = getElementSelectorDesc(element);
  3093. specifiedSelector = `${scopeDirectChildren}${elementSelectorText}${regularSelector}`;
  3094. } else if (regularSelector === ASTERISK) {
  3095. rootElement = element;
  3096. specifiedSelector = `${scopeAnyChildren}${ASTERISK}`;
  3097. } else {
  3098. specifiedSelector = `${scopeAnyChildren}${regularSelector}`;
  3099. rootElement = element;
  3100. }
  3101. if (!rootElement) {
  3102. throw new Error(
  3103. `Selection by :${pseudoName}() pseudo-class is not possible`
  3104. );
  3105. }
  3106. let relativeElements;
  3107. try {
  3108. relativeElements = getElementsForSelectorNode(
  3109. selectorNode,
  3110. rootElement,
  3111. specifiedSelector
  3112. );
  3113. } catch (e) {
  3114. logger.error(getErrorMessage(e));
  3115. throw new Error(
  3116. `Invalid selector for :${pseudoName}() pseudo-class: '${regularSelector}'`
  3117. );
  3118. }
  3119. return relativeElements.length > 0;
  3120. });
  3121. };
  3122. const isAnyElementBySelectorList = (argsData) => {
  3123. const { element, relativeSelectorList, pseudoName } = argsData;
  3124. return relativeSelectorList.children.some((selectorNode) => {
  3125. const relativeRegularSelector = getFirstInnerRegularChild(
  3126. selectorNode,
  3127. pseudoName
  3128. );
  3129. const rootElement = getParent(
  3130. element,
  3131. `Selection by :${pseudoName}() pseudo-class is not possible`
  3132. );
  3133. const specifiedSelector = `${scopeDirectChildren}${getNodeValue(
  3134. relativeRegularSelector
  3135. )}`;
  3136. let anyElements;
  3137. try {
  3138. anyElements = getElementsForSelectorNode(
  3139. selectorNode,
  3140. rootElement,
  3141. specifiedSelector
  3142. );
  3143. } catch (e) {
  3144. return false;
  3145. }
  3146. return anyElements.includes(element);
  3147. });
  3148. };
  3149. const notElementBySelectorList = (argsData) => {
  3150. const { element, relativeSelectorList, pseudoName } = argsData;
  3151. return relativeSelectorList.children.every((selectorNode) => {
  3152. const relativeRegularSelector = getFirstInnerRegularChild(
  3153. selectorNode,
  3154. pseudoName
  3155. );
  3156. const rootElement = getParent(
  3157. element,
  3158. `Selection by :${pseudoName}() pseudo-class is not possible`
  3159. );
  3160. const specifiedSelector = `${scopeDirectChildren}${getNodeValue(
  3161. relativeRegularSelector
  3162. )}`;
  3163. let anyElements;
  3164. try {
  3165. anyElements = getElementsForSelectorNode(
  3166. selectorNode,
  3167. rootElement,
  3168. specifiedSelector
  3169. );
  3170. } catch (e) {
  3171. logger.error(getErrorMessage(e));
  3172. throw new Error(
  3173. `Invalid selector for :${pseudoName}() pseudo-class: '${getNodeValue(
  3174. relativeRegularSelector
  3175. )}'`
  3176. );
  3177. }
  3178. return !anyElements.includes(element);
  3179. });
  3180. };
  3181. const getByRegularSelector = (
  3182. regularSelectorNode,
  3183. root,
  3184. specifiedSelector
  3185. ) => {
  3186. const selectorText = specifiedSelector
  3187. ? specifiedSelector
  3188. : getNodeValue(regularSelectorNode);
  3189. let selectedElements = [];
  3190. try {
  3191. selectedElements = Array.from(root.querySelectorAll(selectorText));
  3192. } catch (e) {
  3193. throw new Error(
  3194. `Error: unable to select by '${selectorText}' ${getErrorMessage(e)}`
  3195. );
  3196. }
  3197. return selectedElements;
  3198. };
  3199. const getByExtendedSelector = (domElements, extendedSelectorNode) => {
  3200. let foundElements = [];
  3201. const extendedPseudoClassNode = getPseudoClassNode(extendedSelectorNode);
  3202. const pseudoName = getNodeName(extendedPseudoClassNode);
  3203. if (isAbsolutePseudoClass(pseudoName)) {
  3204. const absolutePseudoArg = getNodeValue(
  3205. extendedPseudoClassNode,
  3206. `Missing arg for :${pseudoName}() pseudo-class`
  3207. );
  3208. if (pseudoName === NTH_ANCESTOR_PSEUDO_CLASS_MARKER) {
  3209. foundElements = findByAbsolutePseudoPseudo.nthAncestor(
  3210. domElements,
  3211. absolutePseudoArg,
  3212. pseudoName
  3213. );
  3214. } else if (pseudoName === XPATH_PSEUDO_CLASS_MARKER) {
  3215. try {
  3216. document.createExpression(absolutePseudoArg, null);
  3217. } catch (e) {
  3218. throw new Error(
  3219. `Invalid argument of :${pseudoName}() pseudo-class: '${absolutePseudoArg}'`
  3220. );
  3221. }
  3222. foundElements = findByAbsolutePseudoPseudo.xpath(
  3223. domElements,
  3224. absolutePseudoArg
  3225. );
  3226. } else if (pseudoName === UPWARD_PSEUDO_CLASS_MARKER) {
  3227. if (Number.isNaN(Number(absolutePseudoArg))) {
  3228. foundElements = findByAbsolutePseudoPseudo.upward(
  3229. domElements,
  3230. absolutePseudoArg
  3231. );
  3232. } else {
  3233. foundElements = findByAbsolutePseudoPseudo.nthAncestor(
  3234. domElements,
  3235. absolutePseudoArg,
  3236. pseudoName
  3237. );
  3238. }
  3239. } else {
  3240. foundElements = domElements.filter((element) => {
  3241. return isMatchedByAbsolutePseudo(
  3242. element,
  3243. pseudoName,
  3244. absolutePseudoArg
  3245. );
  3246. });
  3247. }
  3248. } else if (isRelativePseudoClass(pseudoName)) {
  3249. const relativeSelectorList = getRelativeSelectorListNode(
  3250. extendedPseudoClassNode
  3251. );
  3252. let relativePredicate;
  3253. switch (pseudoName) {
  3254. case HAS_PSEUDO_CLASS_MARKER:
  3255. case ABP_HAS_PSEUDO_CLASS_MARKER:
  3256. relativePredicate = (element) =>
  3257. hasRelativesBySelectorList({
  3258. element,
  3259. relativeSelectorList,
  3260. pseudoName,
  3261. });
  3262. break;
  3263. case IS_PSEUDO_CLASS_MARKER:
  3264. relativePredicate = (element) =>
  3265. isAnyElementBySelectorList({
  3266. element,
  3267. relativeSelectorList,
  3268. pseudoName,
  3269. });
  3270. break;
  3271. case NOT_PSEUDO_CLASS_MARKER:
  3272. relativePredicate = (element) =>
  3273. notElementBySelectorList({
  3274. element,
  3275. relativeSelectorList,
  3276. pseudoName,
  3277. });
  3278. break;
  3279. default:
  3280. throw new Error(`Unknown relative pseudo-class: '${pseudoName}'`);
  3281. }
  3282. foundElements = domElements.filter(relativePredicate);
  3283. } else {
  3284. throw new Error(`Unknown extended pseudo-class: '${pseudoName}'`);
  3285. }
  3286. return foundElements;
  3287. };
  3288. const getByFollowingRegularSelector = (domElements, regularSelectorNode) => {
  3289. let foundElements = [];
  3290. const value = getNodeValue(regularSelectorNode);
  3291. if (value.startsWith(CHILD_COMBINATOR)) {
  3292. foundElements = domElements.map((root) => {
  3293. const specifiedSelector = `${SCOPE_CSS_PSEUDO_CLASS}${value}`;
  3294. return getByRegularSelector(
  3295. regularSelectorNode,
  3296. root,
  3297. specifiedSelector
  3298. );
  3299. });
  3300. } else if (
  3301. value.startsWith(NEXT_SIBLING_COMBINATOR) ||
  3302. value.startsWith(SUBSEQUENT_SIBLING_COMBINATOR)
  3303. ) {
  3304. foundElements = domElements.map((element) => {
  3305. const rootElement = element.parentElement;
  3306. if (!rootElement) {
  3307. return [];
  3308. }
  3309. const elementSelectorText = getElementSelectorDesc(element);
  3310. const specifiedSelector = `${scopeDirectChildren}${elementSelectorText}${value}`;
  3311. const selected = getByRegularSelector(
  3312. regularSelectorNode,
  3313. rootElement,
  3314. specifiedSelector
  3315. );
  3316. return selected;
  3317. });
  3318. } else {
  3319. foundElements = domElements.map((root) => {
  3320. const specifiedSelector = `${scopeAnyChildren}${getNodeValue(
  3321. regularSelectorNode
  3322. )}`;
  3323. return getByRegularSelector(
  3324. regularSelectorNode,
  3325. root,
  3326. specifiedSelector
  3327. );
  3328. });
  3329. }
  3330. return flatten(foundElements);
  3331. };
  3332. const getElementsForSelectorNode = (
  3333. selectorNode,
  3334. root,
  3335. specifiedSelector
  3336. ) => {
  3337. let selectedElements = [];
  3338. let i = 0;
  3339. while (i < selectorNode.children.length) {
  3340. const selectorNodeChild = getItemByIndex(
  3341. selectorNode.children,
  3342. i,
  3343. "selectorNodeChild should be specified"
  3344. );
  3345. if (i === 0) {
  3346. selectedElements = getByRegularSelector(
  3347. selectorNodeChild,
  3348. root,
  3349. specifiedSelector
  3350. );
  3351. } else if (isExtendedSelectorNode(selectorNodeChild)) {
  3352. selectedElements = getByExtendedSelector(
  3353. selectedElements,
  3354. selectorNodeChild
  3355. );
  3356. } else if (isRegularSelectorNode(selectorNodeChild)) {
  3357. selectedElements = getByFollowingRegularSelector(
  3358. selectedElements,
  3359. selectorNodeChild
  3360. );
  3361. }
  3362. i += 1;
  3363. }
  3364. return selectedElements;
  3365. };
  3366. const selectElementsByAst = function (ast) {
  3367. let doc =
  3368. arguments.length > 1 && arguments[1] !== undefined
  3369. ? arguments[1]
  3370. : document;
  3371. const selectedElements = [];
  3372. ast.children.forEach((selectorNode) => {
  3373. selectedElements.push(...getElementsForSelectorNode(selectorNode, doc));
  3374. });
  3375. const uniqueElements = [...new Set(flatten(selectedElements))];
  3376. return uniqueElements;
  3377. };
  3378. class ExtCssDocument {
  3379. constructor() {
  3380. this.astCache = new Map();
  3381. }
  3382. saveAstToCache(selector, ast) {
  3383. this.astCache.set(selector, ast);
  3384. }
  3385. getAstFromCache(selector) {
  3386. const cachedAst = this.astCache.get(selector) || null;
  3387. return cachedAst;
  3388. }
  3389. getSelectorAst(selector) {
  3390. let ast = this.getAstFromCache(selector);
  3391. if (!ast) {
  3392. ast = parse(selector);
  3393. }
  3394. this.saveAstToCache(selector, ast);
  3395. return ast;
  3396. }
  3397. querySelectorAll(selector) {
  3398. const ast = this.getSelectorAst(selector);
  3399. return selectElementsByAst(ast);
  3400. }
  3401. }
  3402. const extCssDocument = new ExtCssDocument();
  3403. const getObjectFromEntries = (entries) => {
  3404. const object = {};
  3405. entries.forEach((el) => {
  3406. const [key, value] = el;
  3407. object[key] = value;
  3408. });
  3409. return object;
  3410. };
  3411. const DEBUG_PSEUDO_PROPERTY_KEY = "debug";
  3412. const parseRemoveSelector = (rawSelector) => {
  3413. const VALID_REMOVE_MARKER = `${COLON}${REMOVE_PSEUDO_MARKER}${BRACKET.PARENTHESES.LEFT}${BRACKET.PARENTHESES.RIGHT}`;
  3414. const INVALID_REMOVE_MARKER = `${COLON}${REMOVE_PSEUDO_MARKER}${BRACKET.PARENTHESES.LEFT}`;
  3415. let selector;
  3416. let shouldRemove = false;
  3417. const firstIndex = rawSelector.indexOf(VALID_REMOVE_MARKER);
  3418. if (firstIndex === 0) {
  3419. throw new Error(
  3420. `${REMOVE_ERROR_PREFIX.NO_TARGET_SELECTOR}: '${rawSelector}'`
  3421. );
  3422. } else if (firstIndex > 0) {
  3423. if (firstIndex !== rawSelector.lastIndexOf(VALID_REMOVE_MARKER)) {
  3424. throw new Error(
  3425. `${REMOVE_ERROR_PREFIX.MULTIPLE_USAGE}: '${rawSelector}'`
  3426. );
  3427. } else if (firstIndex + VALID_REMOVE_MARKER.length < rawSelector.length) {
  3428. throw new Error(
  3429. `${REMOVE_ERROR_PREFIX.INVALID_POSITION}: '${rawSelector}'`
  3430. );
  3431. } else {
  3432. selector = rawSelector.substring(0, firstIndex);
  3433. shouldRemove = true;
  3434. }
  3435. } else if (rawSelector.includes(INVALID_REMOVE_MARKER)) {
  3436. throw new Error(
  3437. `${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${rawSelector}'`
  3438. );
  3439. } else {
  3440. selector = rawSelector;
  3441. }
  3442. const stylesOfSelector = shouldRemove
  3443. ? [
  3444. {
  3445. property: REMOVE_PSEUDO_MARKER,
  3446. value: PSEUDO_PROPERTY_POSITIVE_VALUE,
  3447. },
  3448. ]
  3449. : [];
  3450. return {
  3451. selector,
  3452. stylesOfSelector,
  3453. };
  3454. };
  3455. const parseSelectorRulePart = (selectorBuffer, extCssDoc) => {
  3456. let selector = selectorBuffer.trim();
  3457. if (selector.startsWith(AT_RULE_MARKER)) {
  3458. throw new Error(`${NO_AT_RULE_ERROR_PREFIX}: '${selector}'.`);
  3459. }
  3460. let removeSelectorData;
  3461. try {
  3462. removeSelectorData = parseRemoveSelector(selector);
  3463. } catch (e) {
  3464. logger.error(getErrorMessage(e));
  3465. throw new Error(`${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`);
  3466. }
  3467. let stylesOfSelector = [];
  3468. let success = false;
  3469. let ast;
  3470. try {
  3471. selector = removeSelectorData.selector;
  3472. stylesOfSelector = removeSelectorData.stylesOfSelector;
  3473. ast = extCssDoc.getSelectorAst(selector);
  3474. success = true;
  3475. } catch (e) {
  3476. success = false;
  3477. }
  3478. return {
  3479. success,
  3480. selector,
  3481. ast,
  3482. stylesOfSelector,
  3483. };
  3484. };
  3485. const createRawResultsMap = () => {
  3486. return new Map();
  3487. };
  3488. const saveToRawResults = (rawResults, rawRuleData) => {
  3489. const { selector, ast, rawStyles } = rawRuleData;
  3490. if (!rawStyles) {
  3491. throw new Error(`No style declaration for selector: '${selector}'`);
  3492. }
  3493. if (!ast) {
  3494. throw new Error(`No ast parsed for selector: '${selector}'`);
  3495. }
  3496. const storedRuleData = rawResults.get(selector);
  3497. if (!storedRuleData) {
  3498. rawResults.set(selector, {
  3499. ast,
  3500. styles: rawStyles,
  3501. });
  3502. } else {
  3503. storedRuleData.styles.push(...rawStyles);
  3504. }
  3505. };
  3506. const isRemoveSetInStyles = (styles) => {
  3507. return styles.some((s) => {
  3508. return (
  3509. s.property === REMOVE_PSEUDO_MARKER &&
  3510. s.value === PSEUDO_PROPERTY_POSITIVE_VALUE
  3511. );
  3512. });
  3513. };
  3514. const getDebugStyleValue = (styles) => {
  3515. const debugStyle = styles.find((s) => {
  3516. return s.property === DEBUG_PSEUDO_PROPERTY_KEY;
  3517. });
  3518. return debugStyle === null || debugStyle === void 0
  3519. ? void 0
  3520. : debugStyle.value;
  3521. };
  3522. const prepareRuleData = (rawRuleData) => {
  3523. const { selector, ast, rawStyles } = rawRuleData;
  3524. if (!ast) {
  3525. throw new Error(`AST should be parsed for selector: '${selector}'`);
  3526. }
  3527. if (!rawStyles) {
  3528. throw new Error(`Styles should be parsed for selector: '${selector}'`);
  3529. }
  3530. const ruleData = {
  3531. selector,
  3532. ast,
  3533. };
  3534. const debugValue = getDebugStyleValue(rawStyles);
  3535. const shouldRemove = isRemoveSetInStyles(rawStyles);
  3536. let styles = rawStyles;
  3537. if (debugValue) {
  3538. styles = rawStyles.filter(
  3539. (s) => s.property !== DEBUG_PSEUDO_PROPERTY_KEY
  3540. );
  3541. if (
  3542. debugValue === PSEUDO_PROPERTY_POSITIVE_VALUE ||
  3543. debugValue === DEBUG_PSEUDO_PROPERTY_GLOBAL_VALUE
  3544. ) {
  3545. ruleData.debug = debugValue;
  3546. }
  3547. }
  3548. if (shouldRemove) {
  3549. ruleData.style = {
  3550. [REMOVE_PSEUDO_MARKER]: PSEUDO_PROPERTY_POSITIVE_VALUE,
  3551. };
  3552. const contentStyle = styles.find(
  3553. (s) => s.property === CONTENT_CSS_PROPERTY
  3554. );
  3555. if (contentStyle) {
  3556. ruleData.style[CONTENT_CSS_PROPERTY] = contentStyle.value;
  3557. }
  3558. } else {
  3559. if (styles.length > 0) {
  3560. const stylesAsEntries = styles.map((style) => {
  3561. const { property, value } = style;
  3562. return [property, value];
  3563. });
  3564. const preparedStyleData = getObjectFromEntries(stylesAsEntries);
  3565. ruleData.style = preparedStyleData;
  3566. }
  3567. }
  3568. return ruleData;
  3569. };
  3570. const combineRulesData = (rawResults) => {
  3571. const results = [];
  3572. rawResults.forEach((value, key) => {
  3573. const selector = key;
  3574. const { ast, styles: rawStyles } = value;
  3575. results.push(
  3576. prepareRuleData({
  3577. selector,
  3578. ast,
  3579. rawStyles,
  3580. })
  3581. );
  3582. });
  3583. return results;
  3584. };
  3585. const tokenizeStyleBlock = (rawStyle) => {
  3586. const styleDeclaration = rawStyle.trim();
  3587. return tokenize(styleDeclaration, SUPPORTED_STYLE_DECLARATION_MARKS);
  3588. };
  3589. const DECLARATION_PART = {
  3590. PROPERTY: "property",
  3591. VALUE: "value",
  3592. };
  3593. const isValueQuotesOpen = (context) => {
  3594. return context.bufferValue !== "" && context.valueQuoteMark !== null;
  3595. };
  3596. const collectStyle = (context) => {
  3597. context.styles.push({
  3598. property: context.bufferProperty.trim(),
  3599. value: context.bufferValue.trim(),
  3600. });
  3601. context.bufferProperty = "";
  3602. context.bufferValue = "";
  3603. };
  3604. const processPropertyToken = (context, styleBlock, token) => {
  3605. const { value: tokenValue } = token;
  3606. switch (token.type) {
  3607. case TOKEN_TYPE.WORD:
  3608. if (context.bufferProperty.length > 0) {
  3609. throw new Error(
  3610. `Invalid style property in style block: '${styleBlock}'`
  3611. );
  3612. }
  3613. context.bufferProperty += tokenValue;
  3614. break;
  3615. case TOKEN_TYPE.MARK:
  3616. if (tokenValue === COLON) {
  3617. if (context.bufferProperty.trim().length === 0) {
  3618. throw new Error(
  3619. `Missing style property before ':' in style block: '${styleBlock}'`
  3620. );
  3621. }
  3622. context.bufferProperty = context.bufferProperty.trim();
  3623. context.processing = DECLARATION_PART.VALUE;
  3624. } else if (WHITE_SPACE_CHARACTERS.includes(tokenValue));
  3625. else {
  3626. throw new Error(
  3627. `Invalid style declaration in style block: '${styleBlock}'`
  3628. );
  3629. }
  3630. break;
  3631. default:
  3632. throw new Error(
  3633. `Unsupported style property character: '${tokenValue}' in style block: '${styleBlock}'`
  3634. );
  3635. }
  3636. };
  3637. const processValueToken = (context, styleBlock, token) => {
  3638. const { value: tokenValue } = token;
  3639. if (token.type === TOKEN_TYPE.WORD) {
  3640. context.bufferValue += tokenValue;
  3641. } else {
  3642. switch (tokenValue) {
  3643. case COLON:
  3644. if (!isValueQuotesOpen(context)) {
  3645. throw new Error(
  3646. `Invalid style value for property '${context.bufferProperty}' in style block: '${styleBlock}'`
  3647. );
  3648. }
  3649. context.bufferValue += tokenValue;
  3650. break;
  3651. case SEMICOLON:
  3652. if (isValueQuotesOpen(context)) {
  3653. context.bufferValue += tokenValue;
  3654. } else {
  3655. collectStyle(context);
  3656. context.processing = DECLARATION_PART.PROPERTY;
  3657. }
  3658. break;
  3659. case SINGLE_QUOTE:
  3660. case DOUBLE_QUOTE:
  3661. if (context.valueQuoteMark === null) {
  3662. context.valueQuoteMark = tokenValue;
  3663. } else if (
  3664. !context.bufferValue.endsWith(BACKSLASH) &&
  3665. context.valueQuoteMark === tokenValue
  3666. ) {
  3667. context.valueQuoteMark = null;
  3668. }
  3669. context.bufferValue += tokenValue;
  3670. break;
  3671. case BACKSLASH:
  3672. if (!isValueQuotesOpen(context)) {
  3673. throw new Error(
  3674. `Invalid style value for property '${context.bufferProperty}' in style block: '${styleBlock}'`
  3675. );
  3676. }
  3677. context.bufferValue += tokenValue;
  3678. break;
  3679. case SPACE:
  3680. case TAB:
  3681. case CARRIAGE_RETURN:
  3682. case LINE_FEED:
  3683. case FORM_FEED:
  3684. if (context.bufferValue.length > 0) {
  3685. context.bufferValue += tokenValue;
  3686. }
  3687. break;
  3688. default:
  3689. throw new Error(`Unknown style declaration token: '${tokenValue}'`);
  3690. }
  3691. }
  3692. };
  3693. const parseStyleBlock = (rawStyleBlock) => {
  3694. const styleBlock = rawStyleBlock.trim();
  3695. const tokens = tokenizeStyleBlock(styleBlock);
  3696. const context = {
  3697. processing: DECLARATION_PART.PROPERTY,
  3698. styles: [],
  3699. bufferProperty: "",
  3700. bufferValue: "",
  3701. valueQuoteMark: null,
  3702. };
  3703. let i = 0;
  3704. while (i < tokens.length) {
  3705. const token = tokens[i];
  3706. if (!token) {
  3707. break;
  3708. }
  3709. if (context.processing === DECLARATION_PART.PROPERTY) {
  3710. processPropertyToken(context, styleBlock, token);
  3711. } else if (context.processing === DECLARATION_PART.VALUE) {
  3712. processValueToken(context, styleBlock, token);
  3713. } else {
  3714. throw new Error("Style declaration parsing failed");
  3715. }
  3716. i += 1;
  3717. }
  3718. if (isValueQuotesOpen(context)) {
  3719. throw new Error(
  3720. `Unbalanced style declaration quotes in style block: '${styleBlock}'`
  3721. );
  3722. }
  3723. if (context.bufferProperty.length > 0) {
  3724. if (context.bufferValue.length === 0) {
  3725. throw new Error(
  3726. `Missing style value for property '${context.bufferProperty}' in style block '${styleBlock}'`
  3727. );
  3728. }
  3729. collectStyle(context);
  3730. }
  3731. if (context.styles.length === 0) {
  3732. throw new Error(STYLE_ERROR_PREFIX.NO_STYLE);
  3733. }
  3734. return context.styles;
  3735. };
  3736. const getLeftCurlyBracketIndexes = (cssRule) => {
  3737. const indexes = [];
  3738. for (let i = 0; i < cssRule.length; i += 1) {
  3739. if (cssRule[i] === BRACKET.CURLY.LEFT) {
  3740. indexes.push(i);
  3741. }
  3742. }
  3743. return indexes;
  3744. };
  3745. const parseRule = (rawCssRule, extCssDoc) => {
  3746. var _rawRuleData$selector;
  3747. const cssRule = rawCssRule.trim();
  3748. if (
  3749. cssRule.includes(`${SLASH}${ASTERISK}`) &&
  3750. cssRule.includes(`${ASTERISK}${SLASH}`)
  3751. ) {
  3752. throw new Error(STYLE_ERROR_PREFIX.NO_COMMENT);
  3753. }
  3754. const leftCurlyBracketIndexes = getLeftCurlyBracketIndexes(cssRule);
  3755. if (getFirst(leftCurlyBracketIndexes) === 0) {
  3756. throw new Error(NO_SELECTOR_ERROR_PREFIX);
  3757. }
  3758. let selectorData;
  3759. if (
  3760. leftCurlyBracketIndexes.length > 0 &&
  3761. !cssRule.includes(BRACKET.CURLY.RIGHT)
  3762. ) {
  3763. throw new Error(
  3764. `${STYLE_ERROR_PREFIX.NO_STYLE} OR ${STYLE_ERROR_PREFIX.UNCLOSED_STYLE}`
  3765. );
  3766. }
  3767. if (
  3768. leftCurlyBracketIndexes.length === 0 ||
  3769. !cssRule.includes(BRACKET.CURLY.RIGHT)
  3770. ) {
  3771. try {
  3772. selectorData = parseSelectorRulePart(cssRule, extCssDoc);
  3773. if (selectorData.success) {
  3774. var _selectorData$stylesO;
  3775. if (
  3776. ((_selectorData$stylesO = selectorData.stylesOfSelector) === null ||
  3777. _selectorData$stylesO === void 0
  3778. ? void 0
  3779. : _selectorData$stylesO.length) === 0
  3780. ) {
  3781. throw new Error(STYLE_ERROR_PREFIX.NO_STYLE_OR_REMOVE);
  3782. }
  3783. return {
  3784. selector: selectorData.selector.trim(),
  3785. ast: selectorData.ast,
  3786. rawStyles: selectorData.stylesOfSelector,
  3787. };
  3788. } else {
  3789. throw new Error("Invalid selector");
  3790. }
  3791. } catch (e) {
  3792. throw new Error(getErrorMessage(e));
  3793. }
  3794. }
  3795. let selectorBuffer;
  3796. let styleBlockBuffer;
  3797. const rawRuleData = {
  3798. selector: "",
  3799. };
  3800. for (let i = leftCurlyBracketIndexes.length - 1; i > -1; i -= 1) {
  3801. const index = leftCurlyBracketIndexes[i];
  3802. if (!index) {
  3803. throw new Error(
  3804. `Impossible to continue, no '{' to process for rule: '${cssRule}'`
  3805. );
  3806. }
  3807. selectorBuffer = cssRule.slice(0, index);
  3808. styleBlockBuffer = cssRule.slice(index + 1, cssRule.length - 1);
  3809. selectorData = parseSelectorRulePart(selectorBuffer, extCssDoc);
  3810. if (selectorData.success) {
  3811. var _rawRuleData$rawStyle;
  3812. rawRuleData.selector = selectorData.selector.trim();
  3813. rawRuleData.ast = selectorData.ast;
  3814. rawRuleData.rawStyles = selectorData.stylesOfSelector;
  3815. const parsedStyles = parseStyleBlock(styleBlockBuffer);
  3816. (_rawRuleData$rawStyle = rawRuleData.rawStyles) === null ||
  3817. _rawRuleData$rawStyle === void 0
  3818. ? void 0
  3819. : _rawRuleData$rawStyle.push(...parsedStyles);
  3820. break;
  3821. } else {
  3822. continue;
  3823. }
  3824. }
  3825. if (
  3826. ((_rawRuleData$selector = rawRuleData.selector) === null ||
  3827. _rawRuleData$selector === void 0
  3828. ? void 0
  3829. : _rawRuleData$selector.length) === 0
  3830. ) {
  3831. throw new Error("Selector in not valid");
  3832. }
  3833. return rawRuleData;
  3834. };
  3835. const parseRules$1 = (rawCssRules, extCssDoc) => {
  3836. const rawResults = createRawResultsMap();
  3837. const warnings = [];
  3838. const uniqueRules = [...new Set(rawCssRules.map((r) => r.trim()))];
  3839. uniqueRules.forEach((rule) => {
  3840. try {
  3841. saveToRawResults(rawResults, parseRule(rule, extCssDoc));
  3842. } catch (e) {
  3843. const errorMessage = getErrorMessage(e);
  3844. warnings.push(`'${rule}' - error: '${errorMessage}'`);
  3845. }
  3846. });
  3847. if (warnings.length > 0) {
  3848. logger.info(`Invalid rules:\n ${warnings.join("\n ")}`);
  3849. }
  3850. return combineRulesData(rawResults);
  3851. };
  3852. const REGEXP_DECLARATION_END = /[;}]/g;
  3853. const REGEXP_DECLARATION_DIVIDER = /[;:}]/g;
  3854. const REGEXP_NON_WHITESPACE = /\S/g;
  3855. const restoreRuleAcc = (context) => {
  3856. context.rawRuleData = {
  3857. selector: "",
  3858. };
  3859. };
  3860. const parseSelectorPart = (context, extCssDoc) => {
  3861. let selector = context.selectorBuffer.trim();
  3862. if (selector.startsWith(AT_RULE_MARKER)) {
  3863. throw new Error(`${NO_AT_RULE_ERROR_PREFIX}: '${selector}'.`);
  3864. }
  3865. let removeSelectorData;
  3866. try {
  3867. removeSelectorData = parseRemoveSelector(selector);
  3868. } catch (e) {
  3869. logger.error(getErrorMessage(e));
  3870. throw new Error(`${REMOVE_ERROR_PREFIX.INVALID_REMOVE}: '${selector}'`);
  3871. }
  3872. if (context.nextIndex === -1) {
  3873. if (selector === removeSelectorData.selector) {
  3874. throw new Error(
  3875. `${STYLE_ERROR_PREFIX.NO_STYLE_OR_REMOVE}: '${context.cssToParse}'`
  3876. );
  3877. }
  3878. context.cssToParse = "";
  3879. }
  3880. let stylesOfSelector = [];
  3881. let success = false;
  3882. let ast;
  3883. try {
  3884. selector = removeSelectorData.selector;
  3885. stylesOfSelector = removeSelectorData.stylesOfSelector;
  3886. ast = extCssDoc.getSelectorAst(selector);
  3887. success = true;
  3888. } catch (e) {
  3889. success = false;
  3890. }
  3891. if (context.nextIndex > 0) {
  3892. context.cssToParse = context.cssToParse.slice(context.nextIndex);
  3893. }
  3894. return {
  3895. success,
  3896. selector,
  3897. ast,
  3898. stylesOfSelector,
  3899. };
  3900. };
  3901. const parseUntilClosingBracket = (context, styles) => {
  3902. REGEXP_DECLARATION_DIVIDER.lastIndex = context.nextIndex;
  3903. let match = REGEXP_DECLARATION_DIVIDER.exec(context.cssToParse);
  3904. if (match === null) {
  3905. throw new Error(
  3906. `${STYLE_ERROR_PREFIX.INVALID_STYLE}: '${context.cssToParse}'`
  3907. );
  3908. }
  3909. let matchPos = match.index;
  3910. let matched = match[0];
  3911. if (matched === BRACKET.CURLY.RIGHT) {
  3912. const declarationChunk = context.cssToParse.slice(
  3913. context.nextIndex,
  3914. matchPos
  3915. );
  3916. if (declarationChunk.trim().length === 0) {
  3917. if (styles.length === 0) {
  3918. throw new Error(
  3919. `${STYLE_ERROR_PREFIX.NO_STYLE}: '${context.cssToParse}'`
  3920. );
  3921. }
  3922. } else {
  3923. throw new Error(
  3924. `${STYLE_ERROR_PREFIX.INVALID_STYLE}: '${context.cssToParse}'`
  3925. );
  3926. }
  3927. return matchPos;
  3928. }
  3929. if (matched === COLON) {
  3930. const colonIndex = matchPos;
  3931. REGEXP_DECLARATION_END.lastIndex = colonIndex;
  3932. match = REGEXP_DECLARATION_END.exec(context.cssToParse);
  3933. if (match === null) {
  3934. throw new Error(
  3935. `${STYLE_ERROR_PREFIX.UNCLOSED_STYLE}: '${context.cssToParse}'`
  3936. );
  3937. }
  3938. matchPos = match.index;
  3939. matched = match[0];
  3940. const property = context.cssToParse
  3941. .slice(context.nextIndex, colonIndex)
  3942. .trim();
  3943. if (property.length === 0) {
  3944. throw new Error(
  3945. `${STYLE_ERROR_PREFIX.NO_PROPERTY}: '${context.cssToParse}'`
  3946. );
  3947. }
  3948. const value = context.cssToParse.slice(colonIndex + 1, matchPos).trim();
  3949. if (value.length === 0) {
  3950. throw new Error(
  3951. `${STYLE_ERROR_PREFIX.NO_VALUE}: '${context.cssToParse}'`
  3952. );
  3953. }
  3954. styles.push({
  3955. property,
  3956. value,
  3957. });
  3958. if (matched === BRACKET.CURLY.RIGHT) {
  3959. return matchPos;
  3960. }
  3961. }
  3962. context.cssToParse = context.cssToParse.slice(matchPos + 1);
  3963. context.nextIndex = 0;
  3964. return parseUntilClosingBracket(context, styles);
  3965. };
  3966. const parseNextStyle = (context) => {
  3967. const styles = [];
  3968. const styleEndPos = parseUntilClosingBracket(context, styles);
  3969. REGEXP_NON_WHITESPACE.lastIndex = styleEndPos + 1;
  3970. const match = REGEXP_NON_WHITESPACE.exec(context.cssToParse);
  3971. if (match === null) {
  3972. context.cssToParse = "";
  3973. return styles;
  3974. }
  3975. const matchPos = match.index;
  3976. context.cssToParse = context.cssToParse.slice(matchPos);
  3977. return styles;
  3978. };
  3979. const parseStylesheet = (rawStylesheet, extCssDoc) => {
  3980. const stylesheet = rawStylesheet.trim();
  3981. if (
  3982. stylesheet.includes(`${SLASH}${ASTERISK}`) &&
  3983. stylesheet.includes(`${ASTERISK}${SLASH}`)
  3984. ) {
  3985. throw new Error(
  3986. `${STYLE_ERROR_PREFIX.NO_COMMENT} in stylesheet: '${stylesheet}'`
  3987. );
  3988. }
  3989. const context = {
  3990. isSelector: true,
  3991. nextIndex: 0,
  3992. cssToParse: stylesheet,
  3993. selectorBuffer: "",
  3994. rawRuleData: {
  3995. selector: "",
  3996. },
  3997. };
  3998. const rawResults = createRawResultsMap();
  3999. let selectorData;
  4000. while (context.cssToParse) {
  4001. if (context.isSelector) {
  4002. context.nextIndex = context.cssToParse.indexOf(BRACKET.CURLY.LEFT);
  4003. if (context.selectorBuffer.length === 0 && context.nextIndex === 0) {
  4004. throw new Error(
  4005. `${STYLE_ERROR_PREFIX.NO_SELECTOR}: '${context.cssToParse}'`
  4006. );
  4007. }
  4008. if (context.nextIndex === -1) {
  4009. context.selectorBuffer = context.cssToParse;
  4010. } else {
  4011. context.selectorBuffer += context.cssToParse.slice(
  4012. 0,
  4013. context.nextIndex
  4014. );
  4015. }
  4016. selectorData = parseSelectorPart(context, extCssDoc);
  4017. if (selectorData.success) {
  4018. context.rawRuleData.selector = selectorData.selector.trim();
  4019. context.rawRuleData.ast = selectorData.ast;
  4020. context.rawRuleData.rawStyles = selectorData.stylesOfSelector;
  4021. context.isSelector = false;
  4022. if (context.nextIndex === -1) {
  4023. saveToRawResults(rawResults, context.rawRuleData);
  4024. restoreRuleAcc(context);
  4025. } else {
  4026. context.nextIndex = 1;
  4027. context.selectorBuffer = "";
  4028. }
  4029. } else {
  4030. context.selectorBuffer += BRACKET.CURLY.LEFT;
  4031. context.cssToParse = context.cssToParse.slice(1);
  4032. }
  4033. } else {
  4034. var _context$rawRuleData$;
  4035. const parsedStyles = parseNextStyle(context);
  4036. (_context$rawRuleData$ = context.rawRuleData.rawStyles) === null ||
  4037. _context$rawRuleData$ === void 0
  4038. ? void 0
  4039. : _context$rawRuleData$.push(...parsedStyles);
  4040. saveToRawResults(rawResults, context.rawRuleData);
  4041. context.nextIndex = 0;
  4042. restoreRuleAcc(context);
  4043. context.isSelector = true;
  4044. }
  4045. }
  4046. return combineRulesData(rawResults);
  4047. };
  4048. const isNumber = (arg) => {
  4049. return typeof arg === "number" && !Number.isNaN(arg);
  4050. };
  4051. class ThrottleWrapper {
  4052. constructor(callback) {
  4053. this.callback = callback;
  4054. this.executeCallback = this.executeCallback.bind(this);
  4055. }
  4056. executeCallback() {
  4057. this.lastRunTime = performance.now();
  4058. if (isNumber(this.timerId)) {
  4059. clearTimeout(this.timerId);
  4060. delete this.timerId;
  4061. }
  4062. this.callback();
  4063. }
  4064. run() {
  4065. if (isNumber(this.timerId)) {
  4066. return;
  4067. }
  4068. if (isNumber(this.lastRunTime)) {
  4069. const elapsedTime = performance.now() - this.lastRunTime;
  4070. if (elapsedTime < ThrottleWrapper.THROTTLE_DELAY_MS) {
  4071. this.timerId = window.setTimeout(
  4072. this.executeCallback,
  4073. ThrottleWrapper.THROTTLE_DELAY_MS - elapsedTime
  4074. );
  4075. return;
  4076. }
  4077. }
  4078. this.timerId = window.setTimeout(this.executeCallback);
  4079. }
  4080. }
  4081. _defineProperty(ThrottleWrapper, "THROTTLE_DELAY_MS", 150);
  4082. const LAST_EVENT_TIMEOUT_MS = 10;
  4083. const IGNORED_EVENTS = ["mouseover", "mouseleave", "mouseenter", "mouseout"];
  4084. const SUPPORTED_EVENTS = [
  4085. "keydown",
  4086. "keypress",
  4087. "keyup",
  4088. "auxclick",
  4089. "click",
  4090. "contextmenu",
  4091. "dblclick",
  4092. "mousedown",
  4093. "mouseenter",
  4094. "mouseleave",
  4095. "mousemove",
  4096. "mouseover",
  4097. "mouseout",
  4098. "mouseup",
  4099. "pointerlockchange",
  4100. "pointerlockerror",
  4101. "select",
  4102. "wheel",
  4103. ];
  4104. const SAFARI_PROBLEMATIC_EVENTS = ["wheel"];
  4105. class EventTracker {
  4106. constructor() {
  4107. _defineProperty(this, "getLastEventType", () => this.lastEventType);
  4108. _defineProperty(this, "getTimeSinceLastEvent", () => {
  4109. if (!this.lastEventTime) {
  4110. return null;
  4111. }
  4112. return Date.now() - this.lastEventTime;
  4113. });
  4114. this.trackedEvents = isSafariBrowser
  4115. ? SUPPORTED_EVENTS.filter(
  4116. (event) => !SAFARI_PROBLEMATIC_EVENTS.includes(event)
  4117. )
  4118. : SUPPORTED_EVENTS;
  4119. this.trackedEvents.forEach((eventName) => {
  4120. document.documentElement.addEventListener(
  4121. eventName,
  4122. this.trackEvent,
  4123. true
  4124. );
  4125. });
  4126. }
  4127. trackEvent(event) {
  4128. this.lastEventType = event.type;
  4129. this.lastEventTime = Date.now();
  4130. }
  4131. isIgnoredEventType() {
  4132. const lastEventType = this.getLastEventType();
  4133. const sinceLastEventTime = this.getTimeSinceLastEvent();
  4134. return (
  4135. !!lastEventType &&
  4136. IGNORED_EVENTS.includes(lastEventType) &&
  4137. !!sinceLastEventTime &&
  4138. sinceLastEventTime < LAST_EVENT_TIMEOUT_MS
  4139. );
  4140. }
  4141. stopTracking() {
  4142. this.trackedEvents.forEach((eventName) => {
  4143. document.documentElement.removeEventListener(
  4144. eventName,
  4145. this.trackEvent,
  4146. true
  4147. );
  4148. });
  4149. }
  4150. }
  4151. function shouldIgnoreMutations(mutations) {
  4152. return !mutations.some((m) => m.type !== "attributes");
  4153. }
  4154. function observeDocument(context) {
  4155. if (context.isDomObserved) {
  4156. return;
  4157. }
  4158. context.isDomObserved = true;
  4159. context.domMutationObserver = new natives.MutationObserver((mutations) => {
  4160. if (!mutations || mutations.length === 0) {
  4161. return;
  4162. }
  4163. const eventTracker = new EventTracker();
  4164. if (
  4165. eventTracker.isIgnoredEventType() &&
  4166. shouldIgnoreMutations(mutations)
  4167. ) {
  4168. return;
  4169. }
  4170. context.eventTracker = eventTracker;
  4171. context.scheduler.run();
  4172. });
  4173. context.domMutationObserver.observe(document, {
  4174. childList: true,
  4175. subtree: true,
  4176. attributes: true,
  4177. attributeFilter: ["id", "class"],
  4178. });
  4179. }
  4180. function disconnectDocument(context) {
  4181. if (!context.isDomObserved) {
  4182. return;
  4183. }
  4184. context.isDomObserved = false;
  4185. if (context.domMutationObserver) {
  4186. context.domMutationObserver.disconnect();
  4187. }
  4188. if (context.eventTracker) {
  4189. context.eventTracker.stopTracking();
  4190. }
  4191. }
  4192. const CONTENT_ATTR_PREFIX_REGEXP = /^("|')adguard.+?/;
  4193. const removeElement = (context, affectedElement) => {
  4194. const { node } = affectedElement;
  4195. affectedElement.removed = true;
  4196. const elementSelector = getElementSelectorPath(node);
  4197. const elementRemovalsCounter =
  4198. context.removalsStatistic[elementSelector] || 0;
  4199. if (elementRemovalsCounter > MAX_STYLE_PROTECTION_COUNT) {
  4200. logger.error(
  4201. `ExtendedCss: infinite loop protection for selector: '${elementSelector}'`
  4202. );
  4203. return;
  4204. }
  4205. if (node.parentElement) {
  4206. node.parentElement.removeChild(node);
  4207. context.removalsStatistic[elementSelector] = elementRemovalsCounter + 1;
  4208. }
  4209. };
  4210. const setStyleToElement = (node, style) => {
  4211. if (!(node instanceof HTMLElement)) {
  4212. return;
  4213. }
  4214. Object.keys(style).forEach((prop) => {
  4215. if (typeof node.style.getPropertyValue(prop.toString()) !== "undefined") {
  4216. let value = style[prop];
  4217. if (!value) {
  4218. return;
  4219. }
  4220. if (
  4221. prop === CONTENT_CSS_PROPERTY &&
  4222. value.match(CONTENT_ATTR_PREFIX_REGEXP)
  4223. ) {
  4224. return;
  4225. }
  4226. value = removeSuffix(value.trim(), "!important").trim();
  4227. node.style.setProperty(prop, value, "important");
  4228. }
  4229. });
  4230. };
  4231. const isIAffectedElement = (affectedElement) => {
  4232. return (
  4233. "node" in affectedElement &&
  4234. "rules" in affectedElement &&
  4235. affectedElement.rules instanceof Array
  4236. );
  4237. };
  4238. const isAffectedElement = (affectedElement) => {
  4239. return (
  4240. "node" in affectedElement &&
  4241. "originalStyle" in affectedElement &&
  4242. "rules" in affectedElement &&
  4243. affectedElement.rules instanceof Array
  4244. );
  4245. };
  4246. const applyStyle = (context, rawAffectedElement) => {
  4247. if (rawAffectedElement.protectionObserver) {
  4248. return;
  4249. }
  4250. let affectedElement;
  4251. if (context.beforeStyleApplied) {
  4252. if (!isIAffectedElement(rawAffectedElement)) {
  4253. throw new Error(
  4254. "Returned IAffectedElement should have 'node' and 'rules' properties"
  4255. );
  4256. }
  4257. affectedElement = context.beforeStyleApplied(rawAffectedElement);
  4258. if (!affectedElement) {
  4259. throw new Error(
  4260. "Callback 'beforeStyleApplied' should return IAffectedElement"
  4261. );
  4262. }
  4263. } else {
  4264. affectedElement = rawAffectedElement;
  4265. }
  4266. if (!isAffectedElement(affectedElement)) {
  4267. throw new Error(
  4268. "Returned IAffectedElement should have 'node' and 'rules' properties"
  4269. );
  4270. }
  4271. const { node, rules } = affectedElement;
  4272. for (let i = 0; i < rules.length; i += 1) {
  4273. const rule = rules[i];
  4274. const selector =
  4275. rule === null || rule === void 0 ? void 0 : rule.selector;
  4276. const style = rule === null || rule === void 0 ? void 0 : rule.style;
  4277. const debug = rule === null || rule === void 0 ? void 0 : rule.debug;
  4278. if (style) {
  4279. if (style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE) {
  4280. removeElement(context, affectedElement);
  4281. return;
  4282. }
  4283. setStyleToElement(node, style);
  4284. } else if (!debug) {
  4285. throw new Error(
  4286. `No style declaration in rule for selector: '${selector}'`
  4287. );
  4288. }
  4289. }
  4290. };
  4291. const revertStyle = (affectedElement) => {
  4292. if (affectedElement.protectionObserver) {
  4293. affectedElement.protectionObserver.disconnect();
  4294. }
  4295. affectedElement.node.style.cssText = affectedElement.originalStyle;
  4296. };
  4297. class ExtMutationObserver {
  4298. constructor(protectionCallback) {
  4299. this.styleProtectionCount = 0;
  4300. this.observer = new natives.MutationObserver((mutations) => {
  4301. if (!mutations.length) {
  4302. return;
  4303. }
  4304. this.styleProtectionCount += 1;
  4305. protectionCallback(mutations, this);
  4306. });
  4307. }
  4308. observe(target, options) {
  4309. if (this.styleProtectionCount < MAX_STYLE_PROTECTION_COUNT) {
  4310. this.observer.observe(target, options);
  4311. } else {
  4312. logger.error("ExtendedCss: infinite loop protection for style");
  4313. }
  4314. }
  4315. disconnect() {
  4316. this.observer.disconnect();
  4317. }
  4318. }
  4319. const PROTECTION_OBSERVER_OPTIONS = {
  4320. attributes: true,
  4321. attributeOldValue: true,
  4322. attributeFilter: ["style"],
  4323. };
  4324. const createProtectionCallback = (styles) => {
  4325. const protectionCallback = (mutations, extObserver) => {
  4326. if (!mutations[0]) {
  4327. return;
  4328. }
  4329. const { target } = mutations[0];
  4330. extObserver.disconnect();
  4331. styles.forEach((style) => {
  4332. setStyleToElement(target, style);
  4333. });
  4334. extObserver.observe(target, PROTECTION_OBSERVER_OPTIONS);
  4335. };
  4336. return protectionCallback;
  4337. };
  4338. const protectStyleAttribute = (node, rules) => {
  4339. if (!natives.MutationObserver) {
  4340. return null;
  4341. }
  4342. const styles = [];
  4343. rules.forEach((ruleData) => {
  4344. const { style } = ruleData;
  4345. if (style) {
  4346. styles.push(style);
  4347. }
  4348. });
  4349. const protectionObserver = new ExtMutationObserver(
  4350. createProtectionCallback(styles)
  4351. );
  4352. protectionObserver.observe(node, PROTECTION_OBSERVER_OPTIONS);
  4353. return protectionObserver;
  4354. };
  4355. const STATS_DECIMAL_DIGITS_COUNT = 4;
  4356. class TimingStats {
  4357. constructor() {
  4358. this.appliesTimings = [];
  4359. this.appliesCount = 0;
  4360. this.timingsSum = 0;
  4361. this.meanTiming = 0;
  4362. this.squaredSum = 0;
  4363. this.standardDeviation = 0;
  4364. }
  4365. push(elapsedTimeMs) {
  4366. this.appliesTimings.push(elapsedTimeMs);
  4367. this.appliesCount += 1;
  4368. this.timingsSum += elapsedTimeMs;
  4369. this.meanTiming = this.timingsSum / this.appliesCount;
  4370. this.squaredSum += elapsedTimeMs * elapsedTimeMs;
  4371. this.standardDeviation = Math.sqrt(
  4372. this.squaredSum / this.appliesCount - Math.pow(this.meanTiming, 2)
  4373. );
  4374. }
  4375. }
  4376. const beautifyTimingNumber = (timestamp) => {
  4377. return Number(timestamp.toFixed(STATS_DECIMAL_DIGITS_COUNT));
  4378. };
  4379. const beautifyTimings = (rawTimings) => {
  4380. return {
  4381. appliesTimings: rawTimings.appliesTimings.map((t) =>
  4382. beautifyTimingNumber(t)
  4383. ),
  4384. appliesCount: beautifyTimingNumber(rawTimings.appliesCount),
  4385. timingsSum: beautifyTimingNumber(rawTimings.timingsSum),
  4386. meanTiming: beautifyTimingNumber(rawTimings.meanTiming),
  4387. standardDeviation: beautifyTimingNumber(rawTimings.standardDeviation),
  4388. };
  4389. };
  4390. const printTimingInfo = (context) => {
  4391. if (context.areTimingsPrinted) {
  4392. return;
  4393. }
  4394. context.areTimingsPrinted = true;
  4395. const timingsLogData = {};
  4396. context.parsedRules.forEach((ruleData) => {
  4397. if (ruleData.timingStats) {
  4398. const { selector, style, debug, matchedElements } = ruleData;
  4399. if (!style && !debug) {
  4400. throw new Error(
  4401. `Rule should have style declaration for selector: '${selector}'`
  4402. );
  4403. }
  4404. const selectorData = {
  4405. selectorParsed: selector,
  4406. timings: beautifyTimings(ruleData.timingStats),
  4407. };
  4408. if (
  4409. style &&
  4410. style[REMOVE_PSEUDO_MARKER] === PSEUDO_PROPERTY_POSITIVE_VALUE
  4411. ) {
  4412. selectorData.removed = true;
  4413. } else {
  4414. selectorData.styleApplied = style || null;
  4415. selectorData.matchedElements = matchedElements;
  4416. }
  4417. timingsLogData[selector] = selectorData;
  4418. }
  4419. });
  4420. if (Object.keys(timingsLogData).length === 0) {
  4421. return;
  4422. }
  4423. logger.info(
  4424. "[ExtendedCss] Timings in milliseconds for %o:\n%o",
  4425. window.location.href,
  4426. timingsLogData
  4427. );
  4428. };
  4429. const findAffectedElement = (affElements, domNode) => {
  4430. return affElements.find((affEl) => affEl.node === domNode);
  4431. };
  4432. const applyRule = (context, ruleData) => {
  4433. const isDebuggingMode = !!ruleData.debug || context.debug;
  4434. let startTime;
  4435. if (isDebuggingMode) {
  4436. startTime = performance.now();
  4437. }
  4438. const { ast } = ruleData;
  4439. const nodes = [];
  4440. try {
  4441. nodes.push(...selectElementsByAst(ast));
  4442. } catch (e) {
  4443. if (context.debug) {
  4444. logger.error(getErrorMessage(e));
  4445. }
  4446. }
  4447. nodes.forEach((node) => {
  4448. let affectedElement = findAffectedElement(context.affectedElements, node);
  4449. if (affectedElement) {
  4450. affectedElement.rules.push(ruleData);
  4451. applyStyle(context, affectedElement);
  4452. } else {
  4453. const originalStyle = node.style.cssText;
  4454. affectedElement = {
  4455. node,
  4456. rules: [ruleData],
  4457. originalStyle,
  4458. protectionObserver: null,
  4459. };
  4460. applyStyle(context, affectedElement);
  4461. context.affectedElements.push(affectedElement);
  4462. }
  4463. });
  4464. if (isDebuggingMode && startTime) {
  4465. const elapsedTimeMs = performance.now() - startTime;
  4466. if (!ruleData.timingStats) {
  4467. ruleData.timingStats = new TimingStats();
  4468. }
  4469. ruleData.timingStats.push(elapsedTimeMs);
  4470. }
  4471. return nodes;
  4472. };
  4473. const applyRules = (context) => {
  4474. const newSelectedElements = [];
  4475. disconnectDocument(context);
  4476. context.parsedRules.forEach((ruleData) => {
  4477. const nodes = applyRule(context, ruleData);
  4478. Array.prototype.push.apply(newSelectedElements, nodes);
  4479. if (ruleData.debug) {
  4480. ruleData.matchedElements = nodes;
  4481. }
  4482. });
  4483. let affLength = context.affectedElements.length;
  4484. while (affLength) {
  4485. const affectedElement = context.affectedElements[affLength - 1];
  4486. if (!affectedElement) {
  4487. break;
  4488. }
  4489. if (!newSelectedElements.includes(affectedElement.node)) {
  4490. revertStyle(affectedElement);
  4491. context.affectedElements.splice(affLength - 1, 1);
  4492. } else if (!affectedElement.removed) {
  4493. if (!affectedElement.protectionObserver) {
  4494. affectedElement.protectionObserver = protectStyleAttribute(
  4495. affectedElement.node,
  4496. affectedElement.rules
  4497. );
  4498. }
  4499. }
  4500. affLength -= 1;
  4501. }
  4502. observeDocument(context);
  4503. printTimingInfo(context);
  4504. };
  4505. class ExtendedCss {
  4506. constructor(configuration) {
  4507. if (!configuration) {
  4508. throw new Error("ExtendedCss configuration should be provided.");
  4509. }
  4510. this.applyRulesCallbackListener =
  4511. this.applyRulesCallbackListener.bind(this);
  4512. this.context = {
  4513. beforeStyleApplied: configuration.beforeStyleApplied,
  4514. debug: false,
  4515. affectedElements: [],
  4516. isDomObserved: false,
  4517. removalsStatistic: {},
  4518. parsedRules: [],
  4519. scheduler: new ThrottleWrapper(this.applyRulesCallbackListener),
  4520. };
  4521. if (!isBrowserSupported()) {
  4522. logger.error("Browser is not supported by ExtendedCss");
  4523. return;
  4524. }
  4525. if (!configuration.styleSheet && !configuration.cssRules) {
  4526. throw new Error(
  4527. "ExtendedCss configuration should have 'styleSheet' or 'cssRules' defined."
  4528. );
  4529. }
  4530. if (configuration.styleSheet) {
  4531. try {
  4532. this.context.parsedRules.push(
  4533. ...parseStylesheet(configuration.styleSheet, extCssDocument)
  4534. );
  4535. } catch (e) {
  4536. throw new Error(
  4537. `Pass the rules as configuration.cssRules since configuration.styleSheet cannot be parsed because of: '${getErrorMessage(
  4538. e
  4539. )}'`
  4540. );
  4541. }
  4542. }
  4543. if (configuration.cssRules) {
  4544. this.context.parsedRules.push(
  4545. ...parseRules$1(configuration.cssRules, extCssDocument)
  4546. );
  4547. }
  4548. this.context.debug =
  4549. configuration.debug ||
  4550. this.context.parsedRules.some((ruleData) => {
  4551. return ruleData.debug === DEBUG_PSEUDO_PROPERTY_GLOBAL_VALUE;
  4552. });
  4553. if (
  4554. this.context.beforeStyleApplied &&
  4555. typeof this.context.beforeStyleApplied !== "function"
  4556. ) {
  4557. throw new Error(
  4558. `Invalid configuration. Type of 'beforeStyleApplied' should be a function, received: '${typeof this
  4559. .context.beforeStyleApplied}'`
  4560. );
  4561. }
  4562. }
  4563. applyRulesCallbackListener() {
  4564. applyRules(this.context);
  4565. }
  4566. init() {
  4567. nativeTextContent.setGetter();
  4568. }
  4569. apply() {
  4570. applyRules(this.context);
  4571. if (document.readyState !== "complete") {
  4572. document.addEventListener(
  4573. "DOMContentLoaded",
  4574. this.applyRulesCallbackListener,
  4575. false
  4576. );
  4577. }
  4578. }
  4579. dispose() {
  4580. disconnectDocument(this.context);
  4581. this.context.affectedElements.forEach((el) => {
  4582. revertStyle(el);
  4583. });
  4584. document.removeEventListener(
  4585. "DOMContentLoaded",
  4586. this.applyRulesCallbackListener,
  4587. false
  4588. );
  4589. }
  4590. getAffectedElements() {
  4591. return this.context.affectedElements;
  4592. }
  4593. static query(selector) {
  4594. let noTiming =
  4595. arguments.length > 1 && arguments[1] !== undefined
  4596. ? arguments[1]
  4597. : true;
  4598. if (typeof selector !== "string") {
  4599. throw new Error("Selector should be defined as a string.");
  4600. }
  4601. const start = performance.now();
  4602. try {
  4603. return extCssDocument.querySelectorAll(selector);
  4604. } finally {
  4605. const end = performance.now();
  4606. if (!noTiming) {
  4607. logger.info(
  4608. `[ExtendedCss] Elapsed: ${Math.round((end - start) * 1000)} μs.`
  4609. );
  4610. }
  4611. }
  4612. }
  4613. static validate(inputSelector) {
  4614. try {
  4615. const { selector } = parseRemoveSelector(inputSelector);
  4616. ExtendedCss.query(selector);
  4617. return {
  4618. ok: true,
  4619. error: null,
  4620. };
  4621. } catch (e) {
  4622. const error = `Error: Invalid selector: '${inputSelector}' -- ${getErrorMessage(
  4623. e
  4624. )}`;
  4625. return {
  4626. ok: false,
  4627. error,
  4628. };
  4629. }
  4630. }
  4631. }
  4632.  
  4633. function parseBRules() {
  4634. data.appliedLevel = 0;
  4635. values.brules.forEach((br) => {
  4636. const level = bRuleParser(br);
  4637. if (level > 0) data.bRules.push(br);
  4638. if (level > data.appliedLevel) data.appliedLevel = level;
  4639. });
  4640. }
  4641. function canApplyCss(type) {
  4642. return (
  4643. (data.appliedLevel & (type >= 2 ? 2 : 1)) == 0 &&
  4644. data[styleBoxes[type]].length > 0
  4645. );
  4646. }
  4647.  
  4648. function cleanRules() {
  4649. if (
  4650. confirm(`是否清空存储规则 ?
  4651.  
  4652. 如果要卸载脚本,点击 确定 以后不要刷新,也不要打开任何新页面,
  4653. (如果可以)清空脚本存储(全选,删除,填 [],保存),然后删除脚本`)
  4654. ) {
  4655. values.rules = {};
  4656. values.time = "0/0/0 0:0:0";
  4657. values.etags = {};
  4658. values.brules = [];
  4659. values.hasSave.forEach((host) => {
  4660. gmValue("set", true, `ajs_saved_styles_${host}`);
  4661. });
  4662. values.hasSave = [];
  4663. data.isClean = true;
  4664. gmMenu("update");
  4665. gmMenu("export");
  4666. gmMenu("count", () => location.reload());
  4667. }
  4668. }
  4669. function reportRecord() {
  4670. let text = "";
  4671. function pushRecord(css) {
  4672. const match = cssToAbp(css);
  4673. if (match === null) return;
  4674. const [item, type, sel] = match,
  4675. count =
  4676. type % 2 === 1
  4677. ? ExtendedCss.query(sel).length
  4678. : document.querySelectorAll(sel).length;
  4679. if (count > 0) {
  4680. text += `
  4681. ! 匹配元素数量: ${count}
  4682. ${item}
  4683. `;
  4684. }
  4685. }
  4686. data.bRules.forEach((br) => {
  4687. if (br.level > 0) {
  4688. text += `
  4689. ! 禁用${["", "通用", "特定", "所有"][br.level]}元素隐藏
  4690. ${br.rule}
  4691. `;
  4692. }
  4693. });
  4694. styleBoxes.forEach((box, i) => {
  4695. if (canApplyCss(i)) {
  4696. data[box]
  4697. .split("\n")
  4698. .filter((css, i, csss) => csss.indexOf(css) === i)
  4699. .forEach((css) => pushRecord(css));
  4700. }
  4701. });
  4702. if (text.length > 0) {
  4703. downUrl(
  4704. URL.createObjectURL(
  4705. new Blob([
  4706. `[Adblock Plus 2.0]
  4707. ! 应用地址:
  4708. ! ${location.href}
  4709. ${text}`,
  4710. ])
  4711. ),
  4712. `拦截报告_${location.hostname}.txt`
  4713. );
  4714. } else {
  4715. alert("这个页面没有任何规则生效");
  4716. }
  4717. }
  4718. function switchDisabledStat() {
  4719. const disaList = values.black;
  4720. data.disabled = !disaList.includes(location.hostname);
  4721. if (data.disabled) {
  4722. disaList.push(location.hostname);
  4723. } else {
  4724. disaList.splice(disaList.indexOf(location.hostname), 1);
  4725. }
  4726. values.black = disaList;
  4727. location.reload();
  4728. }
  4729.  
  4730. function getCustomHash(saveHash) {
  4731. return __awaiter(this, void 0, void 0, function* () {
  4732. {
  4733. if (location.protocol === "https:") {
  4734. const hash = new Uint32Array(
  4735. yield window.crypto.subtle.digest(
  4736. "SHA-1",
  4737. yield new Blob([data.customRules]).arrayBuffer()
  4738. )
  4739. ).toString();
  4740. if (saveHash) values.hash = hash;
  4741. return hash;
  4742. } else {
  4743. return values.hash;
  4744. }
  4745. }
  4746. });
  4747. }
  4748. function initRules(apply) {
  4749. let abpRules = {};
  4750. data.receivedRules = "";
  4751. abpRules = values.rules;
  4752. {
  4753. onlineRules.forEach((rule) => {
  4754. const resRule = getRuleFromResource(rule.标识);
  4755. if (resRule && !abpRules[rule.标识]) abpRules[rule.标识] = resRule;
  4756. });
  4757. }
  4758. Object.keys(abpRules).forEach((name) => {
  4759. data.receivedRules += "\n" + abpRules[name];
  4760. });
  4761. data.allRules = data.customRules + data.receivedRules;
  4762. if (apply) splitRules();
  4763. return data.receivedRules.length;
  4764. }
  4765. function styleApplyExec(type) {
  4766. if (canApplyCss(type)) {
  4767. const csss = data[styleBoxes[type]];
  4768. new ExtendedCss({
  4769. styleSheet: csss.replaceAll(/\/\*\s*\d.+?\s*\*\//g, ""),
  4770. }).apply();
  4771. if (!(type % 2 == 1)) addStyle(csss);
  4772. }
  4773. }
  4774. function styleApply() {
  4775. parseBRules();
  4776. if (data.appliedLevel == 3) return;
  4777. for (let type = 0; type < 4; type++) styleApplyExec(type);
  4778. gmMenu("export", reportRecord);
  4779. }
  4780. function parseRules() {
  4781. function addRule(rule, exten) {
  4782. const [full, selector] = ruleToCss(rule, data.preset);
  4783. const index = exten + (rule.generic ? 0 : 2);
  4784. const checkResult = ExtendedCss.validate(selector);
  4785. if (checkResult.ok) {
  4786. data[styleBoxes[index]] += full;
  4787. data.appliedCount++;
  4788. } else {
  4789. console.error("选择器检查错误:", rule, checkResult.error);
  4790. }
  4791. }
  4792. styleBoxes.forEach((box) => {
  4793. data[box] = "";
  4794. });
  4795. [data.styles, data.extStyles, data.selectors, data.extSelectors].forEach(
  4796. (r, t) => {
  4797. const sels = new Set();
  4798. r.white.forEach((obj) => !sels.has(obj.sel) && sels.add(obj.sel));
  4799. r.black
  4800. .filter((obj) => !sels.has(obj.sel) && sels.add(obj.sel))
  4801. .forEach((s) => addRule(s, t % 2));
  4802. }
  4803. );
  4804. gmMenu("count", cleanRules);
  4805. saveCss();
  4806. if (!data.saved) styleApply();
  4807. }
  4808. function splitRules() {
  4809. const bRules = [],
  4810. sels = new Set();
  4811. dataBoxes.forEach((box) => {
  4812. data[box] = makeRuleBox();
  4813. });
  4814. data.allRules.split("\n").forEach((rule) => {
  4815. if (isBasicRule(rule)) {
  4816. const brule = bRuleSpliter(rule);
  4817. if (brule) bRules.push(brule);
  4818. } else {
  4819. const ruleObj = ruleLoader(rule);
  4820. if (typeof ruleObj !== "undefined") {
  4821. if (ruleObj.black === "black" && sels.has(ruleObj.sel)) return;
  4822. if (ruleObj.black === "white" && !sels.has(ruleObj.sel))
  4823. sels.add(ruleObj.sel);
  4824. data[dataBoxes[ruleObj.type]][ruleObj.black].push(ruleObj);
  4825. }
  4826. }
  4827. });
  4828. values.brules = bRules;
  4829. parseRules();
  4830. }
  4831.  
  4832. function makeInitMenu() {
  4833. gmMenu("count", cleanRules);
  4834. {
  4835. gmMenu("update", () =>
  4836. __awaiter(this, void 0, void 0, function* () {
  4837. yield performUpdate(true);
  4838. location.reload();
  4839. })
  4840. );
  4841. }
  4842. }
  4843. function storeRule(rule, resp) {
  4844. let savedRules = {};
  4845. savedRules = values.rules;
  4846. if (resp.responseText) {
  4847. let parsed = resp.responseText;
  4848. if (rule.筛选后存储) {
  4849. parsed = resp.responseText
  4850. .split("\n")
  4851. .filter((rule) => CRRE.test(rule) || isBasicRule(rule))
  4852. .join("\n");
  4853. }
  4854. savedRules[rule.标识] = parsed;
  4855. {
  4856. values.rules = savedRules;
  4857. if (values.rules[rule.标识].length !== 0) {
  4858. const etag = extrEtag(resp),
  4859. savedEtags = values.etags;
  4860. if (etag) {
  4861. savedEtags[rule.标识] = etag;
  4862. values.etags = savedEtags;
  4863. }
  4864. }
  4865. }
  4866. data.receivedRules += "\n" + savedRules[rule.标识];
  4867. }
  4868. }
  4869. function fetchRuleBody(rule) {
  4870. var _a;
  4871. return __awaiter(this, void 0, void 0, function* () {
  4872. const getResp = yield promiseXhr({
  4873. method: "GET",
  4874. responseType: "text",
  4875. url: rule.地址,
  4876. }).catch((error) => {
  4877. console.error("规则: ", rule.地址, " 下载错误: ", error);
  4878. });
  4879. if (
  4880. (_a =
  4881. getResp === null || getResp === void 0
  4882. ? void 0
  4883. : getResp.responseText) === null || _a === void 0
  4884. ? void 0
  4885. : _a.length
  4886. ) {
  4887. storeRule(rule, getResp);
  4888. return true;
  4889. } else return false;
  4890. });
  4891. }
  4892. function fetchRuleGet(resp, rule) {
  4893. const etag = extrEtag(resp),
  4894. savedEtags = values.etags;
  4895. return new Promise((resolve, reject) =>
  4896. __awaiter(this, void 0, void 0, function* () {
  4897. var _a;
  4898. if (
  4899. (_a =
  4900. resp === null || resp === void 0 ? void 0 : resp.responseText) ===
  4901. null || _a === void 0
  4902. ? void 0
  4903. : _a.length
  4904. ) {
  4905. storeRule(rule, resp);
  4906. etag !==
  4907. (savedEtags === null || savedEtags === void 0
  4908. ? void 0
  4909. : savedEtags[rule.标识])
  4910. ? resolve()
  4911. : reject("ETag 一致");
  4912. } else {
  4913. if (
  4914. etag !==
  4915. (savedEtags === null || savedEtags === void 0
  4916. ? void 0
  4917. : savedEtags[rule.标识])
  4918. ) {
  4919. (yield fetchRuleBody(rule)) ? resolve() : reject("GET 失败");
  4920. } else reject("ETag 一致");
  4921. }
  4922. })
  4923. );
  4924. }
  4925. function fetchRule(rule) {
  4926. let headRespError;
  4927. return new Promise((resolve, reject) =>
  4928. __awaiter(this, void 0, void 0, function* () {
  4929. var _a;
  4930. const headResp = yield promiseXhr({
  4931. method: "HEAD",
  4932. responseType: "text",
  4933. url: rule.地址,
  4934. }).catch((error) => {
  4935. headRespError = error;
  4936. console.error("规则: ", rule.地址, " HEAD 错误: ", error);
  4937. });
  4938. if (!headResp) {
  4939. // Via HEAD 会超时,但可以得到 ETag
  4940. if (
  4941. (_a =
  4942. headRespError === null || headRespError === void 0
  4943. ? void 0
  4944. : headRespError.resp) === null || _a === void 0
  4945. ? void 0
  4946. : _a.responseHeaders
  4947. ) {
  4948. resolve(fetchRuleGet(headRespError.resp, rule));
  4949. } else {
  4950. reject("HEAD 失败");
  4951. }
  4952. } else {
  4953. resolve(fetchRuleGet(headResp, rule));
  4954. }
  4955. })
  4956. );
  4957. }
  4958. function fetchRules() {
  4959. return __awaiter(this, void 0, void 0, function* () {
  4960. let hasUpdate = onlineRules.length;
  4961. data.updating = true;
  4962. gmMenu("update", () => undefined);
  4963. for (const rule of onlineRules) {
  4964. if (rule.在线更新) {
  4965. yield fetchRule(rule).catch((error) => {
  4966. console.error("获取规则 ", rule, " 发生错误: ", error);
  4967. hasUpdate--;
  4968. });
  4969. } else {
  4970. hasUpdate--;
  4971. }
  4972. }
  4973. values.time = new Date().toLocaleString("zh-CN");
  4974. if (hasUpdate > 0) {
  4975. values.hasSave.forEach((host) => {
  4976. if (host === location.hostname) {
  4977. initRules(true);
  4978. data.updating = false;
  4979. makeInitMenu();
  4980. } else {
  4981. const save = gmValue("get", true, `ajs_saved_styles_${host}`);
  4982. if (save) {
  4983. save.needUpdate = true;
  4984. gmValue("set", true, `ajs_saved_styles_${host}`, save);
  4985. }
  4986. }
  4987. });
  4988. } else {
  4989. data.updating = false;
  4990. makeInitMenu();
  4991. }
  4992. });
  4993. }
  4994. function performUpdate(force) {
  4995. if (data.isFrame) return Promise.reject();
  4996. return force || new Date(values.time).getDate() !== new Date().getDate()
  4997. ? fetchRules()
  4998. : Promise.resolve();
  4999. }
  5000.  
  5001. function main() {
  5002. return __awaiter(this, void 0, void 0, function* () {
  5003. gmMenu("disable", switchDisabledStat);
  5004. if (data.disabled) return;
  5005. if (values.hasSave.includes(location.hostname)) readCss();
  5006. const hash = yield getCustomHash(false);
  5007. saved: {
  5008. makeInitMenu();
  5009. if (values.hash !== hash) {
  5010. getCustomHash(true);
  5011. initRules(true);
  5012. break saved;
  5013. }
  5014. if (data.saved) {
  5015. styleApply();
  5016. if (!data.update) break saved;
  5017. }
  5018. if (initRules(false) === 0) {
  5019. yield performUpdate(true);
  5020. initRules(true);
  5021. }
  5022. splitRules();
  5023. }
  5024. {
  5025. try {
  5026. yield performUpdate(false);
  5027. } catch (_error) {
  5028. console.warn("iframe: ", location.href, " 取消更新");
  5029. }
  5030. }
  5031. });
  5032. }
  5033. function runOnce(key, func) {
  5034. if (key in cat.unsafeWindow) return Promise.reject();
  5035. cat.unsafeWindow[key] = true;
  5036. return func();
  5037. }
  5038. {
  5039. runOnce(data.mutex, main);
  5040. }
  5041. })({
  5042. GM_info: typeof GM_info == "object" ? GM_info : {},
  5043. unsafeWindow: typeof unsafeWindow == "object" ? unsafeWindow : window,
  5044. GM_registerMenuCommand:
  5045. typeof GM_registerMenuCommand == "function"
  5046. ? GM_registerMenuCommand
  5047. : undefined,
  5048. GM_unregisterMenuCommand:
  5049. typeof GM_unregisterMenuCommand == "function"
  5050. ? GM_unregisterMenuCommand
  5051. : undefined,
  5052. GM_getValue: typeof GM_getValue == "function" ? GM_getValue : undefined,
  5053. GM_deleteValue:
  5054. typeof GM_deleteValue == "function" ? GM_deleteValue : undefined,
  5055. GM_setValue: typeof GM_setValue == "function" ? GM_setValue : undefined,
  5056. GM_addStyle: typeof GM_addStyle == "function" ? GM_addStyle : undefined,
  5057. GM_xmlhttpRequest:
  5058. typeof GM_xmlhttpRequest == "function" ? GM_xmlhttpRequest : undefined,
  5059. GM_getResourceText:
  5060. typeof GM_getResourceText == "function" ? GM_getResourceText : undefined,
  5061. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址