DMHY Bangumi Index

Let DMHY header index back!

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name       DMHY Bangumi Index
// @name:zh-TW 動漫花園新番索引
// @description       Let DMHY header index back!
// @description:zh-TW 把動漫花園上方的索引弄回來
// @namespace https://github.com/FlandreDaisuki
// @author    FlandreDaisuki
// @match     https://dmhy.org/
// @match     https://dmhy.org/topics/*
// @match     https://share.dmhy.org/
// @match     https://share.dmhy.org/topics/*
// @require   https://unpkg.com/[email protected]/dist/vue.global.prod.js
// @require   https://unpkg.com/[email protected]/dist/vue-router.global.prod.js
// @require   https://unpkg.com/[email protected]/dist/js-yaml.min.js
// @require   https://unpkg.com/[email protected]/libs/lz-string.min.js
// @connect   flandredaisuki.github.io
// @license   MIT
// @noframes
// @version   1.5.0
// @grant     GM_xmlhttpRequest
// @grant     unsafeWindow
// ==/UserScript==

(function (vue, lzString, yaml) {
  'use strict';

  const HOUR_IN_MS = 60 * 60 * 1000;

  const WEEKDAY_STR = '日一二三四五六';

  const BASE_URI = 'https://flandredaisuki.github.io/DMHY-Bangumi-Index';
  // const BASE_URI = 'https://flandredaisuki.github.io/DMHY-Bangumi-Index/history/2019-10';

  const $ = (s) => document.querySelector(s);
  const $$ = (s) => Array.from(document.querySelectorAll(s));
  const createKeywordLink = (keyword) => `/topics/list?keyword=${keyword}`;
  const transformWeekday = (weekdayStr) => {
    switch (weekdayStr) {
    case '日':
      return '星期日(日)';
    case '一':
      return '星期一(月)';
    case '二':
      return '星期二(火)';
    case '三':
      return '星期三(水)';
    case '四':
      return '星期四(木)';
    case '五':
      return '星期五(金)';
    case '六':
      return '星期六(土)';
    }
  };

  const STORAGE_KEY$2 = 'DMHY-Bangumi-Index::favorite';
  const favorites = vue.ref([]);

  const load$1 = () => {
    const fav = localStorage.getItem(STORAGE_KEY$2);
    if (!fav) { favorites.value = []; }
    try {
      favorites.value = JSON.parse(lzString.decompressFromBase64(fav));
    }
    catch {
      favorites.value = [];
    }
  };

  const save$1 = () => {
    localStorage.setItem(
      STORAGE_KEY$2,
      lzString.compressToBase64(JSON.stringify(favorites.value)),
    );
  };

  const find = (title) => {
    const foundIndex = favorites.value.findIndex((fav) => fav.title === title);
    return {
      found: (foundIndex >= 0) ? favorites.value[foundIndex] : null,
      foundIndex,
    };
  };

  /** @type {(title: string) => void */
  const add$1 = (title) => {
    const keyword = new URL(location).searchParams.get('keyword');
    favorites.value.push({ title, keyword });
    save$1();
  };

  /** @type {(title: string) => void */
  const remove = (title) => {
    const { found, foundIndex } = find(title);
    if (found) {
      favorites.value.splice(foundIndex, 1);
    }
    save$1();
  };

  const fetcher = async (url, options = {}) => {
    const defaultOptions = {
      method: 'GET',
      headers: {
        'Cache-Control': 'no-cache',
      },
    };
    const opt = Object.assign({}, defaultOptions, options);
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        ...opt,
        url,
        onload: (res) => {
          resolve(res.responseText);
        },
        onerror: (err) => {
          console.error('DMHY-Bangumi-Index::req-err', err);
          reject(err);
        },
      });
    });
  };

  const YAMLToWeeklyBangumiPayload = (data, isnew) => {
    const weeklyBangumiPayload = {};

    for (const weekdayStr of WEEKDAY_STR) {
      weeklyBangumiPayload[weekdayStr] = [];
      for (const [title, keyword] of Object.entries(data[weekdayStr])) {
        weeklyBangumiPayload[weekdayStr].push({
          title,
          keyword,
          isnew,
        });
      }
    }
    return weeklyBangumiPayload;
  };

  const downloadBangumi = async (newold) => {
    const txt = await fetcher(
      `${BASE_URI}/${newold}.yaml`,
    );

    const data = yaml.load(txt);
    return YAMLToWeeklyBangumiPayload(data, newold === 'new');
  };

  const weeklyBangumi = vue.ref({});

  /**
   * wb := weeklyBangumi
   * {
   *   日: [
   *     {title, keyword, isnew},
   *     {title, keyword, isnew},...
   *   ],
   *   一: [],
   *   ...
   * }
   * → xwb := [...W].join('\0'); # ordered by WEEKDAY_STR #
   * → W := [...B].join('\1')
   * → B := [T, K, N].join('\2')
   * → N := true: 1; false: 0;
   */
  const compressedEncode = (wb) => {
    return [...WEEKDAY_STR]
      .map((w) =>
        wb[w]
          .map((b) => [b.title, b.keyword, Number(b.isnew)].join('\x02'))
          .join('\x01'),
      )
      .join('\x00');
  };

  const compressedDecode = (xwb) => {
    return xwb
      .split('\x00')
      .map((xw, i) => {
        return {
          [WEEKDAY_STR[i]]: xw.split('\x01')
            .filter(Boolean)
            .map((b) => {
              const [title, keyword, isnew] = b.split('\x02');
              return {
                title,
                keyword,
                isnew: isnew === '1',
              };
            }),
        };
      })
      .reduce((c, v) => {
        return Object.assign(c, v);
      }, {});
  };

  const encodeWeeklyBangumiToStorage = (wb) => {
    return lzString.compressToBase64(compressedEncode(wb));
  };

  const decodeWeeklyBangumiFromStorage = (xwb) => {
    return compressedDecode(lzString.decompressFromBase64(xwb));
  };

  const STORAGE_KEY$1 = 'DMHY-Bangumi-Index::weekly-bangumi';
  const CACHE_KEY = 'DMHY-Bangumi-Index::weekly-bangumi-cache-t';

  const save = () => {
    localStorage.setItem(
      STORAGE_KEY$1,
      encodeWeeklyBangumiToStorage(weeklyBangumi.value),
    );
  };

  const load = () => {
    const xwb = localStorage.getItem(STORAGE_KEY$1);
    if (xwb) {
      weeklyBangumi.value = decodeWeeklyBangumiFromStorage(xwb) || {};
    }
    else {
      weeklyBangumi.value = {};
    }
  };

  const add = (payload) => {
    for (const weekdayStr of WEEKDAY_STR) {
      if (!weeklyBangumi.value[weekdayStr]) {
        weeklyBangumi.value[weekdayStr] = [];
      }
      weeklyBangumi.value[weekdayStr].push(...payload[weekdayStr]);
    }
  };

  const loadRemote = async () => {
    const [oldPayload, newPayload] = await Promise.all([
      downloadBangumi('old'),
      downloadBangumi('new'),
    ]);
    add(oldPayload);
    add(newPayload);
    localStorage.setItem(CACHE_KEY, Date.now());
    save();
  };

  const loadWithCache = async () => {
    const cacheTime = Number(localStorage.getItem(CACHE_KEY)) || 0;
    const maxCacheTime = 12 * HOUR_IN_MS;
    if (Date.now() - cacheTime > maxCacheTime) {
      await loadRemote();
    }
    else {
      load();
    }
  };

  const cleanCacheTime = () => {
    localStorage.setItem(CACHE_KEY, 0);
  };

  const STORAGE_KEY = 'DMHY-Bangumi-Index::expansion';

  const expansion = vue.ref(Boolean(JSON.parse(localStorage.getItem(STORAGE_KEY) ?? 'false')));

  const get = () => expansion.value;

  const set = (v) => {
    expansion.value = Boolean(v);
    localStorage.setItem(STORAGE_KEY, expansion.value);
  };

  var script$2 = {
    setup() {
      const date = new Date();
      const todayWeekday = date.getDay();
      const toggleExpansion = () => set(!get());

      const expansionStr = vue.computed(() => get() ? '收起' : '展開');
      const todayStr = vue.computed(() => {
        const longWeekdayStr = new Intl.DateTimeFormat('zh', {
          weekday: 'long',
        }).format(date);

        const dateStr = new Intl.DateTimeFormat('zh', {
          day: 'numeric',
          month: 'long',
          year: 'numeric',
        }).format(date);

        return `西元 ${dateStr} ${longWeekdayStr}`;
      });

      const TODAY_SENSITIVE_WEEKDAY_STR = WEEKDAY_STR.repeat(3)
        .slice(todayWeekday + 5, todayWeekday + 12);

      const orderedWeeklyBangumi = vue.computed(() => {
        const weeklyBangumiMap = [...TODAY_SENSITIVE_WEEKDAY_STR].reduce(
          (collection, weekdayStr) => {
            return collection.set(weekdayStr, weeklyBangumi.value[weekdayStr]);
          },
          new Map(),
        );
        return [...weeklyBangumiMap.entries()];
      });

      const forceUpdateWeekly = async () => {
        cleanCacheTime();
        location.assign('https://share.dmhy.org/');
      };

      const isIndexShow = (index) => get() ? true : index < 4;

      return {
        todayStr,
        expansionStr,
        orderedWeeklyBangumi,

        toggleExpansion,
        createKeywordLink,
        transformWeekday,
        isIndexShow,
        forceUpdateWeekly,
      };
    },
  };

  const _hoisted_1$2 = { class: "weekly-table" };
  const _hoisted_2$1 = { class: "weekly-weekday-str" };
  const _hoisted_3$1 = ["href"];

  function render$2(_ctx, _cache, $props, $setup, $data, $options) {
    return (vue.openBlock(), vue.createElementBlock("div", null, [
      vue.createElementVNode("header", null, [
        _cache[2] || (_cache[2] = vue.createElementVNode("span", null, "新番資源索引", -1 /* HOISTED */)),
        vue.createElementVNode("span", null, vue.toDisplayString($setup.todayStr), 1 /* TEXT */),
        vue.createElementVNode("span", null, [
          vue.createElementVNode("a", {
            href: "javascript:;",
            role: "button",
            onClick: _cache[0] || (_cache[0] = (...args) => ($setup.toggleExpansion && $setup.toggleExpansion(...args)))
          }, vue.toDisplayString($setup.expansionStr), 1 /* TEXT */)
        ]),
        vue.createElementVNode("span", null, [
          vue.createElementVNode("a", {
            href: "javascript:;",
            role: "button",
            onClick: _cache[1] || (_cache[1] = (...args) => ($setup.forceUpdateWeekly && $setup.forceUpdateWeekly(...args)))
          }, "強制更新")
        ])
      ]),
      vue.createElementVNode("table", _hoisted_1$2, [
        (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($setup.orderedWeeklyBangumi, ([weekday, dayBangumiList], index) => {
          return vue.withDirectives((vue.openBlock(), vue.createElementBlock("tr", {
            key: weekday,
            class: vue.normalizeClass(["weekly-tr", { 'weekly-tr-today': index === 2 }])
          }, [
            vue.createElementVNode("td", _hoisted_2$1, vue.toDisplayString($setup.transformWeekday(weekday)), 1 /* TEXT */),
            vue.createElementVNode("td", null, [
              (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(dayBangumiList, (bangumi) => {
                return (vue.openBlock(), vue.createElementBlock("a", {
                  key: bangumi.title,
                  class: vue.normalizeClass(["bangumi", { 'bangumi-old': !bangumi.isnew }]),
                  href: $setup.createKeywordLink(bangumi.keyword)
                }, vue.toDisplayString(bangumi.title), 11 /* TEXT, CLASS, PROPS */, _hoisted_3$1))
              }), 128 /* KEYED_FRAGMENT */))
            ])
          ], 2 /* CLASS */)), [
            [vue.vShow, $setup.isIndexShow(index)]
          ])
        }), 128 /* KEYED_FRAGMENT */))
      ])
    ]))
  }

  function styleInject(css, ref) {
    if ( ref === void 0 ) ref = {};
    var insertAt = ref.insertAt;

    if (!css || typeof document === 'undefined') { return; }

    var head = document.head || document.getElementsByTagName('head')[0];
    var style = document.createElement('style');
    style.type = 'text/css';

    if (insertAt === 'top') {
      if (head.firstChild) {
        head.insertBefore(style, head.firstChild);
      } else {
        head.appendChild(style);
      }
    } else {
      head.appendChild(style);
    }

    if (style.styleSheet) {
      style.styleSheet.cssText = css;
    } else {
      style.appendChild(document.createTextNode(css));
    }
  }

  var css_248z$2 = "\na[data-v-5ce41dcd] {\n  color: #247;\n  text-decoration: none;\n}\nheader[data-v-5ce41dcd] {\n  color: #fff;\n  background-color: #247;\n  padding: 5px;\n  display: flex;\n  font-size: 0.8rem;\n}\nheader > span[data-v-5ce41dcd]:nth-of-type(n + 2)::before {\n  content: \"::\";\n  padding: 0 8px;\n}\nheader > span > a[data-v-5ce41dcd] {\n  color: #fff;\n}\n.weekly-table[data-v-5ce41dcd] {\n  border-collapse: collapse;\n  width: 100%;\n}\n.weekly-tr[data-v-5ce41dcd] {\n  display: flex;\n  align-items: center;\n  border: 2px solid white;\n  background: white;\n}\n.weekly-tr.weekly-tr-today[data-v-5ce41dcd] {\n  background-color: #ff9;\n}\n.weekly-weekday-str[data-v-5ce41dcd] {\n  padding: 3px 15px;\n  margin-right: 3px;\n  background-color: #7e99be;\n  color: white;\n  font-weight: bolder;\n}\n.weekly-weekday-str + td[data-v-5ce41dcd] {\n  display: flex;\n  flex-flow: row wrap;\n  flex: 1;\n}\n.bangumi[data-v-5ce41dcd] {\n  border: 1px solid #ffa500;\n  padding: 2px;\n  margin: 1px 3px;\n  display: inline-flex;\n  align-items: center;\n}\n.bangumi-old[data-v-5ce41dcd] {\n  border: 1px solid #002fff;\n}\n";
  styleInject(css_248z$2);

  script$2.render = render$2;
  script$2.__scopeId = "data-v-5ce41dcd";

  var script$1 = {
    setup() {
      const userInputStr = vue.ref('');
      const validityMsg = vue.ref('');
      const title = vue.computed(() => userInputStr.value.trim());
      const titleInputEl = vue.ref();

      const setValidity = (msg) => {
        validityMsg.value = msg;
        titleInputEl.value.setCustomValidity(msg);
      };
      const onClickAdd = () => {
        if (!title.value) { return setValidity('名稱欄為空'); }
        if (find(title.value).found) { return setValidity('書籤名稱已存在'); }
        add$1(title.value);
      };
      const onClickRemove = () => {
        if (!title.value) { return setValidity('名稱欄為空'); }
        if (!find(title.value).found) { return setValidity('書籤名稱不存在'); }
        remove(title.value);
      };

      return {
        userInputStr,
        favorites,
        validityMsg,
        titleInputEl,
        createKeywordLink,
        setValidity,
        onClickAdd,
        onClickRemove,
      };
    },
  };

  const _hoisted_1$1 = { class: "favorite-area" };
  const _hoisted_2 = { class: "favorite-pool" };
  const _hoisted_3 = ["href"];
  const _hoisted_4 = { class: "input-area" };
  const _hoisted_5 = { class: "tooltip" };

  function render$1(_ctx, _cache, $props, $setup, $data, $options) {
    return (vue.openBlock(), vue.createElementBlock("div", null, [
      _cache[5] || (_cache[5] = vue.createElementVNode("header", null, [
        vue.createElementVNode("span", null, "書籤索引"),
        vue.createElementVNode("span", null, "將當前的搜索加入書籤,並自訂名稱")
      ], -1 /* HOISTED */)),
      vue.createElementVNode("div", _hoisted_1$1, [
        vue.createElementVNode("div", _hoisted_2, [
          (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($setup.favorites, (bangumi) => {
            return (vue.openBlock(), vue.createElementBlock("a", {
              key: bangumi.title,
              href: $setup.createKeywordLink(bangumi.keyword),
              role: "button",
              class: "bangumi"
            }, vue.toDisplayString(bangumi.title), 9 /* TEXT, PROPS */, _hoisted_3))
          }), 128 /* KEYED_FRAGMENT */))
        ]),
        vue.createElementVNode("div", _hoisted_4, [
          vue.withDirectives(vue.createElementVNode("input", {
            ref: "titleInputEl",
            "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (($setup.userInputStr) = $event)),
            type: "text",
            class: "user-title-input",
            placeholder: "為目前網址取名",
            onInput: _cache[1] || (_cache[1] = $event => ($setup.setValidity(''))),
            onFocus: _cache[2] || (_cache[2] = $event => ($setup.setValidity('')))
          }, null, 544 /* NEED_HYDRATION, NEED_PATCH */), [
            [vue.vModelText, $setup.userInputStr]
          ]),
          vue.createElementVNode("span", _hoisted_5, vue.toDisplayString($setup.validityMsg), 1 /* TEXT */),
          vue.createElementVNode("button", {
            class: "add-btn",
            onClick: _cache[3] || (_cache[3] = (...args) => ($setup.onClickAdd && $setup.onClickAdd(...args)))
          }, " 加入 "),
          vue.createElementVNode("button", {
            class: "del-btn",
            onClick: _cache[4] || (_cache[4] = (...args) => ($setup.onClickRemove && $setup.onClickRemove(...args)))
          }, " 刪除 ")
        ])
      ])
    ]))
  }

  var css_248z$1 = "\na[data-v-65175dc4] {\n  color: #247;\n  text-decoration: none;\n}\nheader[data-v-65175dc4] {\n  color: #fff;\n  background-color: #247;\n  padding: 5px;\n  display: flex;\n  font-size: 0.8rem;\n}\nheader > span[data-v-65175dc4]:nth-of-type(n + 2)::before {\n  content: '::';\n  padding: 0 8px;\n}\nheader > span > a[data-v-65175dc4] {\n  color: #fff;\n}\n.favorite-area[data-v-65175dc4] {\n  background-color: #fff;\n}\n.favorite-pool[data-v-65175dc4] {\n  padding: 10px;\n  min-height: 14px;\n  display: flex;\n}\n.bangumi[data-v-65175dc4] {\n  border: 1px solid #ffa500;\n  padding: 2px;\n  margin: 1px 3px;\n  display: inline-flex;\n  align-items: center;\n}\n.input-area[data-v-65175dc4] {\n  display: flex;\n  justify-content: center;\n  padding: 4px;\n  border-top: 1px dotted #247;\n}\n.input-area[data-v-65175dc4] > * {\n  margin: 0 15px;\n}\n.input-area > .user-title-input[data-v-65175dc4] {\n  border: 1px solid #247;\n  padding: 0 7px;\n  border-radius: 5px;\n  font-size: 14px;\n}\n.tooltip[data-v-65175dc4] {\n  position: absolute;\n  background-color: #000;\n  color: white;\n  padding: 5px 10px;\n  border-radius: 5px;\n  transform-origin: bottom center;\n  transform: translateY(-35px);\n  display: none;\n}\n.tooltip[data-v-65175dc4]::after {\n  content: '';\n  width: 0;\n  height: 0;\n  border-left: 5px solid transparent;\n  border-right: 5px solid transparent;\n  border-top: 5px solid black;\n  position: absolute;\n  top: 100%;\n  right: 20%;\n}\n.user-title-input:invalid + .tooltip[data-v-65175dc4] {\n  display: block;\n}\n.input-area > button[data-v-65175dc4] {\n  border: none;\n  border-radius: 5px;\n  padding: 5px 21px;\n  font-size: 14px;\n}\n.input-area > button.add-btn[data-v-65175dc4] {\n  background-color: lightgreen;\n}\n.input-area > button.del-btn[data-v-65175dc4] {\n  background-color: crimson;\n  color: white;\n}\n";
  styleInject(css_248z$1);

  script$1.render = render$1;
  script$1.__scopeId = "data-v-65175dc4";

  const TAB_NAMES = Object.freeze({
    WEEKLY: 'weekly',
    FAVORITE: 'favorite',
  });

  var script = {
    components: {
      PageFavorite: script$1,
      PageWeekly: script$2,
    },
    setup() {
      const tab = vue.ref(TAB_NAMES.WEEKLY);

      vue.onMounted(() => {
        load$1();
        save$1();
        loadWithCache();
      });

      return {
        TAB_NAMES,

        setTab: (tabName) => {
          tab.value = tabName;
        },
        shouldShowWeeklyTab: vue.computed(() => {
          return tab.value === TAB_NAMES.WEEKLY;
        }),
        shouldShowFavoriteTab: vue.computed(() => {
          return tab.value === TAB_NAMES.FAVORITE;
        }),
      };
    },
  };

  const _hoisted_1 = { id: "🌐" };

  function render(_ctx, _cache, $props, $setup, $data, $options) {
    const _component_PageWeekly = vue.resolveComponent("PageWeekly");
    const _component_PageFavorite = vue.resolveComponent("PageFavorite");

    return (vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
      vue.createElementVNode("nav", null, [
        vue.createElementVNode("button", {
          type: "button",
          class: vue.normalizeClass({ 'active-tab': $setup.shouldShowWeeklyTab }),
          onClick: _cache[0] || (_cache[0] = $event => ($setup.setTab($setup.TAB_NAMES.WEEKLY)))
        }, " 新番索引 ", 2 /* CLASS */),
        vue.createElementVNode("button", {
          type: "button",
          class: vue.normalizeClass({ 'active-tab': $setup.shouldShowFavoriteTab }),
          onClick: _cache[1] || (_cache[1] = $event => ($setup.setTab($setup.TAB_NAMES.FAVORITE)))
        }, " 書籤索引 ", 2 /* CLASS */)
      ]),
      vue.withDirectives(vue.createVNode(_component_PageWeekly, null, null, 512 /* NEED_PATCH */), [
        [vue.vShow, $setup.shouldShowWeeklyTab]
      ]),
      vue.withDirectives(vue.createVNode(_component_PageFavorite, null, null, 512 /* NEED_PATCH */), [
        [vue.vShow, $setup.shouldShowFavoriteTab]
      ])
    ]))
  }

  var css_248z = "\n#🌐[data-v-9efce4c4] {\n  margin-top: 20px;\n  font-size: 14px;\n}\nbutton[data-v-9efce4c4] {\n  font-size: 1rem;\n  color: black;\n  text-decoration: none;\n}\nnav > button[data-v-9efce4c4] {\n  display: inline-block;\n  padding: 4px 16px;\n  background: #fff;\n  cursor: pointer;\n  border-width: 1px 1px 0 1px;\n  border-style: solid;\n  border-color: #247;\n  border-radius: 4px 4px 0 0;\n}\nnav > button.active-tab[data-v-9efce4c4] {\n  border-top: 3px solid dodgerblue;\n}\n.page-view[data-v-9efce4c4] {\n  border: 1px solid #247;\n}\n";
  styleInject(css_248z);

  script.render = render;
  script.__scopeId = "data-v-9efce4c4";

  // put constant as front as possible

  // pre-process

  const adSelectors = [
    '.ad',
    '[id="1280_ad"] > a',
    '.main > div:first-child',
  ].join(',');
  for (const adEl of $$(adSelectors)) {
    adEl.remove();
  }

  // entry point

  const app = vue.createApp(script)
    .mount($('#mini_jmd').parentElement);

  unsafeWindow.DMHYBangumiIndex$app = app;

})(Vue, LZString, jsyaml);