搜索引擎去广告

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

目前为 2024-08-25 提交的版本。查看 最新版本

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

QingJ © 2025

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