DMHY Bangumi Index

Let DMHY header index back!

2019/08/05のページです。最新版はこちら。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==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://share.dmhy.org/*
// @require   https://unpkg.com/[email protected]/dist/vue.min.js
// @require   https://unpkg.com/[email protected]/dist/vuex.min.js
// @require   https://unpkg.com/[email protected]/dist/vue-router.min.js
// @require   https://unpkg.com/[email protected]/dist/js-yaml.min.js
// @require   https://unpkg.com/[email protected]/libs/lz-string.min.js
// @resource  OLD_YAML https://flandredaisuki.github.io/DMHY-Bangumi-Index/old.yaml
// @resource  NEW_YAML https://flandredaisuki.github.io/DMHY-Bangumi-Index/new.yaml
// @license   MIT
// @noframes
// @version   1.0.1
// @grant     GM_getResourceText
// ==/UserScript==

(function (Vue, Vuex, VueRouter, jsyaml, LZString) {
  'use strict';

  Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue;
  Vuex = Vuex && Vuex.hasOwnProperty('default') ? Vuex['default'] : Vuex;
  VueRouter = VueRouter && VueRouter.hasOwnProperty('default') ? VueRouter['default'] : VueRouter;
  jsyaml = jsyaml && jsyaml.hasOwnProperty('default') ? jsyaml['default'] : jsyaml;
  LZString = LZString && LZString.hasOwnProperty('default') ? LZString['default'] : LZString;

  //
  //
  //
  //
  //
  //
  //
  //
  //
  //

  var script = {};

  function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
  /* server only */
  , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
    if (typeof shadowMode !== 'boolean') {
      createInjectorSSR = createInjector;
      createInjector = shadowMode;
      shadowMode = false;
    } // Vue.extend constructor export interop.


    var options = typeof script === 'function' ? script.options : script; // render functions

    if (template && template.render) {
      options.render = template.render;
      options.staticRenderFns = template.staticRenderFns;
      options._compiled = true; // functional template

      if (isFunctionalTemplate) {
        options.functional = true;
      }
    } // scopedId


    if (scopeId) {
      options._scopeId = scopeId;
    }

    var hook;

    if (moduleIdentifier) {
      // server build
      hook = function hook(context) {
        // 2.3 injection
        context = context || // cached call
        this.$vnode && this.$vnode.ssrContext || // stateful
        this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
        // 2.2 with runInNewContext: true

        if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
          context = __VUE_SSR_CONTEXT__;
        } // inject component styles


        if (style) {
          style.call(this, createInjectorSSR(context));
        } // register component module identifier for async chunk inference


        if (context && context._registeredComponents) {
          context._registeredComponents.add(moduleIdentifier);
        }
      }; // used by ssr in case component is cached and beforeCreate
      // never gets called


      options._ssrRegister = hook;
    } else if (style) {
      hook = shadowMode ? function () {
        style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
      } : function (context) {
        style.call(this, createInjector(context));
      };
    }

    if (hook) {
      if (options.functional) {
        // register for functional component in vue file
        var originalRender = options.render;

        options.render = function renderWithStyleInjection(h, context) {
          hook.call(context);
          return originalRender(h, context);
        };
      } else {
        // inject component registration as beforeCreate hook
        var existing = options.beforeCreate;
        options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
      }
    }

    return script;
  }

  var normalizeComponent_1 = normalizeComponent;

  var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase());
  function createInjector(context) {
    return function (id, style) {
      return addStyle(id, style);
    };
  }
  var HEAD;
  var styles = {};

  function addStyle(id, css) {
    var group = isOldIE ? css.media || 'default' : id;
    var style = styles[group] || (styles[group] = {
      ids: new Set(),
      styles: []
    });

    if (!style.ids.has(id)) {
      style.ids.add(id);
      var code = css.source;

      if (css.map) {
        // https://developer.chrome.com/devtools/docs/javascript-debugging
        // this makes source maps inside style tags work properly in Chrome
        code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; // http://stackoverflow.com/a/26603875

        code += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + ' */';
      }

      if (!style.element) {
        style.element = document.createElement('style');
        style.element.type = 'text/css';
        if (css.media) style.element.setAttribute('media', css.media);

        if (HEAD === undefined) {
          HEAD = document.head || document.getElementsByTagName('head')[0];
        }

        HEAD.appendChild(style.element);
      }

      if ('styleSheet' in style.element) {
        style.styles.push(code);
        style.element.styleSheet.cssText = style.styles.filter(Boolean).join('\n');
      } else {
        var index = style.ids.size - 1;
        var textNode = document.createTextNode(code);
        var nodes = style.element.childNodes;
        if (nodes[index]) style.element.removeChild(nodes[index]);
        if (nodes.length) style.element.insertBefore(textNode, nodes[index]);else style.element.appendChild(textNode);
      }
    }
  }

  var browser = createInjector;

  /* script */
  const __vue_script__ = script;

  /* template */
  var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{"id":"🌐"}},[_c('nav',[_c('router-link',{attrs:{"to":"/weekly"}},[_vm._v("新番索引")]),_vm._v(" "),_c('router-link',{attrs:{"to":"/favorite"}},[_vm._v("書籤索引")])],1),_vm._v(" "),_c('router-view',{staticClass:"page-view"})],1)};
  var __vue_staticRenderFns__ = [];

    /* style */
    const __vue_inject_styles__ = function (inject) {
      if (!inject) return
      inject("data-v-6d304cfc_0", { source: "#🌐[data-v-6d304cfc]{margin-top:20px;font-size:14px}a[data-v-6d304cfc]{color:#000;text-decoration:none}nav>a[data-v-6d304cfc]{display:inline-block;padding:3px 15px;background:#fff;cursor:pointer;border-top:1px solid #247;border-left:1px solid #247;border-right:1px solid #247;border-radius:5px 5px 0 0}nav>a.router-link-exact-active[data-v-6d304cfc]{border-top:3px solid #1e90ff}.page-view[data-v-6d304cfc]{border:1px solid #247}", map: undefined, media: undefined });

    };
    /* scoped */
    const __vue_scope_id__ = "data-v-6d304cfc";
    /* module identifier */
    const __vue_module_identifier__ = undefined;
    /* functional template */
    const __vue_is_functional_template__ = false;
    /* style inject SSR */
    

    
    var MainComp = normalizeComponent_1(
      { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
      __vue_inject_styles__,
      __vue_script__,
      __vue_scope_id__,
      __vue_is_functional_template__,
      __vue_module_identifier__,
      browser,
      undefined
    );

  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //

  var script$1 = {
    filters: {
      keywordLink(keyword) {
        return `/topics/list?keyword=${keyword}`;
      },
    },
    data() {
      return {
        userInputStr: '',
        validityMsg: '',
      };
    },
    computed: {
      favoriteBangumiList() {
        return this.$store.state.favoriteBangumiList;
      },
      utitle() {
        return this.userInputStr.trim();
      },
    },
    methods: {
      setValidity(msg) {
        this.validityMsg = msg;
        this.$refs.userTitleInput.setCustomValidity(this.validityMsg);
      },
      addFavorite() {
        if (!this.utitle) {
          this.setValidity('名稱欄為空');
          return;
        }
        const found = this.favoriteBangumiList.find(b => b.title === this.utitle);
        if (found) {
          this.setValidity('書籤名稱已存在');
          return;
        }

        const keyword = new URL(location).searchParams.get('keyword');
        this.$store.dispatch('appendFavoriteBangumi', {
          title: this.utitle,
          keyword,
        });
      },
      delFavorite() {
        if (!this.utitle) {
          this.setValidity('名稱欄為空');
          return;
        }
        const found = this.favoriteBangumiList.find(b => b.title === this.utitle);
        if (!found) {
          this.setValidity('書籤名稱不存在');
          return;
        }

        this.$store.dispatch('removeFavoriteBangumi', this.utitle);
      },
    },
  };

  /* script */
  const __vue_script__$1 = script$1;

  /* template */
  var __vue_render__$1 = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._m(0),_vm._v(" "),_c('div',{staticClass:"favorite-area"},[_c('div',{staticClass:"favorite-pool"},_vm._l((_vm.favoriteBangumiList),function(bangumi){return _c('a',{key:bangumi.title,staticClass:"bangumi",attrs:{"href":_vm._f("keywordLink")(bangumi.keyword),"role":"button"}},[_vm._v(_vm._s(bangumi.title))])}),0),_vm._v(" "),_c('div',{staticClass:"input-area"},[_c('input',{directives:[{name:"model",rawName:"v-model",value:(_vm.userInputStr),expression:"userInputStr"}],ref:"userTitleInput",staticClass:"user-title-input",attrs:{"type":"text","placeholder":"為目前網址取名"},domProps:{"value":(_vm.userInputStr)},on:{"input":[function($event){if($event.target.composing){ return; }_vm.userInputStr=$event.target.value;},function($event){return _vm.setValidity('')}],"focus":function($event){return _vm.setValidity('')}}}),_vm._v(" "),_c('span',{staticClass:"tooltip"},[_vm._v(_vm._s(_vm.validityMsg))]),_vm._v(" "),_c('button',{staticClass:"add-btn",on:{"click":_vm.addFavorite}},[_vm._v("加入")]),_vm._v(" "),_c('button',{staticClass:"del-btn",on:{"click":_vm.delFavorite}},[_vm._v("刪除")])])])])};
  var __vue_staticRenderFns__$1 = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('header',[_c('span',[_vm._v("書籤索引")]),_vm._v(" "),_c('span',[_vm._v("將當前的搜索加入書籤,並自訂名稱")])])}];

    /* style */
    const __vue_inject_styles__$1 = function (inject) {
      if (!inject) return
      inject("data-v-27ecab8b_0", { source: "a[data-v-27ecab8b]{color:#247;text-decoration:none}header[data-v-27ecab8b]{color:#fff;background-color:#247;padding:5px;display:flex;font-size:.8rem}header>span[data-v-27ecab8b]:nth-of-type(n+2)::before{content:'::';padding:0 8px}header>span>a[data-v-27ecab8b]{color:#fff}.favorite-area[data-v-27ecab8b]{background-color:#fff}.favorite-pool[data-v-27ecab8b]{padding:10px;min-height:14px}.bangumi[data-v-27ecab8b]{border:1px solid orange;padding:2px;margin:1px 3px}.input-area[data-v-27ecab8b]{display:flex;justify-content:center;padding:4px;border-top:1px dotted #247}.input-area>*[data-v-27ecab8b]{margin:0 15px}.input-area>.user-title-input[data-v-27ecab8b]{border:1px solid #247;padding:0 7px;border-radius:5px;font-size:14px}.tooltip[data-v-27ecab8b]{position:absolute;background-color:#000;color:#fff;padding:5px 10px;border-radius:5px;transform-origin:bottom center;transform:translateY(-35px);display:none}.tooltip[data-v-27ecab8b]::after{content:'';width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000;position:absolute;top:100%;right:20%}.user-title-input:invalid+.tooltip[data-v-27ecab8b]{display:block}.input-area>button[data-v-27ecab8b]{border:none;border-radius:5px;padding:5px 21px;font-size:14px}.input-area>button.add-btn[data-v-27ecab8b]{background-color:#90ee90}.input-area>button.del-btn[data-v-27ecab8b]{background-color:#dc143c;color:#fff}", map: undefined, media: undefined });

    };
    /* scoped */
    const __vue_scope_id__$1 = "data-v-27ecab8b";
    /* module identifier */
    const __vue_module_identifier__$1 = undefined;
    /* functional template */
    const __vue_is_functional_template__$1 = false;
    /* style inject SSR */
    

    
    var PageFavoriteComp = normalizeComponent_1(
      { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 },
      __vue_inject_styles__$1,
      __vue_script__$1,
      __vue_scope_id__$1,
      __vue_is_functional_template__$1,
      __vue_module_identifier__$1,
      browser,
      undefined
    );

  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //
  //

  var script$2 = {
    filters: {
      keywordLink(keyword) {
        return `/topics/list?keyword=${keyword}`;
      },
      longerWeekdayStr(weekdayStr) {
        switch (weekdayStr) {
          case '日':
            return '星期日(日)';
          case '一':
            return '星期一(月)';
          case '二':
            return '星期二(火)';
          case '三':
            return '星期三(水)';
          case '四':
            return '星期四(木)';
          case '五':
            return '星期五(金)';
          case '六':
            return '星期六(土)';
        }
      },
    },
    data() {
      const now = Date.now();
      return {
        now,
        date: new Date(now),
        todayWeekday: new Date(now).getDay(),
        expansion: false,
      };
    },
    computed: {
      todayStr() {
        const longWeekdayStr = new Intl.DateTimeFormat('zh', {
          weekday: 'long',
        }).format(this.date);

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

        return `西元 ${dateStr} ${longWeekdayStr}`;
      },
      orderedWeeklyBangumi() {
        const WEEKDAY_STR = this.$store.state.const.WEEKDAY_STR;
        const TODAY_SENSITIVE_WEEKDAY_STR = WEEKDAY_STR.repeat(3).slice(
          this.todayWeekday + 5,
          this.todayWeekday + 12,
        );

        const weeklyBangumi = this.$store.state.weeklyBangumi;
        const keyedWeeklyBangumi = [...TODAY_SENSITIVE_WEEKDAY_STR].reduce(
          (collection, weekdayStr) => {
            return collection.set(weekdayStr, weeklyBangumi[weekdayStr]);
          },
          new Map(),
        );
        return [...keyedWeeklyBangumi.entries()];
      },
    },
    methods: {
      invExpansion() {
        this.expansion = !this.expansion;
      },
    },
  };

  /* script */
  const __vue_script__$2 = script$2;

  /* template */
  var __vue_render__$2 = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('header',[_c('span',[_vm._v("新番資源索引")]),_vm._v(" "),_c('span',[_vm._v(_vm._s(_vm.todayStr))]),_vm._v(" "),_c('span',[_c('a',{attrs:{"href":"javascript:;","role":"button"},on:{"click":_vm.invExpansion}},[_vm._v("\n        "+_vm._s(_vm.expansion ? '收起' : '展開')+"\n      ")])])]),_vm._v(" "),_c('table',{staticClass:"weekly-table"},_vm._l((_vm.orderedWeeklyBangumi),function(ref,index){
  var weekday = ref[0];
  var dayBangumiList = ref[1];
  return _c('tr',{directives:[{name:"show",rawName:"v-show",value:(_vm.expansion ? true : index < 4),expression:"expansion ? true : index < 4"}],key:weekday,staticClass:"weekly-tr",class:{ 'weekly-tr-today': index === 2 }},[_c('td',{staticClass:"weekly-weekday-str"},[_vm._v(_vm._s(_vm._f("longerWeekdayStr")(weekday)))]),_vm._v(" "),_c('td',_vm._l((dayBangumiList),function(bangumi){return _c('a',{key:bangumi.title,staticClass:"bangumi",class:{ 'bangumi-old': bangumi.newold === 'old' },attrs:{"href":_vm._f("keywordLink")(bangumi.keyword)}},[_vm._v(_vm._s(bangumi.title))])}),0)])}),0)])};
  var __vue_staticRenderFns__$2 = [];

    /* style */
    const __vue_inject_styles__$2 = function (inject) {
      if (!inject) return
      inject("data-v-abff4512_0", { source: "a[data-v-abff4512]{color:#247;text-decoration:none}header[data-v-abff4512]{color:#fff;background-color:#247;padding:5px;display:flex;font-size:.8rem}header>span[data-v-abff4512]:nth-of-type(n+2)::before{content:'::';padding:0 8px}header>span>a[data-v-abff4512]{color:#fff}.weekly-table[data-v-abff4512]{border-collapse:collapse;width:100%}.weekly-tr[data-v-abff4512]{display:flex;align-items:center;border:2px solid #fff;background:#fff}.weekly-tr.weekly-tr-today[data-v-abff4512]{background-color:#ff9}.weekly-weekday-str[data-v-abff4512]{padding:3px 15px;margin-right:3px;background-color:#7e99be;color:#fff;font-weight:bolder}.bangumi[data-v-abff4512]{border:1px solid orange;padding:2px;margin:1px 3px}.bangumi-old[data-v-abff4512]{border:1px solid #002fff}", map: undefined, media: undefined });

    };
    /* scoped */
    const __vue_scope_id__$2 = "data-v-abff4512";
    /* module identifier */
    const __vue_module_identifier__$2 = undefined;
    /* functional template */
    const __vue_is_functional_template__$2 = false;
    /* style inject SSR */
    

    
    var PageWeeklyComp = normalizeComponent_1(
      { render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 },
      __vue_inject_styles__$2,
      __vue_script__$2,
      __vue_scope_id__$2,
      __vue_is_functional_template__$2,
      __vue_module_identifier__$2,
      browser,
      undefined
    );

  // pre-process
  const $$ = s => Array.from(document.querySelectorAll(s));

  for (const adEl of $$('[id*="1280"]')) {
    adEl.remove();
  }

  const routes = [
    { path: '/weekly', component: PageWeeklyComp },
    { path: '/favorite', component: PageFavoriteComp },
  ];

  const YAMLtoPayloadList = newold => {
    const name = newold.toUpperCase() + '_YAML';
    const txt = GM_getResourceText(name);

    const data = jsyaml.safeLoad(txt);
    const payloadList = [];
    for (const weekdayStr of store.state.const.WEEKDAY_STR) {
      const payload = {
        weekdayStr,
        bangumiList: [],
      };
      for (const [title, keyword] of Object.entries(data[weekdayStr])) {
        payload.bangumiList.push({
          title,
          keyword,
          newold,
        });
      }
      payloadList.push(payload);
    }
    return payloadList;
  };

  // entry point

  Vue.use(Vuex);
  Vue.use(VueRouter);

  const store = new Vuex.Store({
    state: {
      weeklyBangumi: {},
      favoriteBangumiList: [],
      const: {
        WEEKDAY_STR: '日一二三四五六',
      },
    },
    mutations: {
      appendWeeklyBangumi(state, { weekdayStr, bangumiList }) {
        if (!state.weeklyBangumi[weekdayStr]) {
          state.weeklyBangumi[weekdayStr] = [];
        }
        state.weeklyBangumi[weekdayStr].push(...bangumiList);

        // re-assign to trigger rendering
        state.weeklyBangumi = Object.assign({}, state.weeklyBangumi);
      },
      appendFavoriteBangumi(state, bangumi) {
        state.favoriteBangumiList.push(bangumi);
      },
      removeFavoriteBangumi(state, bangumiTitle) {
        const indexFound = state.favoriteBangumiList.findIndex(
          b => b.title === bangumiTitle,
        );
        if (indexFound >= 0) {
          state.favoriteBangumiList.splice(indexFound, 1);
        }
      },
      saveFavorites(state) {
        const key = 'DMHY-Bangumi-Index::favorite';
        localStorage.setItem(
          key,
          LZString.compressToBase64(JSON.stringify(state.favoriteBangumiList)),
        );
      },
      loadFavorites(state) {
        const key = 'DMHY-Bangumi-Index::favorite';
        state.favoriteBangumiList =
          JSON.parse(LZString.decompressFromBase64(localStorage.getItem(key))) ||
          [];
      },
      saveWeeklyBangumi(state) {
        const key = 'DMHY-Bangumi-Index::weekly-bangumi';
        localStorage.setItem(
          key,
          LZString.compressToBase64(JSON.stringify(state.weeklyBangumi)),
        );
      },
      loadWeeklyBangumi(state) {
        const key = 'DMHY-Bangumi-Index::weekly-bangumi';
        state.weeklyBangumi = JSON.parse(
          LZString.decompressFromBase64(localStorage.getItem(key)) || '{}',
        );
      },
    },
    actions: {
      async downloadWeeklyBangumi({ commit }) {
        const oldPayloadList = YAMLtoPayloadList('old');
        for (const payload of oldPayloadList) {
          commit('appendWeeklyBangumi', payload);
        }
        const newPayloadList = YAMLtoPayloadList('new');
        for (const payload of newPayloadList) {
          commit('appendWeeklyBangumi', payload);
        }
        commit('saveWeeklyBangumi');
      },
      appendFavoriteBangumi({ commit }, bangumi) {
        commit('appendFavoriteBangumi', bangumi);
        commit('saveFavorites');
      },
      removeFavoriteBangumi({ commit }, bangumiTitle) {
        commit('removeFavoriteBangumi', bangumiTitle);
        commit('saveFavorites');
      },
    },
  });

  const router = new VueRouter({
    routes,
  });

  window.vm = new Vue({
    el: $$('div.main > div')[0],
    store,
    router,
    mounted() {
      this.$router.push('/weekly');
      this.$store.commit('loadFavorites');
      this.$store.commit('saveFavorites');
      this.loadCachedWeeklyBangumi();
    },
    methods: {
      loadCachedWeeklyBangumi() {
        const cacheKey = 'DMHY-Bangumi-Index::weekly-bangumi-cache-t';
        const cacheT = Number(localStorage.getItem(cacheKey)) || 0;
        const maxCacheTime = 86400 * 1000 * 0.5; // ms of half day

        if (Date.now() - cacheT > maxCacheTime) {
          this.$store.dispatch('downloadWeeklyBangumi');
          localStorage.setItem(cacheKey, Date.now());
        } else {
          this.$store.commit('loadWeeklyBangumi');
        }
      },
    },
    render(h) {
      return h(MainComp);
    },
  });

  // window.vm.$mount($$('div.main > div')[0]);
  // processBangumi('old').then(() => processBangumi('new'));

}(Vue, Vuex, VueRouter, jsyaml, LZString));