套壳油猴的广告拦截脚本

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

当前为 2022-12-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AdBlock Script for WebView
  3. // @name:zh-CN 套壳油猴的广告拦截脚本
  4. // @author Lemon399
  5. // @version 2.4.0
  6. // @description Parse ABP Cosmetic rules to CSS and apply it.
  7. // @description:zh-CN 将 ABP 中的元素隐藏规则转换为 CSS 使用
  8. // @require https://greasyfork.org/scripts/452263-extended-css/code/extended-css.js?version=1130140
  9. // @resource jiekouAD https://code.gitlink.org.cn/damengzhu/banad/raw/branch/main/jiekouAD.txt
  10. // @resource CSSRule https://code.gitlink.org.cn/damengzhu/abpmerge/raw/branch/main/CSSRule.txt
  11. // @match https://*/*
  12. // @match http://*/*
  13. // @run-at document-start
  14. // @grant unsafeWindow
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_unregisterMenuCommand
  17. // @grant GM_getValue
  18. // @grant GM_deleteValue
  19. // @grant GM_setValue
  20. // @grant GM_addStyle
  21. // @grant GM_xmlhttpRequest
  22. // @grant GM_getResourceText
  23. // @namespace https://lemon399-bitbucket-io.vercel.app/
  24. // @source https://gitee.com/lemon399/tampermonkey-cli/tree/master/projects/abp_parse
  25. // @connect code.gitlink.org.cn
  26. // @copyright GPL-3.0
  27. // @license GPL-3.0
  28. // ==/UserScript==
  29.  
  30. (function (tm, ExtendedCss) {
  31. "use strict";
  32.  
  33. function __awaiter(thisArg, _arguments, P, generator) {
  34. function adopt(value) {
  35. return value instanceof P
  36. ? value
  37. : new P(function (resolve) {
  38. resolve(value);
  39. });
  40. }
  41. return new (P || (P = Promise))(function (resolve, reject) {
  42. function fulfilled(value) {
  43. try {
  44. step(generator.next(value));
  45. } catch (e) {
  46. reject(e);
  47. }
  48. }
  49. function rejected(value) {
  50. try {
  51. step(generator["throw"](value));
  52. } catch (e) {
  53. reject(e);
  54. }
  55. }
  56. function step(result) {
  57. result.done
  58. ? resolve(result.value)
  59. : adopt(result.value).then(fulfilled, rejected);
  60. }
  61. step((generator = generator.apply(thisArg, _arguments || [])).next());
  62. });
  63. }
  64.  
  65. const onlineRules = [];
  66. onlineRules.push(
  67. {
  68. 标识: "jiekouAD",
  69. 地址: "https://code.gitlink.org.cn/damengzhu/banad/raw/branch/main/jiekouAD.txt",
  70. 在线更新: !!1,
  71. 筛选后存储: !!1,
  72. },
  73. {
  74. 标识: "CSSRule",
  75. 地址: "https://code.gitlink.org.cn/damengzhu/abpmerge/raw/branch/main/CSSRule.txt",
  76. 在线更新: !!1,
  77. 筛选后存储: !!0,
  78. }
  79. );
  80. let defaultRules = `
  81. ! 没有 ## #@# #?# #@?#
  82. ! #$# #@$# #$?# #@$?# 的行和
  83. ! 开头为 ! 的行会忽略
  84. !
  85. ! 由于语法限制,内置规则中
  86. ! 一个反斜杠需要改成两个,像这样 \\
  87. !
  88. ! 若要修改地址,请注意同步修改
  89. ! 头部的 @connect @resource
  90.  
  91. `;
  92.  
  93. const CRRE =
  94. /^(~?[\w-]+(?:\.[\w-]+)*(?:\.[\w-]+|\.\*)(?:,~?[\w-]+(?:\.[\w-]+)*(?:\.[\w-]+|\.\*))*)?(#@?\$?\??#)([^\s^+].*)/,
  95. BRRE =
  96. /^(?:@@?)(\|\|?)?(https?:\/\/)?([^\s"<>`]+?[|^]?)\$((?:~?[\w-]+(?:=[\s\w'":.-]+)?|_+)(?:,(?:~?[\w-]+(?:=[\s\w'":.-]+)?|_+))*)$/,
  97. CCRE = /^\/\* (.+?) \*\/ /,
  98. BROpts = [
  99. "elemhide",
  100. "ehide",
  101. "specifichide",
  102. "shide",
  103. "generichide",
  104. "ghide",
  105. ];
  106. const CRFlags = ["##", "#@#", "#?#", "#@?#", "#$#", "#@$#", "#$?#", "#@$?#"];
  107. function bRuleSpliter(rule) {
  108. const group = rule.match(BRRE),
  109. body = group[3],
  110. options = group[4].split(","),
  111. sepChar = "[!#&'()+,/:;=?@~|{}$]",
  112. anyChar = '([^\\s"<>`]*)',
  113. eh = hasSome(options, ["elemhide", "ehide"]),
  114. sh = hasSome(options, ["specifichide", "shide"]),
  115. gh = hasSome(options, ["generichide", "ghide"]);
  116. let urlres = "";
  117. urlres += group[1]
  118. ? group[2]
  119. ? `^${group[2]}`
  120. : `^https?://((${anyChar}:)?(${anyChar}@))?([\\w-]+\\.)*?`
  121. : `^${anyChar}`;
  122. urlres += body
  123. .replace(/[-\\$+.()[\]{}]/g, "\\$&")
  124. .replace(/\|$/, "$")
  125. .replace(/\|/g, "\\|")
  126. .replace(/\^$/, `(${sepChar}|$)`)
  127. .replace(/\^/g, sepChar)
  128. .replace(/\*$/g, "")
  129. .replace(/\*/g, anyChar);
  130. return {
  131. rule: rule,
  132. match: urlres,
  133. level: eh ? 3 : gh && sh ? 3 : sh ? 2 : gh ? 1 : 0,
  134. };
  135. }
  136. function isBasicRule(rule) {
  137. return BRRE.test(rule) && hasSome(rule, BROpts);
  138. }
  139. function bRuleParser(rule, url = location.href) {
  140. return new RegExp(rule.match).test(url) ? rule.level : 0;
  141. }
  142. function findMatches(string, res) {
  143. let result = [-1, null];
  144. res.forEach((re, i) => {
  145. const match = string.match(re);
  146. if (match) result = [i, match];
  147. });
  148. return result;
  149. }
  150. function getEtag(header) {
  151. const result = findMatches(header, [
  152. /(e|E)tag: \"(\w+)\"/,
  153. // WebMonkey 系
  154. /(e|E)tag: \[\"(\w+)\"\]/,
  155. // 书签地球
  156. /(e|E)tag=\"(\w+)\"/,
  157. ]);
  158. return result[1] ? result[1][2] : null;
  159. }
  160. function makeRuleBox() {
  161. return {
  162. black: [],
  163. white: [],
  164. };
  165. }
  166. function domainChecker(domains) {
  167. const results = [],
  168. invResults = [],
  169. urlSuffix = /\.+?[\w-]+$/.exec(location.hostname);
  170. let totalResult = [0, false],
  171. black = false,
  172. white = false,
  173. match = false;
  174. domains.forEach((domain) => {
  175. if (domain.endsWith(".*") && Array.isArray(urlSuffix)) {
  176. domain = domain.replace(".*", urlSuffix[0]);
  177. }
  178. const invert = domain[0] == "~";
  179. if (invert) domain = domain.slice(1);
  180. const result = location.hostname.endsWith(domain);
  181. if (invert) {
  182. if (result) white = true;
  183. invResults.push([domain.length, !result]);
  184. } else {
  185. if (result) black = true;
  186. results.push([domain.length, result]);
  187. }
  188. });
  189. if (results.length > 0 && !black) {
  190. match = false;
  191. } else if (invResults.length > 0 && !white) {
  192. match = true;
  193. } else {
  194. results.forEach((r) => {
  195. if (r[0] >= totalResult[0] && r[1]) {
  196. totalResult = r;
  197. }
  198. });
  199. invResults.forEach((r) => {
  200. if (r[0] >= totalResult[0] && !r[1]) {
  201. totalResult = r;
  202. }
  203. });
  204. match = totalResult[1];
  205. }
  206. return [match, results.length == 0];
  207. }
  208. function hasSome(str, arr) {
  209. return arr.some((word) => str.includes(word));
  210. }
  211. function ruleSpliter(rule) {
  212. var _a;
  213. const group = rule.match(CRRE);
  214. if (group) {
  215. const sel = group[3],
  216. type = CRFlags.indexOf(group[2]),
  217. matchResult = !group[1]
  218. ? [true, true]
  219. : domainChecker(group[1].split(","));
  220. if (sel && matchResult[0]) {
  221. return {
  222. black: type % 2 ? "white" : "black",
  223. type: Math.floor(type / 2),
  224. place: (_a = group[1]) !== null && _a !== void 0 ? _a : "*",
  225. generic: matchResult[1],
  226. sel,
  227. };
  228. }
  229. }
  230. }
  231. function ruleLoader(rule) {
  232. if (
  233. hasSome(rule, [
  234. ":matches-path(",
  235. ":min-text-length(",
  236. ":watch-attr(",
  237. ":-abp-properties(",
  238. ":matches-property(",
  239. ])
  240. )
  241. return;
  242. // 去掉开头空格
  243. rule = rule.replace(/^ +/, "");
  244. // 如果 #$# 不包含 {} 就排除
  245. // 可以尽量排除 Snippet Filters
  246. if (/(\w|^)#\$#/.test(rule) && !/{.+}/.test(rule)) return;
  247. // ## -> #?#
  248. if (
  249. /(\w|^)#@?#/.test(rule) &&
  250. hasSome(rule, [
  251. ":has(",
  252. ":-abp-has(",
  253. "[-ext-has=",
  254. ":has-text(",
  255. "contains(",
  256. "-abp-contains(",
  257. "[-ext-contains=",
  258. "matches-css(",
  259. "[-ext-matches-css=",
  260. "matches-css-before(",
  261. "[-ext-matches-css-before=",
  262. "matches-css-after(",
  263. "[-ext-matches-css-after=",
  264. "matches-attr(",
  265. "nth-ancestor(",
  266. "upward(",
  267. "xpath(",
  268. "remove()",
  269. "not(",
  270. ])
  271. ) {
  272. rule = rule.replace(/(\w|^)##/, "$1#?#").replace(/(\w|^)#@#/, "$1#@?#");
  273. }
  274. // :style(...) 转换
  275. // example.com#?##id:style(color: red)
  276. // example.com#$?##id { color: red }
  277. if (rule.includes(":style(")) {
  278. rule = rule
  279. .replace(/(\w|^)##/, "$1#$#")
  280. .replace(/(\w|^)#@#/, "$1#@$#")
  281. .replace(/(\w|^)#\?#/, "$1#$?#")
  282. .replace(/(\w|^)#@\?#/, "$1#@$?#")
  283. .replace(/:style\(/, " { ")
  284. .replace(/\)$/, " }");
  285. }
  286. return ruleSpliter(rule);
  287. }
  288. function textToBlobUrl(text) {
  289. return URL.createObjectURL(new Blob([text]));
  290. }
  291. function downUrl(url, name) {
  292. const a = document.createElement("a");
  293. a.href = url;
  294. a.download = name;
  295. Object.assign(a.style, {
  296. position: "fixed",
  297. top: "200%",
  298. });
  299. document.body.appendChild(a);
  300. setTimeout(() => {
  301. a.click();
  302. a.remove();
  303. }, 0);
  304. }
  305.  
  306. const data = {
  307. disabled: false,
  308. saved: false,
  309. update: true,
  310. updating: false,
  311. receivedRules: "",
  312. customRules: defaultRules,
  313. allRules: "",
  314. presetCss:
  315. " {display: none !important;width: 0 !important;height: 0 !important;} ",
  316. genHideCss: "",
  317. genExtraCss: "",
  318. spcHideCss: "",
  319. spcExtraCss: "",
  320. bRules: {
  321. levels: [],
  322. rules: [],
  323. },
  324. appliedLevel: 0,
  325. appliedCount: 0,
  326. records: [],
  327. isFrame: tm.unsafeWindow.self !== tm.unsafeWindow.top,
  328. isClean: false,
  329. mutex: "__lemon__abp__parser__$__",
  330. timeout: 6000,
  331. xTimeout: 700,
  332. tryCount: 3,
  333. tryTimeout: 200,
  334. };
  335.  
  336. const values = {
  337. get black() {
  338. const arrStr = gmValue("get", false, "ajs_disabled_domains", "");
  339. return typeof arrStr == "string" && arrStr.length > 0
  340. ? arrStr.split(",")
  341. : [];
  342. },
  343. set black(v) {
  344. gmValue(
  345. "set",
  346. false,
  347. "ajs_disabled_domains",
  348. v === null || v === void 0 ? void 0 : v.join()
  349. );
  350. },
  351. get rules() {
  352. return gmValue("get", true, "ajs_saved_abprules", {});
  353. },
  354. set rules(v) {
  355. gmValue("set", true, "ajs_saved_abprules", v);
  356. },
  357. get css() {
  358. return gmValue("get", true, `ajs_saved_styles_${location.hostname}`, {
  359. needUpdate: true,
  360. genHideCss: "",
  361. genExtraCss: "",
  362. spcHideCss: "",
  363. spcExtraCss: "",
  364. });
  365. },
  366. set css(v) {
  367. gmValue("set", true, `ajs_saved_styles_${location.hostname}`, v);
  368. },
  369. get hasSave() {
  370. const arrStr = gmValue("get", false, "ajs_hasSave_domains", "");
  371. return typeof arrStr == "string" && arrStr.length > 0
  372. ? arrStr.split(",")
  373. : [];
  374. },
  375. set hasSave(v) {
  376. gmValue(
  377. "set",
  378. false,
  379. "ajs_hasSave_domains",
  380. v === null || v === void 0 ? void 0 : v.join()
  381. );
  382. },
  383. get time() {
  384. return gmValue("get", false, "ajs_rules_ver", "0/0/0 0:0:0");
  385. },
  386. set time(v) {
  387. gmValue("set", false, "ajs_rules_ver", v);
  388. },
  389. get drlen() {
  390. return gmValue("get", false, "ajs_drule_length", 0);
  391. },
  392. set drlen(v) {
  393. gmValue("set", false, "ajs_drule_length", v);
  394. },
  395. get etags() {
  396. return gmValue("get", true, "ajs_rules_etags", {});
  397. },
  398. set etags(v) {
  399. gmValue("set", true, "ajs_rules_etags", v);
  400. },
  401. get brules() {
  402. return gmValue("get", true, "ajs_modifier_rules", []);
  403. },
  404. set brules(v) {
  405. gmValue("set", true, "ajs_modifier_rules", v);
  406. },
  407. },
  408. menus = {
  409. disable: {
  410. id: undefined,
  411. get text() {
  412. return data.disabled ? "在此网站启用拦截" : "在此网站禁用拦截";
  413. },
  414. },
  415. update: {
  416. id: undefined,
  417. get text() {
  418. const time = values.time;
  419. return data.updating
  420. ? "正在更新..."
  421. : `点击更新: ${time.slice(0, 1) === "0" ? "未知时间" : time}`;
  422. },
  423. },
  424. count: {
  425. id: undefined,
  426. get text() {
  427. var _a;
  428. let cssCount = "";
  429. if ((data.appliedLevel & 1) == 0)
  430. cssCount += data.genHideCss + data.genExtraCss;
  431. if ((data.appliedLevel & 2) == 0)
  432. cssCount += data.spcHideCss + data.spcExtraCss;
  433. return data.isClean
  434. ? "已清空,点击刷新重新加载规则"
  435. : `${
  436. data.saved
  437. ? "CSS: " +
  438. ((_a = cssCount.match(/{/g)) === null || _a === void 0
  439. ? void 0
  440. : _a.length)
  441. : "规则: " +
  442. data.appliedCount +
  443. "/" +
  444. data.allRules.split("\n").length
  445. },点击清空规则`;
  446. },
  447. },
  448. export: {
  449. id: undefined,
  450. text: "下载统计报告",
  451. },
  452. };
  453. function gmMenu(name, cb) {
  454. var _a;
  455. const id = (_a = menus[name].id) !== null && _a !== void 0 ? _a : undefined;
  456. if (
  457. typeof tm.GM_registerMenuCommand != "function" ||
  458. typeof tm.GM_unregisterMenuCommand != "function" ||
  459. data.isFrame
  460. )
  461. return;
  462. if (typeof id !== "undefined") {
  463. tm.GM_unregisterMenuCommand(id);
  464. menus[name].id = undefined;
  465. }
  466. if (typeof cb == "function") {
  467. menus[name].id = tm.GM_registerMenuCommand(menus[name].text, cb);
  468. }
  469. }
  470. function gmValue(action, json, key, value) {
  471. switch (action) {
  472. case "get":
  473. let v;
  474. try {
  475. v = tm.GM_getValue(key, json ? JSON.stringify(value) : value);
  476. } catch (error) {
  477. return;
  478. }
  479. return json && typeof v == "string" ? JSON.parse(v) : v;
  480. case "set":
  481. try {
  482. value === null || value === undefined
  483. ? tm.GM_deleteValue(key)
  484. : tm.GM_setValue(key, json ? JSON.stringify(value) : value);
  485. } catch (error) {
  486. tm.GM_deleteValue(key);
  487. }
  488. break;
  489. }
  490. }
  491. function addStyle(css, pass = 0) {
  492. let el;
  493. if (pass >= data.tryCount) return;
  494. if (typeof tm.GM_addStyle == "function") {
  495. el = tm.GM_addStyle(css);
  496. } else {
  497. el = document.createElement("style");
  498. el.textContent = css;
  499. document.documentElement.appendChild(el);
  500. }
  501. if (el) {
  502. setTimeout(() => {
  503. if (!document.documentElement.contains(el)) {
  504. addStyle(css, pass + 1);
  505. }
  506. }, data.tryTimeout);
  507. }
  508. }
  509.  
  510. var _a, _b, _c;
  511. const selectors = makeRuleBox(),
  512. extSelectors = makeRuleBox(),
  513. styles = makeRuleBox(),
  514. extStyles = makeRuleBox(),
  515. styleBoxes = ["genHideCss", "genExtraCss", "spcHideCss", "spcExtraCss"];
  516. data.customRules +=
  517. "\n" +
  518. ((_c =
  519. (_b =
  520. (_a = tm.GM_info.script) === null || _a === void 0
  521. ? void 0
  522. : _a.options) === null || _b === void 0
  523. ? void 0
  524. : _b.comment) !== null && _c !== void 0
  525. ? _c
  526. : "") +
  527. "\n";
  528. function promiseXhr(details) {
  529. return __awaiter(this, void 0, void 0, function* () {
  530. let loaded = false;
  531. try {
  532. return yield new Promise((resolve, reject) => {
  533. tm.GM_xmlhttpRequest(
  534. Object.assign(
  535. {
  536. onload(e) {
  537. loaded = true;
  538. resolve(e);
  539. },
  540. onabort: reject.bind(null, "abort"),
  541. onerror(e) {
  542. reject({
  543. error: "error",
  544. resp: e,
  545. });
  546. },
  547. ontimeout: reject.bind(null, "timeout"),
  548. onreadystatechange(e) {
  549. // X 浏览器超时中断
  550. if (e.readyState === 4) {
  551. setTimeout(() => {
  552. if (!loaded)
  553. reject({
  554. error: "X timeout",
  555. resp: e,
  556. });
  557. }, data.xTimeout);
  558. }
  559. // Via 浏览器超时中断,不给成功状态...
  560. if (e.readyState === 3) {
  561. setTimeout(() => {
  562. if (!loaded)
  563. reject({
  564. error: "Via timeout",
  565. resp: e,
  566. });
  567. }, data.timeout);
  568. }
  569. },
  570. timeout: data.timeout,
  571. },
  572. details
  573. )
  574. );
  575. });
  576. } catch (error) {}
  577. });
  578. }
  579. function storeRule(rule, resp) {
  580. const savedRules = values.rules,
  581. savedEtags = values.etags;
  582. if (resp.responseHeaders) {
  583. const etag = getEtag(resp.responseHeaders);
  584. if (etag) {
  585. savedEtags[rule.标识] = etag;
  586. values.etags = savedEtags;
  587. }
  588. }
  589. if (resp.responseText) {
  590. if (rule.筛选后存储) {
  591. let parsed = "";
  592. resp.responseText.split("\n").forEach((rule) => {
  593. if (CRRE.test(rule) || isBasicRule(rule)) parsed += rule + "\n";
  594. });
  595. savedRules[rule.标识] = parsed;
  596. } else {
  597. savedRules[rule.标识] = resp.responseText;
  598. }
  599. values.rules = savedRules;
  600. if (Object.keys(values.rules).length === 0) {
  601. data.receivedRules += "\n" + savedRules[rule.标识] + "\n";
  602. }
  603. }
  604. }
  605. function fetchRuleBody(rule) {
  606. var _a;
  607. return __awaiter(this, void 0, void 0, function* () {
  608. const getResp = yield promiseXhr({
  609. method: "GET",
  610. responseType: "text",
  611. url: rule.地址,
  612. });
  613. if (
  614. getResp &&
  615. (getResp === null || getResp === void 0
  616. ? void 0
  617. : getResp.responseText) &&
  618. ((_a = getResp.responseText) === null || _a === void 0
  619. ? void 0
  620. : _a.length) > 0
  621. ) {
  622. storeRule(rule, getResp);
  623. return true;
  624. } else return false;
  625. });
  626. }
  627. function fetchRule(rule) {
  628. return new Promise((resolve, reject) =>
  629. __awaiter(this, void 0, void 0, function* () {
  630. var _a, _b, _e;
  631. const headResp = yield promiseXhr({
  632. method: "HEAD",
  633. responseType: "text",
  634. url: rule.地址,
  635. });
  636. if (!headResp) {
  637. reject("HEAD 失败");
  638. } else {
  639. const etag = getEtag(
  640. typeof headResp.responseHeaders == "string"
  641. ? headResp.responseHeaders
  642. : (_b = (_a = headResp).getAllResponseHeaders) === null ||
  643. _b === void 0
  644. ? void 0
  645. : _b.call(_a)
  646. ),
  647. savedEtags = values.etags;
  648. if (
  649. (headResp === null || headResp === void 0
  650. ? void 0
  651. : headResp.responseText) &&
  652. ((_e = headResp.responseText) === null || _e === void 0
  653. ? void 0
  654. : _e.length) > 0
  655. ) {
  656. storeRule(rule, headResp);
  657. !etag || etag !== savedEtags[rule.标识]
  658. ? resolve()
  659. : reject("ETag 一致");
  660. } else {
  661. if (!etag || etag !== savedEtags[rule.标识]) {
  662. (yield fetchRuleBody(rule)) ? resolve() : reject("GET 失败");
  663. } else reject("ETag 一致");
  664. }
  665. }
  666. })
  667. );
  668. }
  669. function fetchRules(apply) {
  670. return __awaiter(this, void 0, void 0, function* () {
  671. const has = values.hasSave;
  672. let hasUpdate = onlineRules.length;
  673. data.updating = true;
  674. gmMenu("update", () => undefined);
  675. for (const rule of onlineRules) {
  676. if (rule.在线更新) {
  677. yield fetchRule(rule).catch((error) => {
  678. hasUpdate--;
  679. });
  680. }
  681. }
  682. values.time = new Date().toLocaleString("zh-CN");
  683. if (has.length > 0 && hasUpdate > 0) {
  684. has.forEach((host) => {
  685. const save = gmValue("get", true, `ajs_saved_styles_${host}`);
  686. save.needUpdate = true;
  687. gmValue("set", true, `ajs_saved_styles_${host}`, save);
  688. });
  689. }
  690. initRules(apply);
  691. });
  692. }
  693. function performUpdate(force, apply) {
  694. if (data.isFrame) return Promise.reject();
  695. return force || new Date(values.time).getDate() !== new Date().getDate()
  696. ? fetchRules(apply)
  697. : Promise.resolve();
  698. }
  699. function switchDisabledStat() {
  700. const disaList = values.black;
  701. data.disabled = !disaList.includes(location.hostname);
  702. if (data.disabled) {
  703. disaList.push(location.hostname);
  704. } else {
  705. disaList.splice(disaList.indexOf(location.hostname), 1);
  706. }
  707. values.black = disaList;
  708. location.reload();
  709. }
  710. function makeInitMenu() {
  711. gmMenu("update", () =>
  712. __awaiter(this, void 0, void 0, function* () {
  713. yield performUpdate(true, false);
  714. location.reload();
  715. })
  716. );
  717. gmMenu("count", cleanRules);
  718. }
  719. function initRules(apply) {
  720. const abpRules = values.rules;
  721. if (typeof tm.GM_getResourceText == "function") {
  722. onlineRules.forEach((rule) => {
  723. let resRule;
  724. try {
  725. resRule = tm.GM_getResourceText(rule.标识);
  726. } catch (error) {
  727. resRule = "";
  728. }
  729. if (resRule && !abpRules[rule.标识]) abpRules[rule.标识] = resRule;
  730. });
  731. }
  732. const abpKeys = Object.keys(abpRules);
  733. abpKeys.forEach((name) => {
  734. data.receivedRules += "\n" + abpRules[name] + "\n";
  735. });
  736. values.drlen = data.customRules.length;
  737. data.allRules = data.customRules + data.receivedRules;
  738. data.updating = false;
  739. makeInitMenu();
  740. if (apply) splitRules();
  741. return data.receivedRules.length;
  742. }
  743. function styleInject(csss, extra) {
  744. if (extra) {
  745. csss.split("\n").forEach((css) => {
  746. new ExtendedCss({
  747. styleSheet: css.replace(CCRE, ""),
  748. }).apply();
  749. });
  750. } else {
  751. addStyle(csss);
  752. }
  753. }
  754. function styleApply() {
  755. if (data.appliedLevel === 3) return;
  756. if (data.genHideCss.length > 0 && data.appliedLevel !== 1) {
  757. styleInject(data.genHideCss, false);
  758. }
  759. if (data.spcHideCss.length > 0 && data.appliedLevel !== 2) {
  760. styleInject(data.spcHideCss, false);
  761. }
  762. if (data.genExtraCss.length > 0 && data.appliedLevel !== 1) {
  763. styleInject(data.genExtraCss, true);
  764. }
  765. if (data.spcExtraCss.length > 0 && data.appliedLevel !== 2) {
  766. styleInject(data.spcExtraCss, true);
  767. }
  768. gmMenu("export", reportRecord);
  769. }
  770. function reportRecord() {
  771. const flags = ["##", "#?#", "#$#", "#$?#"];
  772. let text = "! 应用地址: \n! " + location.href + "\n";
  773. function pushRecord(css) {
  774. if (css.match(CCRE) === null) return void 0;
  775. const extra = css.match(CCRE)[1],
  776. rule = css.replace(CCRE, ""),
  777. sel = rule.replace(/ {.+$/, ""),
  778. fi = parseInt(extra.slice(0, 1)),
  779. place = extra.slice(1),
  780. count =
  781. fi % 2 == 1
  782. ? ExtendedCss.query(sel).length
  783. : document.querySelectorAll(sel).length,
  784. item = (place == "*" ? "" : place) + flags[fi] + (fi >= 2 ? rule : sel);
  785. if (count > 0) {
  786. text += `\n! 匹配元素数量: ${count}\n${item}\n`;
  787. data.records.push([item, count]);
  788. }
  789. }
  790. if (data.bRules.levels.length > 0) {
  791. data.bRules.levels.forEach((l, i) => {
  792. if (l > 0) {
  793. text += `\n! 禁用${l == 2 ? "特定" : "通用"}元素隐藏\n${
  794. data.bRules.rules[i]
  795. }\n`;
  796. }
  797. });
  798. }
  799. styleBoxes.forEach((box, i) => {
  800. if ((data.appliedLevel & (i >= 2 ? 2 : 1)) == 0 && data[box].length > 0) {
  801. data[box].split("\n").forEach((css) => pushRecord(css));
  802. }
  803. });
  804. console.log("地址: " + location.href);
  805. console.table(data.records);
  806. downUrl(textToBlobUrl(text), `拦截报告_${location.hostname}.txt`);
  807. }
  808. function cleanRules() {
  809. if (confirm(`是否清空存储规则 (${Object.keys(values.rules).length}) ?`)) {
  810. const has = values.hasSave;
  811. values.rules = {};
  812. values.time = "0/0/0 0:0:0";
  813. values.drlen = 0;
  814. values.etags = {};
  815. values.brules = [];
  816. if (has.length > 0) {
  817. has.forEach((host) => {
  818. gmValue("set", true, `ajs_saved_styles_${host}`);
  819. });
  820. values.hasSave = null;
  821. }
  822. data.appliedCount = 0;
  823. data.allRules = "";
  824. data.isClean = true;
  825. gmMenu("update");
  826. gmMenu("export");
  827. gmMenu("count", () => location.reload());
  828. }
  829. }
  830. function parseRules() {
  831. styleBoxes.forEach((box) => {
  832. data[box] = "";
  833. });
  834. [styles, extStyles].forEach((r, t) => {
  835. r.black
  836. .filter((v) => !r.white.includes(v))
  837. .forEach((s) => {
  838. const checkResult = ExtendedCss.validate(s.sel.split("{")[0]);
  839. if (checkResult.ok) {
  840. data[
  841. styleBoxes[s.generic ? t : t + 2]
  842. ] += `/* ${s.type}${s.place} */ ${s.sel} \n`;
  843. data.appliedCount++;
  844. } else {
  845. console.error(
  846. "选择器检查错误:",
  847. s.sel.split("{")[0],
  848. checkResult.error
  849. );
  850. }
  851. });
  852. });
  853. [selectors, extSelectors].forEach((r, t) => {
  854. r.black
  855. .filter((v) => !r.white.includes(v))
  856. .forEach((s, i) => {
  857. const checkResult = ExtendedCss.validate(s.sel);
  858. if (checkResult.ok) {
  859. data[styleBoxes[s.generic ? t : t + 2]] += `${
  860. i == 0 ? "" : "\n"
  861. }/* ${s.type}${s.place} */ ${s.sel + data.presetCss}`;
  862. data.appliedCount++;
  863. } else {
  864. console.error("选择器检查错误:", s.sel, checkResult.error);
  865. }
  866. });
  867. });
  868. if (values.brules.length > 0) parseBRules();
  869. gmMenu("count", cleanRules);
  870. saveCss();
  871. if (!data.saved) styleApply();
  872. }
  873. function splitRules() {
  874. const bRules = [];
  875. data.allRules.split("\n").forEach((rule) => {
  876. const ruleObj = ruleLoader(rule),
  877. boxes = [selectors, extSelectors, styles, extStyles];
  878. if (typeof ruleObj != "undefined") {
  879. if (
  880. ruleObj.black == "black" &&
  881. boxes[ruleObj.type].white.includes(ruleObj)
  882. )
  883. return;
  884. boxes[ruleObj.type][ruleObj.black].push(ruleObj);
  885. } else if (isBasicRule(rule)) {
  886. bRules.push(bRuleSpliter(rule));
  887. }
  888. });
  889. values.brules = bRules;
  890. parseRules();
  891. }
  892. function parseBRules() {
  893. const mrules = values.brules;
  894. data.bRules.levels = [];
  895. data.bRules.rules = [];
  896. mrules.forEach((br) => {
  897. data.bRules.levels.push(bRuleParser(br));
  898. data.bRules.rules.push(br.rule);
  899. });
  900. data.appliedLevel = Math.max(...data.bRules.levels);
  901. }
  902. function saveCss() {
  903. const styles = {
  904. needUpdate: false,
  905. genHideCss: data.genHideCss,
  906. genExtraCss: data.genExtraCss,
  907. spcHideCss: data.spcHideCss,
  908. spcExtraCss: data.spcExtraCss,
  909. },
  910. has = values.hasSave;
  911. values.css = styles;
  912. if (!has.includes(location.hostname)) has.push(location.hostname);
  913. values.hasSave = has;
  914. }
  915. function readCss() {
  916. const styles = values.css;
  917. if (!hasSome(Object.keys(styles), styleBoxes)) {
  918. values.css = {
  919. needUpdate: true,
  920. genHideCss: "",
  921. genExtraCss: "",
  922. spcHideCss: "",
  923. spcExtraCss: "",
  924. };
  925. return false;
  926. }
  927. if (values.brules.length > 0) parseBRules();
  928. styleBoxes.forEach((sname) => {
  929. var _a;
  930. if (styles[sname].length > 0) {
  931. data.saved = true;
  932. data.update =
  933. (_a = styles.needUpdate) !== null && _a !== void 0 ? _a : true;
  934. data[sname] = styles[sname];
  935. }
  936. });
  937. return data.saved;
  938. }
  939. function main() {
  940. var _a, _b;
  941. return __awaiter(this, void 0, void 0, function* () {
  942. if (
  943. location.protocol.indexOf("http") !== 0 ||
  944. location.hostname.length < 4
  945. )
  946. return;
  947. data.disabled = values.black.includes(location.hostname);
  948. gmMenu("disable", switchDisabledStat);
  949. if (data.disabled) return;
  950. if (
  951. ((_b =
  952. (_a = tm.unsafeWindow.mbrowser) === null || _a === void 0
  953. ? void 0
  954. : _a.getVersionCode) === null || _b === void 0
  955. ? void 0
  956. : _b.call(_a)) >= 662
  957. ) {
  958. const vars = [
  959. "ajs_disabled_domains",
  960. "ajs_saved_abprules",
  961. "ajs_rules_etags",
  962. "ajs_rules_ver",
  963. ],
  964. stor = tm.unsafeWindow.localStorage;
  965. vars.forEach((key) => {
  966. if (stor.getItem(key)) {
  967. stor.removeItem(key);
  968. }
  969. });
  970. }
  971. if (values.drlen === data.customRules.length) readCss();
  972. saved: {
  973. if (data.saved) {
  974. styleApply();
  975. makeInitMenu();
  976. if (!data.update) break saved;
  977. }
  978. if (initRules(false) === 0) yield performUpdate(true, true);
  979. splitRules();
  980. }
  981. try {
  982. yield performUpdate(false, false);
  983. } catch (_error) {
  984. console.warn("iframe: ", location.href, " 取消更新");
  985. }
  986. });
  987. }
  988. function runOnce(key, func) {
  989. return __awaiter(this, void 0, void 0, function* () {
  990. if (key in tm.unsafeWindow) return;
  991. tm.unsafeWindow[key] = true;
  992. return yield func();
  993. });
  994. }
  995. {
  996. runOnce(data.mutex, main);
  997. }
  998. })(
  999. {
  1000. GM_info: typeof GM_info == "object" ? GM_info : {},
  1001. unsafeWindow: typeof unsafeWindow == "object" ? unsafeWindow : window,
  1002. GM_registerMenuCommand:
  1003. typeof GM_registerMenuCommand == "function"
  1004. ? GM_registerMenuCommand
  1005. : undefined,
  1006. GM_unregisterMenuCommand:
  1007. typeof GM_unregisterMenuCommand == "function"
  1008. ? GM_unregisterMenuCommand
  1009. : undefined,
  1010. GM_getValue: typeof GM_getValue == "function" ? GM_getValue : undefined,
  1011. GM_deleteValue:
  1012. typeof GM_deleteValue == "function" ? GM_deleteValue : undefined,
  1013. GM_setValue: typeof GM_setValue == "function" ? GM_setValue : undefined,
  1014. GM_addStyle: typeof GM_addStyle == "function" ? GM_addStyle : undefined,
  1015. GM_xmlhttpRequest:
  1016. typeof GM_xmlhttpRequest == "function" ? GM_xmlhttpRequest : undefined,
  1017. GM_getResourceText:
  1018. typeof GM_getResourceText == "function" ? GM_getResourceText : undefined,
  1019. },
  1020. ExtendedCss
  1021. );