AdBlock Script for WebView

Parse ABP Cosmetic rules to CSS and apply it.

Fra 01.10.2022. Se den seneste versjonen.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name               AdBlock Script for WebView
// @name:zh-CN         套壳油猴的广告拦截脚本
// @author             Lemon399
// @version            2.0
// @description        Parse ABP Cosmetic rules to CSS and apply it.
// @description:zh-CN  将 ABP 元素隐藏规则转换为 CSS 并且应用
// @require            https://greasyfork.org/scripts/452263-extended-css/code/extended-css.js?version=1099366
// @match              *://*/*
// @run-at             document-start
// @grant              GM_getValue
// @grant              GM_deleteValue
// @grant              GM_setValue
// @grant              GM_registerMenuCommand
// @grant              GM_unregisterMenuCommand
// @grant              GM_xmlhttpRequest
// @grant              GM_addStyle
// @namespace          https://lemon399-bitbucket-io.vercel.app/
// @source             https://gitee.com/lemon399/tampermonkey-cli/tree/master/projects/abp_parse
// @connect            code.gitlink.org.cn
// @copyright          GPL-3.0
// @license          GPL-3.0
// ==/UserScript==

(function (tm, ExtendedCss) {
  "use strict";

  function _interopDefaultLegacy(e) {
    return e && typeof e === "object" && "default" in e
      ? e
      : {
          default: e,
        };
  }

  var ExtendedCss__default = _interopDefaultLegacy(ExtendedCss);

  var __assign = function () {
    __assign =
      Object.assign ||
      function __assign(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
          s = arguments[i];

          for (var p in s)
            if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
        }

        return t;
      };

    return __assign.apply(this, arguments);
  };

  function __awaiter(thisArg, _arguments, P, generator) {
    function adopt(value) {
      return value instanceof P
        ? value
        : new P(function (resolve) {
            resolve(value);
          });
    }

    return new (P || (P = Promise))(function (resolve, reject) {
      function fulfilled(value) {
        try {
          step(generator.next(value));
        } catch (e) {
          reject(e);
        }
      }

      function rejected(value) {
        try {
          step(generator["throw"](value));
        } catch (e) {
          reject(e);
        }
      }

      function step(result) {
        result.done
          ? resolve(result.value)
          : adopt(result.value).then(fulfilled, rejected);
      }

      step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
  }

  function __generator(thisArg, body) {
    var _ = {
        label: 0,
        sent: function () {
          if (t[0] & 1) throw t[1];
          return t[1];
        },
        trys: [],
        ops: [],
      },
      f,
      y,
      t,
      g;
    return (
      (g = {
        next: verb(0),
        throw: verb(1),
        return: verb(2),
      }),
      typeof Symbol === "function" &&
        (g[Symbol.iterator] = function () {
          return this;
        }),
      g
    );

    function verb(n) {
      return function (v) {
        return step([n, v]);
      };
    }

    function step(op) {
      if (f) throw new TypeError("Generator is already executing.");

      while (_)
        try {
          if (
            ((f = 1),
            y &&
              (t =
                op[0] & 2
                  ? y["return"]
                  : op[0]
                  ? y["throw"] || ((t = y["return"]) && t.call(y), 0)
                  : y.next) &&
              !(t = t.call(y, op[1])).done)
          )
            return t;
          if (((y = 0), t)) op = [op[0] & 2, t.value];

          switch (op[0]) {
            case 0:
            case 1:
              t = op;
              break;

            case 4:
              _.label++;
              return {
                value: op[1],
                done: false,
              };

            case 5:
              _.label++;
              y = op[1];
              op = [0];
              continue;

            case 7:
              op = _.ops.pop();

              _.trys.pop();

              continue;

            default:
              if (
                !((t = _.trys), (t = t.length > 0 && t[t.length - 1])) &&
                (op[0] === 6 || op[0] === 2)
              ) {
                _ = 0;
                continue;
              }

              if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
                _.label = op[1];
                break;
              }

              if (op[0] === 6 && _.label < t[1]) {
                _.label = t[1];
                t = op;
                break;
              }

              if (t && _.label < t[2]) {
                _.label = t[2];

                _.ops.push(op);

                break;
              }

              if (t[2]) _.ops.pop();

              _.trys.pop();

              continue;
          }

          op = body.call(thisArg, _);
        } catch (e) {
          op = [6, e];
          y = 0;
        } finally {
          f = t = 0;
        }

      if (op[0] & 5) throw op[1];
      return {
        value: op[0] ? op[1] : void 0,
        done: true,
      };
    }
  }

  var onlineRules = [
      "https://code.gitlink.org.cn/damengzhu/banad/raw/branch/main/jiekouAD.txt",
      "https://code.gitlink.org.cn/damengzhu/abpmerge/raw/branch/main/abpmerge.txt",
    ],
    defaultRules =
      '\n! \u6CA1\u6709\u4E24\u4E2A # \u7684\u884C\u548C \u5F00\u5934\u4E3A ! \u7684\u884C\u4F1A\u5FFD\u7565\n! baidu.com##.ec_wise_ad\n!\n! :remove() \u4F1A\u7528 js \u79FB\u9664\u5143\u7D20\uFF0C:remove() \u5FC5\u987B\u653E\u5728\u884C\u5C3E\n! baidu.com###ad:remove()\n!\n! \u7531\u4E8E\u8BED\u6CD5\u9650\u5236\uFF0C\u5185\u7F6E\u89C4\u5219\u4E2D\n! \u4E00\u4E2A\u53CD\u659C\u6760\u9700\u8981\u6539\u6210\u4E24\u4E2A\uFF0C\u50CF\u8FD9\u6837 \\\n!\n! \u811A\u672C\u4F1A\u9996\u5148\u5C1D\u8BD5\u4ECE\u4E0A\u9762\u7684\u5730\u5740\u6570\u7EC4\u83B7\u53D6\u89C4\u5219\n! \u83B7\u53D6\u5230\u7684\u89C4\u5219\u5C06\u4F1A\u4E0E\u5185\u7F6E\u89C4\u5219\u5408\u5E76\n! \u6240\u6709\u89C4\u5219\u83B7\u53D6\u5B8C\u6BD5\u4EE5\u540E\u624D\u4F1A\u5E94\u7528\u89C4\u5219\n!\n! \u82E5\u8981\u4FEE\u6539\u5730\u5740\uFF0C\u8BF7\u6CE8\u610F\u540C\u6B65\u4FEE\u6539\u5934\u90E8\u7684 @connect \u7684\u57DF\u540D\n!2.3.1\nvercel.app#?#blockquote:has(.mymoney)\nvercel.app#?#blockquote:-abp-has(.myhoney)\nvercel.app#?#blockquote[-ext-has=".mytony"]\n!2.3.2\nvercel.app#?#blockquote:has-text(\u70E6\u607C)\nvercel.app#?#blockquote:has-text(/\u533A\u5206\\d/)\nvercel.app#?#blockquote:contains(\u6ED1\u5757)\nvercel.app#?#blockquote:-abp-contains(\u7EA2\u65E5)\nvercel.app#?#blockquote[-ext-contains="\u5A92\u4F53"]\n!2.3.3\nvercel.app#?#blockquote:matches-css(background-color: rgb\\(135, 206, 235\\))\nvercel.app#?#blockquote:matches-css(background-color: rgb\\(200, 206, 214\\))\nvercel.app#?#blockquote[-ext-matches-css="background-color: rgb\\(240, 255, 240\\)"]\nvercel.app#?#blockquote:matches-css(background-color: /^rgb\\(255,/)\n!2.3.4\nvercel.app#?#blockquote:matches-css-before(content: \u6211\u662F\u5E7F\u544A\u554A)\nvercel.app#?#blockquote[-ext-matches-css-before="content: \u6211\u662F\u5E7F\u544A\u5462"]\n!2.3.5\nvercel.app#?#blockquote:matches-css-after(content: \u6211\u662F\u5E7F\u544A\u54DF)\nvercel.app#?#blockquote[-ext-matches-css-after="content: \u6211\u662F\u5E7F\u544A\u54E6"]\n!2.3.6\nvercel.app#?#[type=range]:matches-attr("disabled")\nvercel.app#?#[type=range]:matches-attr("min"="5")\nvercel.app#?#[type=range]:matches-attr("max"="/^3/")\n!2.3.9\nvercel.app#?#[src$="up.gif"]:nth-ancestor(2)\n!2.3.10\nvercel.app#?#[src$="up2.gif"]:upward(2)\nvercel.app#?#p > em:upward(.box)\n!2.3.12\nvercel.app#?##close:xpath(../../*[1])\n!2.3.13\nvercel.app#?##remo:remove()\n!2.3.15\nvercel.app#?##not > blockquote:not(:has(.ok))\nvercel.app#?##abpnot > blockquote:not(:-abp-has(.ok))\n!2.3.16\nvercel.app#?##ifnot > blockquote:if-not(.ok)\n!2.2.4\nvercel.app#?#blockquote:has(.yes)\nvercel.app#@?#blockquote:has(.yes)\n!2.2.10\nvercel.app#$##turq { color: turquoise !important }\n!2.2.10@\nvercel.app#$##seag { color: seagreen !important }\nvercel.app#@$##seag { color: seagreen !important }\n!2.2.11\nvercel.app#$?#span:contains(\u771F\u7684\u662F) { display: none!important; }\n!2.2.11@\nvercel.app#$?#span:contains(\u771F\u4E0D\u662F) { display: none!important; }\nvercel.app#@$?#span:contains(\u771F\u4E0D\u662F) { display: none!important; }\n';
  const id = "placeholder";

  function isObj(o) {
    return (
      typeof o == "object" &&
      (o === null || o === void 0 ? void 0 : o.toString()) === "[object Object]"
    );
  }

  function runNeed(condition, fn, option, ...args) {
    let ok = false,
      sleep = (time) => {
        return new Promise((r) => setTimeout(r, time));
      },
      defaultOption = {
        count: 20,
        delay: 200,
        failFn: () => null,
      };

    if (isObj(option)) Object.assign(defaultOption, option);
    new Promise(async (resolve, reject) => {
      for (let c = 0; !ok && c < defaultOption.count; c++) {
        await sleep(defaultOption.delay);
        ok = condition.call(null, c + 1);
      }

      ok ? resolve() : reject();
    }).then(fn.bind(null, ...args), defaultOption.failFn);
  }

  `BEXT_LAST_CHECK_KEY_${id}`;

  function getName(path) {
    var reer = /\/([^\/]+)$/.exec(path);
    return reer ? reer[1] : null;
  }

  function getEtag(header) {
    var reer = /etag: \"(\w+)\"/.exec(header);
    return reer ? reer[1] : null;
  }

  function getDay(date) {
    var reer = /\/(\d{1,2}) /.exec(date);
    return reer ? parseInt(reer[1]) : 0;
  }

  function makeRuleBox() {
    return {
      black: [],
      white: [],
      apply: "",
    };
  }

  function domainChecker(domains) {
    var results = [],
      hasTLD = /\.+?[\w-]+$/,
      urlSuffix = hasTLD.exec(location.hostname);
    var invert = false,
      result = false,
      mostMatch = {
        long: 0,
        result: undefined,
      };
    domains.forEach(function (domain) {
      if (domain.endsWith(".*") && Array.isArray(urlSuffix)) {
        domain = domain.replace(".*", urlSuffix[0]);
      }

      if (domain.startsWith("~")) {
        invert = true;
        domain = domain.slice(1);
      } else invert = false;

      result = location.hostname.endsWith(domain);
      results.push(result !== invert);

      if (result) {
        if (domain.length > mostMatch.long) {
          mostMatch = {
            long: domain.length,
            result: result !== invert,
          };
        }
      }
    });
    return mostMatch.long > 0 ? mostMatch.result : results.includes(true);
  }

  function ruleChecker(matches) {
    var index = matches.findIndex(function (i) {
      return i !== null;
    });

    if (
      index >= 0 &&
      (!matches[index][1] || domainChecker(matches[index][1].split(",")))
    ) {
      return [index % 2 == 0, Math.floor(index / 2), matches[index].pop()];
    }
  }

  function ruleSpliter(rule) {
    var result = ruleChecker([
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?##([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#@#([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#\?#([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#@\?#([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#\$#([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#@\$#([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#\$\?#([^\s^+].*)/
      ),
      rule.match(
        /^(~?[\w-]+\.([\w-]+|\*)(,~?[\w-]+\.([\w-]+|\*))*)?#@\$\?#([^\s^+].*)/
      ),
    ]);

    if (result && result[2]) {
      return {
        black: result[0],
        type: result[1],
        sel: result[2],
      };
    }
  }

  var selectors = makeRuleBox(),
    extSelectors = makeRuleBox(),
    styles = makeRuleBox(),
    extStyles = makeRuleBox(),
    values = {
      get black() {
        var v = tm.GM_getValue("ajs_disabled_domains", "");
        return typeof v == "string" ? v : "";
      },

      set black(v) {
        v === null
          ? tm.GM_deleteValue("ajs_disabled_domains")
          : tm.GM_setValue("ajs_disabled_domains", v);
      },

      get rules() {
        var v = tm.GM_getValue("ajs_saved_abprules", "{}");
        return typeof v == "string" ? JSON.parse(v) : {};
      },

      set rules(v) {
        v === null
          ? tm.GM_deleteValue("ajs_saved_abprules")
          : tm.GM_setValue("ajs_saved_abprules", JSON.stringify(v));
      },

      get time() {
        var v = tm.GM_getValue("ajs_rules_ver", "0/0/0 0:0:0");
        return typeof v == "string" ? v : "0/0/0 0:0:0";
      },

      set time(v) {
        v === null
          ? tm.GM_deleteValue("ajs_rules_ver")
          : tm.GM_setValue("ajs_rules_ver", v);
      },

      get etags() {
        var v = tm.GM_getValue("ajs_rules_etags", "{}");
        return typeof v == "string" ? JSON.parse(v) : {};
      },

      set etags(v) {
        v === null
          ? tm.GM_deleteValue("ajs_rules_etags")
          : tm.GM_setValue("ajs_rules_etags", JSON.stringify(v));
      },
    },
    data = {
      disabled: false,
      updating: false,
      receivedRules: "",
      allRules: "",
      genericStyle: document.createElement("style"),
      presetCss:
        " {display: none !important;width: 0 !important;height: 0 !important;} ",
      supportedCount: 0,
      appliedCount: 0,
    },
    menus = {
      disable: {
        id: undefined,

        get text() {
          return data.disabled ? "在此网站启用拦截" : "在此网站禁用拦截";
        },
      },
      update: {
        id: undefined,

        get text() {
          var time = values.time;
          return data.updating
            ? "正在更新..."
            : "\u70B9\u51FB\u66F4\u65B0: ".concat(
                time.slice(0, 1) === "0" ? "未知时间" : time
              );
        },
      },
      count: {
        id: undefined,

        get text() {
          return "\u70B9\u51FB\u6E05\u7A7A: "
            .concat(data.appliedCount, " / ")
            .concat(data.supportedCount, " / ")
            .concat(data.allRules.split("\n").length);
        },
      },
    };

  function gmMenu(name, cb) {
    if (
      typeof tm.GM_registerMenuCommand !== "function" ||
      typeof tm.GM_unregisterMenuCommand !== "function" ||
      window.self !== window.top
    )
      return false;
    var id = menus[name].id;

    if (typeof id !== "undefined") {
      menus[name].id = tm.GM_unregisterMenuCommand(id);
    }

    if (typeof cb == "function") {
      menus[name].id = tm.GM_registerMenuCommand(menus[name].text, cb);
    }

    return typeof menus[name].id !== "undefined";
  }

  function promiseXhr(details) {
    return new Promise(function (resolve, reject) {
      tm.GM_xmlhttpRequest(
        __assign(
          {
            onload: function (e) {
              resolve(e);
            },
            onabort: reject.bind(null),
            onerror: reject.bind(null),
            ontimeout: reject.bind(null),
          },
          details
        )
      );
    });
  }

  function storeRule(name, resp) {
    var savedRules = values.rules,
      savedEtags = values.etags;

    if (resp.responseHeaders) {
      var etag = getEtag(resp.responseHeaders);

      if (etag) {
        savedEtags[name] = etag;
        values.etags = savedEtags;
      }
    }

    if (resp.responseText) {
      savedRules[name] = resp.responseText;
      values.rules = savedRules;
    }
  }

  function fetchRule(url) {
    var _this = this;

    var _a;

    var name =
      (_a = getName(url)) !== null && _a !== void 0
        ? _a
        : "".concat(url.length, ".").concat(url.slice(-5));
    return new Promise(function (resolve, reject) {
      return __awaiter(_this, void 0, void 0, function () {
        var headResp, etag, savedEtags, _a, _b;

        return __generator(this, function (_c) {
          switch (_c.label) {
            case 0:
              if (!name) reject();
              return [
                4,
                promiseXhr({
                  method: "HEAD",
                  responseType: "text",
                  url: url,
                }),
              ];

            case 1:
              headResp = _c.sent();
              if (!headResp.responseText) return [3, 2];
              storeRule(name, headResp);
              resolve();
              return [3, 5];

            case 2:
              if (!headResp.responseHeaders) return [3, 5];
              (etag = getEtag(headResp.responseHeaders)),
                (savedEtags = values.etags);
              if (!(etag !== savedEtags[name])) return [3, 4];
              _a = storeRule;
              _b = [name];
              return [
                4,
                promiseXhr({
                  method: "GET",
                  responseType: "text",
                  url: url,
                }),
              ];

            case 3:
              _a.apply(void 0, _b.concat([_c.sent()]));

              resolve();
              return [3, 5];

            case 4:
              reject();
              _c.label = 5;

            case 5:
              return [2];
          }
        });
      });
    });
  }

  function fetchRules() {
    return __awaiter(this, void 0, void 0, function () {
      var pArray;
      return __generator(this, function (_a) {
        switch (_a.label) {
          case 0:
            pArray = [];
            data.updating = true;
            gmMenu("update", fetchRules);
            onlineRules.forEach(function (url) {
              pArray.push(fetchRule(url));
            });
            return [4, Promise.allSettled(pArray)];

          case 1:
            _a.sent();

            values.time = new Date().toLocaleString("zh-CN");
            initRules();
            return [2];
        }
      });
    });
  }

  function performUpdate(force) {
    if (force) {
      return fetchRules();
    } else {
      return getDay(values.time) !== new Date().getDate()
        ? fetchRules()
        : Promise.resolve();
    }
  }

  function switchDisabledStat() {
    var disaList = values.black.split(","),
      disaResult = disaList.includes(location.hostname);
    data.disabled = !disaResult;

    if (data.disabled) {
      disaList.push(location.hostname);
    } else {
      disaList.splice(disaList.indexOf(location.hostname), 1);
    }

    values.black = disaList.join(",");
    gmMenu("disable", switchDisabledStat);
  }

  function checkDisableStat() {
    var disaResult = values.black.split(",").includes(location.hostname);
    data.disabled = disaResult;
    gmMenu("disable", switchDisabledStat);
    return disaResult;
  }

  function initRules() {
    var abpRules = values.rules,
      abpKeys = Object.keys(abpRules);
    abpKeys.forEach(function (name) {
      data.receivedRules += "\n" + abpRules[name] + "\n";
    });
    data.allRules = defaultRules + data.receivedRules;

    if (abpKeys.length !== 0) {
      data.updating = false;
      gmMenu("update", fetchRules);
    }

    return data.receivedRules.length;
  }

  function styleApply() {
    var css =
        styles.apply +
        (selectors.apply.length > 0 ? selectors.apply + data.presetCss : ""),
      ecss =
        extStyles.apply +
        (extSelectors.apply.length > 0
          ? extSelectors.apply + data.presetCss
          : "");

    if (css.length > 0) {
      if (typeof tm.GM_addStyle == "function") {
        tm.GM_addStyle(css);
      } else {
        runNeed(
          function () {
            return !!document.documentElement;
          },
          function () {
            data.genericStyle.textContent = css;
            document.documentElement.appendChild(data.genericStyle);
          }
        );
      }
    }

    if (ecss.length > 0)
      new ExtendedCss__default.default({
        styleSheet: ecss,
      }).apply();
  }

  function parseRules() {
    var _this = this;

    [selectors, extSelectors].forEach(function (obj) {
      obj.black
        .filter(function (v) {
          return !obj.white.includes(v);
        })
        .forEach(function (sel) {
          obj.apply += "".concat(obj.apply.length == 0 ? "" : ",").concat(sel);
          data.appliedCount++;
        });
    });
    [styles, extStyles].forEach(function (obj) {
      obj.black
        .filter(function (v) {
          return !obj.white.includes(v);
        })
        .forEach(function (sel) {
          obj.apply += " ".concat(sel);
          data.appliedCount++;
        });
    });
    gmMenu("count", function () {
      if (confirm("是否清空存储规则 ?")) {
        values.rules = {};
        values.time = "0/0/0 0:0:0";
        values.etags = {};
        gmMenu("update", performUpdate.bind(_this, true));
      }
    });
    styleApply();
  }

  function main() {
    return __awaiter(this, void 0, void 0, function () {
      return __generator(this, function (_a) {
        switch (_a.label) {
          case 0:
            if (checkDisableStat()) return [2];
            if (!(initRules() === 0)) return [3, 2];
            return [4, performUpdate(true)];

          case 1:
            _a.sent();

            _a.label = 2;

          case 2:
            data.allRules.split("\n").forEach(function (rule) {
              var ruleObj = ruleSpliter(rule);
              var arr = "";

              if (typeof ruleObj !== "undefined") {
                arr = ruleObj.black ? "black" : "white";

                switch (ruleObj.type) {
                  case 0:
                    selectors[arr].push(ruleObj.sel);
                    break;

                  case 1:
                    extSelectors[arr].push(ruleObj.sel);
                    break;

                  case 2:
                    styles[arr].push(ruleObj.sel);
                    break;

                  case 3:
                    extStyles[arr].push(ruleObj.sel);
                    break;
                }

                data.supportedCount++;
              }
            });
            parseRules();
            performUpdate(false);
            return [2];
        }
      });
    });
  }

  main();
})(self, ExtendedCss);