搜索引擎去广告

谷歌百度搜狗神马360必应头条抖音搜索去广告,适配电脑和手机,谷歌搜索拦截部分内容广场

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

QingJ © 2025

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