您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
改進巴哈姆特的勇者福利社,動態載入全部商品、加入過濾隱藏功能、標示競標目前出價等。
// ==UserScript== // @name 巴哈姆特勇者福利社++ // @namespace https://github.com/DonkeyBear // @version 0.8.3 // @description 改進巴哈姆特的勇者福利社,動態載入全部商品、加入過濾隱藏功能、標示競標目前出價等。 // @author DonkeyBear // @match https://fuli.gamer.com.tw/shop.php* // @icon https://fuli.gamer.com.tw/favicon.ico // @grant none // ==/UserScript== const $ = jQuery; const isDarkMode = $(document.documentElement).data('theme') === 'dark'; const stylesheet = /* css */` #BH-wrapper { padding-top: 0; } .items-card { ${isDarkMode ? '' : 'z-index: -1;'} } #tabs-btn-group { display: flex; align-items: center; justify-content: space-between; } .tabs-btn-box { margin-top: 0; } .fuli-enhance-btn-box { overflow: visible; } .btn-distance.fuli-enhance { width: auto; } .btn-distance.fuli-enhance > [type=checkbox] { margin-right: .35rem; } .filter-by-title, .filter-by-type, .filter-by-state, #BH-pagebtn { display: none; } .digital.unaffordable { color: #DF4747; } .fuli-enhance.btn-search { padding: 0 .65rem; } .fuli-enhance.btn-filter { cursor: pointer; padding-left: 1.5rem; padding-right: 1.2rem; position: relative; border-right: 1px solid; ${isDarkMode ? 'border-color: #444444;' : ''}; } .fuli-enhance.btn-filter:hover { ${isDarkMode ? '' : 'opacity: 1;'} } .search-bar { display: flex; align-items: center; border: 1px solid; border-radius: 4px; background-color: white; padding: .2rem .3rem; } .search-bar > [type=text] { width: 9rem; border: 0; } .search-bar > [type=text]:focus-visible { outline: 0; } .icon { width: 1rem; height: 1rem; } .icon-search { border-left: 1px solid; padding-left: .35rem; margin-left: .2rem; } .icon-close { transform: translate(2px, 2px); cursor: pointer; } .icon-filter { margin-left: .3rem; } .btn-filter:hover > #filter-popup { display: flex; flex-direction: column; align-items: baseline; width: max-content; } #filter-popup { padding: .5rem; position: absolute; transform: translateX(-50%); left: 50%; top: 100%; z-index: 1; display: none; cursor: default; overflow: visible; background-color: ${isDarkMode ? '#272728' : '#FBFBFB'}; ${isDarkMode ? 'color: #C7C6CB;' : ''} } #filter-popup::before { content: ''; width: 0; height: 0; border: .4rem solid transparent; border-bottom: .35rem solid currentColor !important; position: absolute; transform: translateX(-50%); left: 50%; bottom: 100%; } #filter-popup label { cursor: pointer; } #exchange-item-counter, #bid-item-counter, #lottery-item-counter { color: #11AAC1; } #exchange-item-counter, #bid-item-counter { margin-right: .75rem; } .digital.current-bid { margin-left: 8px; } `; $('<style>').text(stylesheet).appendTo('head'); // 依照 URL Param 判斷是否執行後續程式 const newUrl = new URL(window.location); const getParam = (param) => newUrl.searchParams.get(param); const isOnHistoryPage = getParam('history') !== null && getParam('history') !== '0'; const isOnFirstPage = getParam('page') === null || getParam('page') === '1'; if (isOnHistoryPage) { return } if (!isOnFirstPage) { window.location.replace('https://fuli.gamer.com.tw/shop.php') } // 持有的巴幣存款 const DEPOSIT = +$('.brave-assets').text().replaceAll(/\D/g, ''); // .items-card .type-tag 的內文 const TYPE_TAG = { exchange: '直購', bid: '競標', lottery: '抽抽樂' }; const itemCounter = new Proxy({ exchange: 0, bid: 0, lottery: 0 }, { set(target, prop, value) { target[prop] = +value; $(`#${prop}-item-counter`).text(target[prop]); return true; } }) class ItemCard { /** @param {HTMLElement|jQuery|string} itemCard */ constructor (itemCard) { this.itemCard = itemCard; this.$itemCard = $(itemCard); this.type = $(itemCard).find('.type-tag').text().trim(); } fetchCurrentBid () { if (this.type !== TYPE_TAG.bid) { return this } // 若非競標品則結束函式 const $priceElement = this.$itemCard.find('.price'); $priceElement.text('正在讀取目前出價'); fetch(this.$itemCard.attr('href'), { method: 'GET' }) .then(res => res.text()) .then(data => { const parser = new DOMParser(); const virtualDoc = parser.parseFromString(data, 'text/html'); let currentBid; $(virtualDoc).find('.pbox-content').each((idnex, item) => { if (!$(item).text().includes('目前出價')) { return } if (!Number.isNaN(+currentBid)) { return } currentBid = $(item).find('.pbox-content-r').text().match(/[\d|,]+/)[0]; }); const newTextHTML = /* html */`目前出價<p class="digital current-bid">${currentBid}</p>巴幣`; $priceElement.html(newTextHTML); this.colorPriceTag(); }) .catch((error) => { $priceElement.text('讀取出價失敗!'); console.error(error); }); } colorPriceTag () { const $priceNumberElement = this.$itemCard.find('.digital'); const price = +$priceNumberElement.text().replaceAll(/\D/g, ''); if (DEPOSIT < price) { $priceNumberElement.addClass('unaffordable') } } registerCard () { // 依照商品卡種類,增加計數和取得目前出價 switch (this.type) { case TYPE_TAG.exchange: itemCounter.exchange++; break; case TYPE_TAG.bid: itemCounter.bid++; break; case TYPE_TAG.lottery: itemCounter.lottery++; break; } } } class ItemCardList { /** @param {HTMLElement|jQuery|string} [itemCardList=$('.item-list-box a.items-card')] */ constructor (itemCardList = $('.item-list-box a.items-card')) { this.$itemCardList = $(itemCardList); } filterByTitle (filterTitle) { this.$itemCardList.each((i, itemCard) => { const title = $(itemCard).find('.items-title').text(); const filterClassName = 'filter-by-title'; title.includes(filterTitle) ? $(itemCard).removeClass(filterClassName) : $(itemCard).addClass(filterClassName); }); } filterByType (filterType) { this.$itemCardList.each((i, itemCard) => { const type = new ItemCard(itemCard).type; if (!type.includes(filterType)) { return } const filterClassName = 'filter-by-type'; $(itemCard).toggleClass(filterClassName); }); } } // 放置商品類型計數區塊 $('#forum-lastBoard').after(/* html */` <div class="m-hidden"> <h5>現有商品數量</h5> <div class="BH-rbox flex-center"> <span>直購:</span> <span id="exchange-item-counter">0</span> <span>競標:</span> <span id="bid-item-counter">0</span> <span>抽抽樂:</span> <span id="lottery-item-counter">0</span> </div> </div> `); // 放置功能按鈕 const $firstTabsBtn = $('.tabs-btn-box'); $firstTabsBtn.wrap(/* html */`<div id="tabs-btn-group"></div>`); const $tabsBtnGroup = $('#tabs-btn-group'); const $newTabsBtn = $('<div>', { class: 'tabs-btn-box fuli-enhance-btn-box' }); $newTabsBtn.html(/* html */` <a class="flex-center btn-distance fuli-enhance btn-filter"> <span>篩選器</span> <svg class="icon icon-filter" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18"> <path d="M18.85 1.1A1.99 1.99 0 0 0 17.063 0H2.937a2 2 0 0 0-1.566 3.242L6.99 9.868 7 14a1 1 0 0 0 .4.8l4 3A1 1 0 0 0 13 17l.01-7.134 5.66-6.676a1.99 1.99 0 0 0 .18-2.09Z"/> </svg> <div id="filter-popup" class="tabs-btn-box"> <div class="flex-center btn-distance fuli-enhance"> <input id="hide-exchange-items" type="checkbox" data-keyword="${TYPE_TAG.exchange}"> <label for="hide-exchange-items">隱藏直購</label> </div> <div class="flex-center btn-distance fuli-enhance"> <input id="hide-bid-items" type="checkbox" data-keyword="${TYPE_TAG.bid}"> <label for="hide-bid-items">隱藏競標</label> </div> <div class="flex-center btn-distance fuli-enhance"> <input id="hide-lottery-items" type="checkbox" data-keyword="${TYPE_TAG.lottery}"> <label for="hide-lottery-items">隱藏抽抽樂</label> </div> </div> </a> <a class="flex-center btn-distance fuli-enhance btn-search"> <div class="search-bar"> <input type="text"> <svg class="icon icon-close" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.9" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/> </svg> <svg class="icon icon-search" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.9" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"></path> </svg> </div> </a> `); $newTabsBtn.on('change', '[type=checkbox]', function() { const typeTag = $(this).data('keyword'); const itemCardList = new ItemCardList(); itemCardList.filterByType(typeTag); }); const iconClose = $newTabsBtn.find('.icon-close'); iconClose.on('click', () => { searchBar.value = ''; const itemCardList = new ItemCardList(); itemCardList.filterByTitle(''); }); const searchBar = $newTabsBtn.find('.search-bar > [type=text]'); searchBar.on('input', function() { const searchText = $(this).val(); const itemCardList = new ItemCardList(); itemCardList.filterByTitle(searchText); }); $tabsBtnGroup.append($newTabsBtn); $('a.items-card').each((i, card) => { // 依照商品卡種類,增加計數和取得目前出價 const itemCard = new ItemCard($(card)); itemCard.registerCard(); if (itemCard.type === TYPE_TAG.bid) { itemCard.fetchCurrentBid() } }); // 動態載入全部商品 const $itemListBox = $('.item-list-box'); const maxPage = $('.BH-pagebtnA a:last-child').text(); if (maxPage === '1') { return } // 若僅一頁則不需讀取 const observer = new MutationObserver((records) => { // 建立觀測器,觀測新加入的商品卡 records.forEach((record) => { record.addedNodes.forEach((newNode) => { if (!$(newNode).hasClass('items-card')) { return } // 依照商品卡種類,增加計數和取得目前出價 const itemCard = new ItemCard($(newNode)); itemCard.registerCard(); if (itemCard.type === TYPE_TAG.bid) { itemCard.fetchCurrentBid() } }) }) }); observer.observe(document.querySelector('.item-list-box'), { childList: true }); for (let page = 2; page <= maxPage; page++) { fetch(`https://fuli.gamer.com.tw/shop.php?page=${page}`, { method: 'GET' }) .then(res => res.text()) .then(data => { const parser = new DOMParser(); const virtualDoc = parser.parseFromString(data, 'text/html'); const $items = $(virtualDoc).find('.item-list-box a.items-card'); $items.each((i, item) => { $itemListBox.append(item) }); }); }