AdBlock Script for WebView

Parse ABP Cosmetic rules to CSS and apply it.

目前為 2023-05-21 提交的版本,檢視 最新版本

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