您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
DLsite内のお気に入り作品一覧で、クーポン対象の作品のみをフィルターする機能を追加します
// ==UserScript== // @name DFavCouponFilter // @namespace sgthr7/monkey-script // @version 0.0.2 // @author SGThr7 // @description DLsite内のお気に入り作品一覧で、クーポン対象の作品のみをフィルターする機能を追加します // @license MIT // @match https://www.dlsite.com/* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js // @grant GM_addStyle // ==/UserScript== (t=>{if(typeof GM_addStyle=="function"){GM_addStyle(t);return}const a=document.createElement("style");a.textContent=t,document.head.append(a)})(" .expand_button[data-v-950d95b9]{width:183px;font-size:12px;display:flex;justify-content:space-between;padding:3px 8px 6px}.expand_icon[data-v-950d95b9]{display:inline-block;width:12px;margin-top:1px;transition:transform .05s}.expand_button[aria-expanded=true] .expand_icon[data-v-950d95b9]{transform:rotate(180deg)}.expand_button[aria-expanded=true]~.content[data-v-950d95b9]{display:block}.expand_button[aria-expanded=false]~.content[data-v-950d95b9]{display:none}.content[data-v-950d95b9]{border:1px solid gray;border-radius:0 10px 10px;padding:5px 10px}.coupons[data-v-3eec1958]{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr))}.no-target-coupons[data-v-3eec1958]{margin-top:20px} "); (function (vue) { 'use strict'; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); async function fetchCoupons() { const couponsUrl = "https://www.dlsite.com/books/mypage/coupon/list/ajax"; const couponsRaw = await fetch(couponsUrl); if (!couponsRaw.ok) { throw new Error(`HTTP error: status=${couponsRaw.status}`); } const coupons = await couponsRaw.json(); return coupons; } async function fetchProductInfo(productId) { const info = await fetchProductInfos([productId]); return info[productId]; } async function fetchProductInfos(productIds) { const baseUrl = "https://www.dlsite.com/maniax/product/info/ajax"; const productSearchParam = "product_id"; const separator = ","; const requestIds = productIds.join(separator); const searchParams = new URLSearchParams({ [productSearchParam]: requestIds }); const url = new URL(`${baseUrl}?${searchParams}`); const infosRes = await fetch(url); if (!infosRes.ok) { throw new Error(`Failed to fetch product info: ${url}`); } const products = await infosRes.json(); return products; } function parseProductId(content) { var _a, _b; const contentInfoDom = content.querySelector("dl.work_1col"); const contentUrlRaw = (_a = contentInfoDom == null ? void 0 : contentInfoDom.querySelector("a")) == null ? void 0 : _a.getAttribute("href"); if (contentUrlRaw == null) { console.error("Content URL not found", content); return null; } const contentUrl = new URL(contentUrlRaw); const productId = (_b = contentUrl.pathname.split("/").at(-1)) == null ? void 0 : _b.split(".").at(0); if (productId == null) { console.error(`Failed to parse product ID: ${contentUrlRaw}`); return null; } return productId; } function parseId(productDom) { var _a; const productLink = productDom.querySelector(".work_name > a"); const urlRaw = productLink == null ? void 0 : productLink.getAttribute("href"); if (urlRaw == null) { console.warn("Failed to find product link", productDom); return null; } const productUrl = new URL(urlRaw); const productId = (_a = productUrl.pathname.split("/").at(-1)) == null ? void 0 : _a.split(".").at(0); if (productId == null) { console.warn(`Failed to parse product ID: ${urlRaw}`); return null; } return productId; } function parseTitle(productDom) { const productLink = productDom.querySelector(".work_name > a"); const title = (productLink == null ? void 0 : productLink.getAttribute("title")) ?? (productLink == null ? void 0 : productLink.textContent); if (title == null) { console.warn("Failed to find product link", productDom); return null; } return title; } class DProduct { // MARK: Constructor constructor(productId, dom) { __publicField(this, "id"); // TODO: 自動ページ送り拡張などを使うとDOMが複数になることがあるため、複数DOMを操作できるようにしたい __publicField(this, "dom"); __publicField(this, "info"); __publicField(this, "promiseFetchInfo"); this.id = productId; this.dom = dom; this.info = void 0; } /** * @returns DOMから取得した作品IDを持つDProduct。作品IDが取得できなかった場合はnullを返す。 */ static tryFromDom(dom) { const productId = parseId(dom); if (productId == null) { console.error("Failed to find product ID", dom); return null; } return new DProduct(productId, dom); } // MARK: 初期化 /** * 非同期で作品情報を取得 */ async asyncFetchInfo() { if (this.promiseFetchInfo == null) { this.info = void 0; this.promiseFetchInfo = new Promise(async (resolve) => { const resInfo = await fetchProductInfo(this.id); this.info = resInfo; resolve(); }); } await this.promiseFetchInfo; return this.info; } // MARK: DOM操作 isVisible() { return !this.dom.hidden; } setIsVisible(isVisible) { this.dom.hidden = !isVisible; } // MARK: Info (async) /** * @note `async`版は`asyncFetchInfo()`を使用する */ getInfo() { return this.info; } setInfo(val) { this.info = val; } // 外部からPromiseを登録する registerFetchInfoPromise(promise) { this.promiseFetchInfo = promise; } // MARK: Accessor getId() { return this.id; } getTitle() { var _a; return ((_a = this.info) == null ? void 0 : _a.title_name) ?? parseTitle(this.dom) ?? "[No Title]"; } async asyncGetMakerId() { await this.asyncFetchInfo(); return this.getMakerIdCache(); } getMakerIdCache() { var _a; console.assert(this.info != null, "Product info has not been fetched yet"); return ((_a = this.info) == null ? void 0 : _a.maker_id) ?? ""; } async asyncGetCustomGenres() { await this.asyncFetchInfo(); return this.getCustomGenresCache(); } getCustomGenresCache() { var _a; console.assert(this.info != null, "Product info has not been fetched yet"); return ((_a = this.info) == null ? void 0 : _a.custom_genres) ?? []; } async getSiteId() { await this.asyncFetchInfo(); return this.getSiteIdCache(); } getSiteIdCache() { var _a; console.assert(this.info != null, "Product info has not been fetched yet"); return (_a = this.info) == null ? void 0 : _a.site_id; } getDom() { return this.dom; } } function compareDiscountType(a, b) { switch (a) { case "rate": { switch (b) { case "rate": { return 0; } case "price": { return -1; } } } case "price": { switch (b) { case "rate": { return 1; } case "price": { return 0; } } } } console.trace(`Unexpected discount type (${a}, ${b})`); return 0; } class DCoupon { constructor(info) { __publicField(this, "info"); this.info = info; } getId() { return this.info.coupon_id; } getName() { return this.info.coupon_name; } getDiscountRate() { return parseInt(this.info.discount); } getDiscountType() { return this.info.discount_type; } /** * @return クーポンの使用期限 */ getUseLimitDate() { const limitDateSec = this.info.limit_date; const limitDateMsec = limitDateSec * 1e3; const limitDate = new Date(limitDateMsec); return limitDate; } /** * @returns 有効なクーポンかどうか */ isAvailable() { const hasPresented = true; const limitDate = this.getUseLimitDate(); const currentDate = /* @__PURE__ */ new Date(); const isInLimitDate = limitDate != null ? currentDate <= limitDate : false; return hasPresented && isInLimitDate; } /** * @param product 対象の作品 * @returns クーポン対象の作品かどうか */ async canDiscount(product) { var _a, _b, _c; switch (this.info.condition_type) { case "id_all": { return ((_a = this.info.conditions.product_all) == null ? void 0 : _a.some( (productId) => productId === product.getId() )) ?? false; } case "custom_genre": { const pGenres = await product.asyncGetCustomGenres(); return ((_b = this.info.conditions.custom_genre) == null ? void 0 : _b.some( (cGenre) => pGenres.some((pGenre) => cGenre === pGenre) )) ?? false; } case "site_ids": { const pSiteId = await product.getSiteId(); return ((_c = this.info.conditions.site_ids) == null ? void 0 : _c.includes(pSiteId)) ?? false; } } console.trace(`Unexpected condition type "${this.info.condition_type}"`); return false; } compare(other) { const cmpDiscountType = compareDiscountType(this.getDiscountType(), other.getDiscountType()); if (cmpDiscountType !== 0) return cmpDiscountType; const cmpDiscount = other.getDiscountRate() - this.getDiscountRate(); if (cmpDiscount !== 0) return cmpDiscount; const maxDate = /* @__PURE__ */ new Date(864e13); const cmpLimitDate = (this.getUseLimitDate() ?? maxDate).getTime() - (other.getUseLimitDate() ?? maxDate).getTime(); return cmpLimitDate; } } class MultiFactorBooleans { constructor() { /** * ある要因に基づいたBooleanの値 */ __publicField(this, "factors"); /** * Booleanをまとめた結果のキャッシュ */ __publicField(this, "result"); this.factors = /* @__PURE__ */ new Map(); this.result = this.getInitialValue(); } /** * Booleanをまとめる際の演算子 * @param lhs 左辺 * @param rhs 右辺 * @returns まとめた演算結果 */ operate(lhs, rhs) { console.error("Not implemented `operate` method"); return false; } getInitialValue() { console.error("Not implemented `initialValue` method"); return false; } /** * 演算結果のキャッシュを再演算し、その結果を返す * * @returns まとめた演算結果 */ recalculateResult() { this.result = this.factors.values().reduce((acc, val) => this.operate(acc, val), this.getInitialValue()); return this.result; } /** * 指定した要因の値をセットする * @param factor 要因名 * @param value 値 * @returns まとめた演算結果 */ setValue(factor, value) { this.setValueImpl(factor, value); return this.recalculateResult(); } setValueImpl(factor, value) { this.factors.set(factor, value); } /** * 指定した要因の値を取得する * @param factor 要因名 * @returns 指定した要因の値。要因が設定されていない場合は`false`を返す */ getValue(factor) { return this.factors.get(factor) ?? false; } /** * @returns まとめた演算結果 */ getResult() { return this.result; } /** * @param factor 要因名 * @returns 要因が設定されているかどうか */ hasFactor(factor) { return this.factors.has(factor); } /** * 指定したFactorを削除する * @param factor 要因名 * @returns 削除したあとのまとめた演算結果 */ removeFactor(factor) { this.removeFactorImpl(factor); return this.recalculateResult(); } removeFactorImpl(factor) { this.factors.delete(factor); } /** * @returns 任意の要因が設定されているかどうか */ hasAnyFactor() { return this.factors.size > 0; } /** * すべての要因を削除する */ clearFactors() { this.factors.clear(); this.result = this.getInitialValue(); } } class OrBooleans extends MultiFactorBooleans { operate(lhs, rhs) { return lhs || rhs; } getInitialValue() { return false; } recalculateResult() { this.result = this.factors.values().some((val) => val); return this.result; } setValue(factor, value) { if (this.hasFactor(factor) && !value) { return super.setValue(factor, value); } else { this.setValueImpl(factor, value); this.result = this.operate(this.result, value); return this.result; } } removeFactor(factor) { if (!this.getValue(factor)) { this.removeFactorImpl(factor); return this.result; } else { return super.removeFactor(factor); } } } function objEntryIter(obj) { return Iterator.from(Object.entries(obj)); } function isValidEntryKey(entry) { return entry[0] != null; } const _DPCManager = class _DPCManager { constructor() { __publicField(this, "products"); __publicField(this, "couponFilter"); __publicField(this, "coupons"); __publicField(this, "discountableCouponMap"); __publicField(this, "filterCoupons"); __publicField(this, "observerAddWishlistDom"); __publicField(this, "promiseFetchCoupons"); this.products = /* @__PURE__ */ new Map(); this.couponFilter = /* @__PURE__ */ new Map(); this.coupons = /* @__PURE__ */ new Set(); this.discountableCouponMap = /* @__PURE__ */ new Map(); this.filterCoupons = /* @__PURE__ */ new Set(); } //MARK: 初期化 init() { console.log("init"); this.bindOnAddedWishlistDom(); this.asyncFetchCoupons(); this.collectAndRegisterProducts(document); this.asyncInitLink(); } clear() { this.unbindOnAddedWishlistDom(); Iterator.from(this.products.values()).forEach((product) => product.setIsVisible(true)); this.products.clear(); this.couponFilter.clear(); this.coupons.clear(); } // MARK: 作品管理 /** * 作品を管理対象へ追加 * @param product 追加する作品 */ registerProduct(product) { const productId = product.getId(); const productExists = this.products.has(productId); console.assert(!productExists, `Exists product: ID=${productId}`); if (!productExists) { console.log(`Register product: "${product.getTitle()}"`, product, product.getDom()); this.products.set(productId, product); this.couponFilter.set(productId, new OrBooleans()); } } /** * 作品を管理対象へ追加 * @param products 追加する作品のリスト */ registerProducts(products) { const productIds = Iterator.from(products.values()).map((product) => product.getId()).toArray(); const mapProducts = ([productIds2, info]) => [this.products.get(productIds2), info]; const fetchPromise = fetchProductInfos(productIds).then((infos) => { objEntryIter(infos).map(mapProducts).filter(isValidEntryKey).forEach(([product, info]) => product.setInfo(info)); }); Iterator.from(products.values()).forEach((product) => { this.registerProduct(product); product.registerFetchInfoPromise(fetchPromise); }); } async asyncInitLink() { const linkPromises = Iterator.from(this.products.values()).map(async (product) => { await this.asyncLinkToCouponsCache(product); }); await Promise.allSettled(linkPromises); } /** * 作品と所持中のクーポンを対応付ける */ async asyncLinkToCouponsCache(product) { await this.asyncWaitFetchCoupons(); const asyncIter = Iterator.from(this.coupons.values()).map((coupon) => this.asyncLink(product, coupon)); await Promise.allSettled(asyncIter); } /** * 作品とクーポンを対応付ける */ async asyncLink(product, coupon) { const filterResult = this.getCouponFilterResult(product.getId()); const canDiscount = await coupon.canDiscount(product); filterResult.setValue(coupon.getId(), canDiscount); if (canDiscount) { const targets = this.discountableCouponMap.get(coupon.getId()); targets == null ? void 0 : targets.add(product.getId()); } } // MARK: DOM追加の監視 // 自動ページ送り拡張など用 /** * 自動ページ送りなどで追加された作品を検知する */ bindOnAddedWishlistDom() { const container = document.querySelector("div#wishlist"); if (container == null) { console.error("Failed to find wishlist container"); return; } this.observerAddWishlistDom = new MutationObserver((records, observer) => this.onAddWishlistDom(records, observer)); this.observerAddWishlistDom.observe(container, { subtree: false, childList: true }); } onAddWishlistDom(records, _observer) { console.groupCollapsed("On add wishlist dom"); console.log(records); records.forEach((record) => { record.addedNodes.values().filter(_DPCManager.isWishlistContainer).forEach(async (dom) => { const addedProducts = this.collectAndRegisterProducts(dom); const asyncLinkIter = addedProducts.values().map((product) => this.asyncLinkToCouponsCache(product)); const asyncLink = Promise.allSettled(asyncLinkIter); await asyncLink; this.updateProductsVisibility(addedProducts.values()); }); }); console.groupEnd(); } unbindOnAddedWishlistDom() { var _a; (_a = this.observerAddWishlistDom) == null ? void 0 : _a.disconnect(); } static isWishlistContainer(node) { return node instanceof HTMLElement && node.id === _DPCManager.WISHLIST_CONTAINER_ID; } // MARK: DOM操作 /** * 作品の一覧を取得して登録する * @returns 追加した作品一覧 */ collectAndRegisterProducts(container) { const addedProduct = []; console.groupCollapsed("Collect and register products"); _DPCManager.collectProductDoms(container).map(([productId, dom]) => new DProduct(productId, dom)).forEach((product) => { this.registerProduct(product); addedProduct.push(product); }); console.groupEnd(); return addedProduct; } /** * 作品のDOMを取得 * @param container 取得時のルートDOM * @returns 作品DOMのIterator */ static collectProductDoms(container) { const products = container.querySelectorAll("form#edit_wishlist > div#wishlist_work > table.n_worklist > tbody > tr._favorite_item"); const productDomsIter = products.values().map((content) => { const product_id = parseProductId(content); if (product_id == null) { console.error("Failed to find product ID", content); return null; } return [product_id, content]; }).filter((val) => val != null); return productDomsIter; } /** * 現在管理対象の作品の可視性を更新する */ updateAllProductsVisibility() { this.updateProductsVisibility(this.products.values()); } /** * 指定した管理対象作品の可視性を更新する * @param products 対象の管理中作品 */ updateProductsVisibility(products) { const isNoFilter = this.filterCoupons.size === 0; const targetProductsIter = Iterator.from(this.filterCoupons.values()).map((couponId) => this.discountableCouponMap.get(couponId)).filter((targetProducts2) => targetProducts2 != null).flatMap((targetProducts2) => targetProducts2); const targetProducts = new Set(targetProductsIter); Iterator.from(products).forEach((product) => { const isVisible = isNoFilter || targetProducts.has(product.getId()); product.setIsVisible(isVisible); }); } // MARK: クーポン /** * クーポンの取得を非同期で開始する */ async asyncFetchCoupons() { if (this.promiseFetchCoupons == null) { this.promiseFetchCoupons = fetchCoupons(); const resCoupons = await this.promiseFetchCoupons; const couponIter = resCoupons.values().map((coupon) => new DCoupon(coupon)).filter((coupon) => coupon.isAvailable()); this.coupons = new Set(couponIter); const discountableCouponsMapIter = Iterator.from(this.coupons.values()).map((coupon) => [coupon.getId(), /* @__PURE__ */ new Set()]); this.discountableCouponMap = new Map(discountableCouponsMapIter); } else { await this.promiseFetchCoupons; } return this.coupons; } /** * 既に開始しているクーポンの取得を`await`する用の関数 */ async asyncWaitFetchCoupons() { if (this.promiseFetchCoupons == null) { console.warn("No fetching promise"); return; } await this.promiseFetchCoupons; } //MARK: アクセサー getCouponFilterResult(productId) { console.assert(this.couponFilter.has(productId), `Not registered product: ID=${productId}`); return this.couponFilter.get(productId); } /** * クーポンの一覧を取得する。 * クーポンがまだ取得できていない可能性があるため、必要に応じて`await asyncWaitFetchCoupons()`で取得を待機する必要がある。 */ getCoupons() { return this.coupons; } /** * クーポン対象の作品一覧を取得する。 * 情報がまだ未収集の場合はカラのコンテナーが返る。 */ getDiscountableCouponMap(couponId) { return this.discountableCouponMap.get(couponId) ?? /* @__PURE__ */ new Set(); } addCouponFilter(couponId) { this.filterCoupons.add(couponId); this.updateAllProductsVisibility(); } removeCouponFilter(couponId) { this.filterCoupons.delete(couponId); this.updateAllProductsVisibility(); } }; __publicField(_DPCManager, "WISHLIST_CONTAINER_ID", "edit_wishlist"); let DPCManager = _DPCManager; const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({ __name: "CouponCheckbox", props: { coupon: {}, discountTargetCount: {} }, emits: ["onChecked"], setup(__props, { emit: __emit }) { const name = vue.computed(() => __props.coupon.getName()); const emit = __emit; function onChecked(e) { const isChecked = e.target.checked; emit("onChecked", isChecked, __props.coupon); } return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", null, [ vue.createElementVNode("label", null, [ vue.createElementVNode("input", { type: "checkbox", onChange: onChecked }, null, 32), vue.createElementVNode("span", null, vue.toDisplayString(name.value) + " (" + vue.toDisplayString(_ctx.discountTargetCount) + ") ", 1) ]) ]); }; } }); const CollapseIconFile = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20xmlns:xlink='http://www.w3.org/1999/xlink'%20aria-hidden='true'%20role='img'%20class='collapse-icon'%20width='8'%20height='5'%20preserveAspectRatio='xMidYMid%20meet'%20viewBox='0%200%208%205'%3e%3cpath%20stroke='white'%20fill='transparent'%20stroke-linecap='square'%20d='M1%201L4%204L7%201'%3e%3c/path%3e%3c/svg%3e"; const _hoisted_1$2 = ["aria-expanded", "aria-controls"]; const _hoisted_2$1 = ["src"]; const _hoisted_3$1 = ["id"]; const _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({ __name: "CollapseMenu", setup(__props) { const contentId = vue.useId(); const isExpanded = vue.ref(false); function onClickButton(_e) { isExpanded.value = !isExpanded.value; } return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", null, [ vue.createElementVNode("button", { type: "button", class: "expand_button", "aria-expanded": isExpanded.value, "aria-controls": vue.unref(contentId), onClick: onClickButton }, [ vue.renderSlot(_ctx.$slots, "title", {}, () => [ _cache[0] || (_cache[0] = vue.createTextVNode(" 表示切り替え ")) ], true), vue.createElementVNode("img", { src: vue.unref(CollapseIconFile), class: "expand_icon" }, null, 8, _hoisted_2$1) ], 8, _hoisted_1$2), vue.createElementVNode("div", { id: vue.unref(contentId), class: "content" }, [ vue.renderSlot(_ctx.$slots, "default", {}, void 0, true) ], 8, _hoisted_3$1) ]); }; } }); const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const CollapseMenu = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-950d95b9"]]); const _hoisted_1$1 = { class: "wrapper" }; const _hoisted_2 = { key: 0 }; const _hoisted_3 = { class: "coupons" }; const _hoisted_4 = { class: "coupons" }; const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({ __name: "AsyncCouponList", async setup(__props) { let __temp, __restore; const dpcManager = vue.reactive(new DPCManager()); dpcManager.init(); vue.onBeforeUnmount(() => { dpcManager.clear(); }); [__temp, __restore] = vue.withAsyncContext(() => dpcManager.asyncWaitFetchCoupons()), __temp = await __temp, __restore(); const allCoupons = vue.computed(() => dpcManager.getCoupons()); const discountCoupons = vue.computed(() => Iterator.from(allCoupons.value.values()).filter((coupon) => getDiscountableCount(coupon) > 0).toArray().sort((a, b) => a.compare(b))); const noDiscountCoupons = vue.computed(() => Iterator.from(allCoupons.value.values()).filter((coupon) => getDiscountableCount(coupon) === 0).toArray().sort((a, b) => a.compare(b))); function getDiscountableCount(coupon) { return dpcManager.getDiscountableCouponMap(coupon.getId()).size; } function onCouponChecked(isChecked, coupon) { if (isChecked) { dpcManager.addCouponFilter(coupon.getId()); } else { dpcManager.removeCouponFilter(coupon.getId()); } } return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, [ allCoupons.value.size <= 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2, "所持クーポン無し")) : vue.createCommentVNode("", true), vue.createElementVNode("div", _hoisted_3, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(discountCoupons.value, (coupon) => { return vue.openBlock(), vue.createBlock(_sfc_main$3, { key: coupon.getId(), coupon, discountTargetCount: getDiscountableCount(coupon), onOnChecked: onCouponChecked }, null, 8, ["coupon", "discountTargetCount"]); }), 128)) ]), noDiscountCoupons.value.length > 0 ? (vue.openBlock(), vue.createBlock(CollapseMenu, { key: 1, class: "no-target-coupons" }, { title: vue.withCtx(() => _cache[0] || (_cache[0] = [ vue.createTextVNode("割引対象無しクーポン一覧") ])), default: vue.withCtx(() => [ vue.createElementVNode("div", _hoisted_4, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(noDiscountCoupons.value, (coupon) => { return vue.openBlock(), vue.createBlock(_sfc_main$3, { key: coupon.getId(), coupon, discountTargetCount: getDiscountableCount(coupon), onOnChecked: onCouponChecked }, null, 8, ["coupon", "discountTargetCount"]); }), 128)) ]) ]), _: 1 })) : vue.createCommentVNode("", true) ]); }; } }); const AsyncCouponList = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-3eec1958"]]); const _hoisted_1 = { class: "border_b" }; const _sfc_main = /* @__PURE__ */ vue.defineComponent({ __name: "CouponFilter", setup(__props) { return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [ (vue.openBlock(), vue.createBlock(vue.Suspense, null, { default: vue.withCtx(() => [ vue.createVNode(AsyncCouponList) ]), fallback: vue.withCtx(() => _cache[0] || (_cache[0] = [ vue.createElementVNode("div", { class: "loading" }, "Loading...", -1) ])), _: 1 })) ]); }; } }); main(); function main() { const bookmarkUrlPattern = new RegExp("^https?://(www.)?dlsite.com/(\\w+)/mypage/wishlist/?.*", "i"); const currentUrl = window.location.href; if (bookmarkUrlPattern.test(currentUrl)) { createFilterBox(); } } function createFilterBox() { var _a; const filterBoxRoot = document.createElement("div"); const filterBox = vue.createApp(_sfc_main); filterBox.mount(filterBoxRoot); const insertAnchor = document.querySelector("div#wishlist > form#showList"); if (insertAnchor == null) return; (_a = insertAnchor.parentNode) == null ? void 0 : _a.insertBefore(filterBoxRoot, insertAnchor.nextElementSibling); } })(Vue);