Greasy Fork镜像 还支持 简体中文。

AdBlock Script for WebView

Parse ABP Cosmetic rules to CSS and apply it.

目前為 2023-04-29 提交的版本,檢視 最新版本

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

QingJ © 2025

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