您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a box for direct links to shop's searches
// ==UserScript== // @name MFC Buy Links // @namespace https://myfigurecollection.net/profile/tharglet // @version 2.2 // @description Adds a box for direct links to shop's searches // @author Tharglet // @license CC BY-NC-SA 4.0 // @match https://myfigurecollection.net/item/* // @grant GM_addStyle // @grant GM.getValue // @grant GM.setValue // ==/UserScript== ////////LICENCE//////// //This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/. //Please credit 'Tharglet' for the original code, and provide a link to my MFC profile: https://myfigurecollection.net/profile/tharglet /////////////////////// //Polyfill for GM_addStyle for Greasemonkey... if(typeof GM_addStyle == 'undefined') { GM_addStyle = (aCss) => { 'use strict'; let head = document.getElementsByTagName('head')[0]; if (head) { let style = document.createElement('style'); style.setAttribute('type', 'text/css'); style.textContent = aCss; head.appendChild(style); return style; } return null; }; } GM_addStyle(` h4 { font-style: italic; padding-bottom: 5px; padding-top: 10px; } .buylinks__row { display: flex; } .buylinks__column { flex: 1; padding-bottom: 4px; } .buylinks__settings__setting-title { display: block; float: left; clear: both; width: 150px; padding-bottom: 4px; } .buylinks__settings__setting-group { display: block; float: left; } .buylinks__shop-title { font-size: 1.2em; font-weight: normal; padding-left:0; padding-bottom: 6px; margin-bottom: 6px; } .buylinks__label { padding-left: 4px; } .buylinks__termselector label { padding-right: 12px; } .buylinks__button[aria-disabled=true] { color: lightgray; } .buylinks--hidden { display: none; } .buylinks__savebutton { margin-top: 8px; } `); const shopTypeLookup = { shop: 'Shop', info: 'Information', proxy: 'Proxy', }; (async () => { 'use strict'; let targetBlank = await GM.getValue('targetBlank', true); let targetString = targetBlank ? "target='_blank'" : ''; const savedSearchSettings = await GM.getValue('searchSettings', null); let searchSettings; if(savedSearchSettings === null) { await GM.setValue('searchSettings', '{"origins": "first", "manufacturers": "first", "title": "yes", "characters": "first"}'); searchSettings = { origins: 'first', manufacturers: 'first', title: 'yes', characters: 'first', }; } else { searchSettings = JSON.parse(savedSearchSettings); } let shopConfig = await GM.getValue('shopConfig', null); let shops = [{ key: 'mandarake', display: 'Mandarake', type: 'shop', searches: { en: 'https://order.mandarake.co.jp/order/listPage/list?keyword=%s&lang=en', ja: 'https://order.mandarake.co.jp/order/listPage/list?keyword=%s&lang=en', } }, { key: 'amiami', display: 'AmiAmi', type: 'shop', searches: { en: 'https://www.amiami.com/eng/search/list/?s_keywords=%s', jan: 'https://www.amiami.com/eng/search/list/?s_keywords=%s', } }, { key: 'surujp', display: 'Suruga-ya.jp', type: 'shop', searches: { ja: 'https://www.suruga-ya.jp/search?search_word=%s', jan: 'https://www.suruga-ya.jp/search?gtin=%s', } }, { key: 'surucom', display: 'Suruga-ya.com', type: 'shop', searches: { en: 'https://www.suruga-ya.com/en/products?keyword=%s', } }, { key: 'amazonjp', display: 'Amazon.co.jp', type: 'shop', searches: { ja: 'https://www.amazon.co.jp/s?k=%s', jan: 'https://www.amazon.co.jp/s?k=%s', } }, { key: 'yaj', display: 'Yahoo! Auctions Japan', type: 'shop', searches: { ja: 'https://auctions.yahoo.co.jp/search/search?p=%s', } }, { key: 'milestone', display: 'Milestone', type: 'info', searches: { en: 'https://b2b.mile-stone.jp/en/search/0/keyword=%s/', jan: 'https://b2b.mile-stone.jp/en/search/0/jan=%s/', } }, { key: 'hpoi', display: 'Hpoi', type: 'info', searches: { ja: 'https://www.hpoi.net/search?keyword=%s', jan: 'https://www.hpoi.net/search?keyword=%s', } }, { key: 'buyee', display: 'Buyee', type: 'proxy', searches: { ja: 'https://buyee.jp/item/search/query/%s', } }, { key: 'fromjapan', display: 'From Japan', type: 'proxy', searches: { ja: 'https://www.fromjapan.co.jp/en/item/search/%s/', } }, { key: 'zenmarket', display: 'Zenmarket', type: 'proxy', searches: { ja: 'https://zenmarket.jp/en/marketplace.aspx?q=%s', } }, { key: 'neokyo', display: 'Neokyo', type: 'proxy', searches: { ja: 'https://neokyo.com/en/search-results?keyword=%s', } }]; //functions const renderShopBlock = () => { let shopGrid = {}; shops.map((shop) => { if(!shopConfig[shop.key]) { shopConfig[shop.key] = { display: 'true' }; GM.setValue('shopConfig', JSON.stringify(shopConfig)); } if(shopConfig[shop.key].display === 'true') { let shopToAdd = ''; shopToAdd += `<div class="buylinks__row"><div class="buylinks__column">${shop.display}</div><div class="buylinks__column">`; shopToAdd += shop.searches.ja ? `<a href='#' id='search_${shop.key}_ja' class="buylinks__button buylinks__button-ja" data-for="${shop.key}" ${targetString}>Japanese</a>` : '<div> </div>'; shopToAdd += '</div><div class="buylinks__column">'; shopToAdd += shop.searches.en ? `<a href='#' id='search_${shop.key}_en' class="buylinks__button buylinks__button-en" data-for="${shop.key}" ${targetString}>English</a>` : '<div> </div>'; shopToAdd += '</div><div class="buylinks__column">'; shopToAdd += shop.searches.jan && jan ? `<a href='${shop.searches.jan.replace('%s', jan)}' class="buylinks__button buylinks__button-jan" data-for="${shop.key}" ${targetString}>JAN</a>` : '<div> </div>'; shopToAdd += '</div></div>'; if(!shopGrid[shop.type]) { shopGrid[shop.type] = shopToAdd; } else { shopGrid[shop.type] += shopToAdd; } } }); let shopHtml = ''; for(let shopType in shopGrid) { shopHtml += `<h3 class="buylinks__shop-title">${shopTypeLookup[shopType]}</h3>${shopGrid[shopType]}` } if(shopHtml.length > 0) { return shopHtml; } return '<h3 class="buylinks__shop-title">No sites enabled</h3><div>Please use the settings button in the top-right of this block to enable one or more sites</div>' } const generateSearchList = (fields, fieldName) => { if(fields.length > 0) { const fieldNameLower = fieldName.toLowerCase(); let seachListHtml = ''; seachListHtml += `<div class="buylinks__termselector">${fieldName}: `; fields.map((field, idx) => { let checked = ''; if(idx === 0 && (searchSettings[fieldNameLower] === 'first' || searchSettings[fieldNameLower] === 'all')) { checked = "checked='checked'"; } else if(idx > 0 && searchSettings[fieldNameLower] === 'all') { checked = "checked='checked'"; } seachListHtml += `<input id='${fieldNameLower}_${idx}' type='checkbox' ${checked} class='buylinks__termselector__checkbox' data-en='${field.en.replace("'", ''')}' data-ja='${field.ja.replace("'", ''')}'/> <label class='buylinks__label' for='${fieldNameLower}_${idx}'>${field.en}</label>` }); seachListHtml += '</div>'; return seachListHtml; } return ''; } const updateSearch = () => { const checkedBoxes = document.querySelectorAll('.buylinks__termselector__checkbox:checked'); if(checkedBoxes.length > 0) { document.querySelectorAll('.buylinks__button-en').forEach(ele => { ele.setAttribute('aria-disabled', false); }); document.querySelectorAll('.buylinks__button-ja').forEach(ele => { ele.setAttribute('aria-disabled', false); }); let searchJa = ''; let searchEn = ''; checkedBoxes.forEach(box => { if(searchEn.length !== 0) { searchEn += '%20'; } searchEn += encodeURIComponent(box.attributes['data-en'].value); if(searchJa.length !== 0) { searchJa += '%20'; } searchJa += encodeURIComponent(box.attributes['data-ja'].value); }); shops.forEach(shop => { if(shopConfig[shop.key].display === 'true') { if(shop.searches.en) { const searchAnchorEn = document.getElementById(`search_${shop.key}_en`); searchAnchorEn.setAttribute('href', shop.searches.en.replace('%s', searchEn)); } if(shop.searches.ja) { const searchAnchorJa = document.getElementById(`search_${shop.key}_ja`); searchAnchorJa.setAttribute('href', shop.searches.ja.replace('%s', searchJa)); } } }); } else { document.querySelectorAll('.buylinks__button-en').forEach(ele => { ele.setAttribute('href', ''); ele.setAttribute('aria-disabled', true); }); document.querySelectorAll('.buylinks__button-ja').forEach(ele => { ele.setAttribute('href', ''); ele.setAttribute('aria-disabled', true); }); } } const saveSettings = async () => { searchSettings = { origins: document.querySelector('input[name="origin_default"]:checked').value || 'first', manufacturers: document.querySelector('input[name="mfr_default"]:checked').value || 'first', title: document.querySelector('input[name="title_default"]:checked').value || 'yes', characters: document.querySelector('input[name="char_default"]:checked').value || 'first', }; await GM.setValue('searchSettings', JSON.stringify(searchSettings)); await Promise.all(shops.map((shop) => { const shopVisibilityElement = document.querySelector(`input[name='${shop.key}_visibility']:checked`); if(shopVisibilityElement !== null) { shopConfig[shop.key].display = shopVisibilityElement.value } })); await GM.setValue('shopConfig', JSON.stringify(shopConfig)); const targetSettingCheckboxValue = document.querySelector(`input[name='targetblank']:checked`).value; if(targetSettingCheckboxValue === 'no') { targetBlank = false; targetString = ''; await GM.setValue('targetBlank', false); } else { targetBlank = true; targetString = "target='_blank'"; await GM.setValue('targetBlank', true); } //refresh shop area document.getElementById("buylinks-shoparea").innerHTML = renderShopBlock(); updateSearch(); //Show "saved!" document.getElementById('buylinks-savesettings-text').classList.remove('buylinks--hidden'); setTimeout(() => { document.getElementById('buylinks-savesettings-text').classList.add('buylinks--hidden'); }, 3000); } //Code! shops.sort(function(a, b) { var textA = a.key var textB = b.key; return (textA < textB) ? -1 : (textA > textB) ? 1 : 0; }); //Prep shopConfig var if(shopConfig) { shopConfig = JSON.parse(shopConfig); } else { shopConfig = {} } //Find item properties let jan = null; let productIdElement = document.querySelector('meta[itemprop="productID"]'); if(productIdElement) { let productId = productIdElement.attributes.content.value; if(productId.startsWith('jan:')) { jan = productId.substring(4); } } //prep selector block let origins = []; let chars = []; let title = null; let mfrs = []; const figureData = Array.from(document.querySelectorAll('.split-right.righter .form-label')); let origin = figureData.find(el => el.textContent === 'Origin'); if(origin) { Array.from(origin.parentElement.querySelectorAll("span")).map((ele) => { if(ele.innerText !== 'Original Character') { origins.push({ en: ele.innerText, ja: ele.attributes.switch.value }); } }); } let character = figureData.find(el => el.textContent.startsWith('Character')); if(character) { Array.from(character.parentElement.querySelectorAll("span")).map((ele) => { chars.push({ en: ele.innerText, ja: ele.attributes.switch.value }); }); } let titleFind = figureData.find(el => el.textContent.startsWith('Title')); if(titleFind) { const titleData = titleFind.parentElement.querySelector('a'); title = { en: titleData.innerText, ja: titleData.attributes.switch.value }; } let manufacturerFind = Array.from(document.querySelectorAll('.split-right.righter .form-input small')) .filter(el => el.textContent === 'As Manufacturer'); if(manufacturerFind) { manufacturerFind.map(ele => { const mfr = ele.parentElement.querySelector('span'); mfrs.push({ en: mfr.innerText, ja: mfr.attributes.switch.value }); }); } let searchSelectorHtml = '<h3 class="buylinks__shop-title">Search options</h3><div>'; searchSelectorHtml += generateSearchList(origins, 'Origins'); searchSelectorHtml += generateSearchList(chars, 'Characters'); searchSelectorHtml += generateSearchList(mfrs, 'Manufacturers'); if(title) { searchSelectorHtml += `<div class="buylinks__termselector">Title: <input id='title_1' type='checkbox' class='buylinks__termselector__checkbox' ${searchSettings.title === 'yes' ? "checked='checked'" : ''} data-en='${title.en.replace("'", ''')}' data-ja='${title.ja.replace("'", ''')}''/> <label class='buylinks__label' for='title_1'>${title.en}</label></div>`; } searchSelectorHtml += '</div>'; //prep buy block let shopHtml = renderShopBlock(); let shopSettingsHtml = ''; shops.forEach((shop) => { shopSettingsHtml += `<div class='buylinks__settings__setting-title'>${shop.display}:</div> <div class='buylinks__settings__setting-group'> <input id='${shop.key}_visibility_no' name='${shop.key}_visibility' type='radio' value='false' ${shopConfig[shop.key].display === 'true'? '' : "checked='checked'"}/><label class='buylinks__label' for='${shop.key}_visibility_no'>No</label> <input id='${shop.key}_visibility_yes' name='${shop.key}_visibility' type='radio' value='true' ${shopConfig[shop.key].display === 'true'? "checked='checked'" : ''}/><label class='buylinks__label' for='${shop.key}_visibility_yes'>Yes</label> </div>` }); shopSettingsHtml += '<div style="clear: both;"></div>'; const buyBox = `<section><h2>Buy!<nav class="actions"><a href="#" id="buylinks-settings-button" title="Settings"><span class="tiny-icon-only icon-sliders"></a></nav></h2> <div id='buylinks-settings' class='form buylinks--hidden'> <h3 class="buylinks__shop-title">General settings</h3> <div class='buylinks__settings__setting-title'>Target new window:</div> <div class='buylinks__settings__setting-group'> <input id='targetblank_no' name='targetblank' type='radio' value='no' ${targetBlank ? '' : "checked='checked'"}/><label class='buylinks__label' for='targetblank_no'>No</label> <input id='targetblank_yes' name='targetblank' type='radio' value='yes' ${targetBlank ? "checked='checked'" : ''}/><label class='buylinks__label' for='targetblank_yes'>Yes</label> </div> <div style="clear: both;"></div> <h3 class="buylinks__shop-title">Default checkboxes</h3> <div class='buylinks__settings__setting-title'>Origins:</div> <div class='buylinks__settings__setting-group'> <input id='origin_default_none' name='origin_default' type='radio' value='none' ${searchSettings.origins === 'none' ? "checked='checked'" : ''}/><label class='buylinks__label' for='origin_default_none'>None</label> <input id='origin_default_first' name='origin_default' type='radio' value='first' ${searchSettings.origins === 'first' ? "checked='checked'" : ''}/><label class='buylinks__label' for='origin_default_first'>First</label> <input id='origin_default_all' name='origin_default' type='radio' value='all' ${searchSettings.origins === 'all' ? "checked='checked'" : ''}/><label class='buylinks__label' for='origin_default_all'>All</label> </div> <div class='buylinks__settings__setting-title'>Characters:</div> <div class='buylinks__settings__setting-group'> <input id='char_default_none' name='char_default' type='radio' value='none' ${searchSettings.characters === 'none' ? "checked='checked'" : ''}/><label class='buylinks__label' for='char_default_none'>None</label> <input id='char_default_first' name='char_default' type='radio' value='first' ${searchSettings.characters === 'first' ? "checked='checked'" : ''}/><label class='buylinks__label' for='char_default_first'>First</label> <input id='char_default_all' name='char_default' type='radio' value='all' ${searchSettings.characters === 'all' ? "checked='checked'" : ''}/><label class='buylinks__label' for='char_default_all'>All</label> </div> <div class='buylinks__settings__setting-title'>Manufacturers:</div> <div class='buylinks__settings__setting-group'> <input id='mfr_default_none' name='mfr_default' type='radio' value='none' ${searchSettings.manufacturers === 'none' ? "checked='checked'" : ''}/><label class='buylinks__label' for='mfr_default_none'>None</label> <input id='mfr_default_first' name='mfr_default' type='radio' value='first' ${searchSettings.manufacturers === 'first' ? "checked='checked'" : ''}/><label class='buylinks__label' for='mfr_default_first'>First</label> <input id='mfr_default_all' name='mfr_default' type='radio' value='all' ${searchSettings.manufacturers === 'all' ? "checked='checked'" : ''}/><label class='buylinks__label' for='mfr_default_all'>All</label> </div> <div class='buylinks__settings__setting-title'>Title:</div> <div class='buylinks__settings__setting-group'> <input id='title_default_no' name='title_default' type='radio' value='no' ${searchSettings.title === 'no' ? "checked='checked'" : ''}/><label class='buylinks__label' for='title_default_no'>No</label> <input id='title_default_yes' name='title_default' type='radio' value='yes' ${searchSettings.title === 'yes' ? "checked='checked'" : ''}/><label class='buylinks__label' for='title_default_yes'>Yes</label> </div> <div style="clear: both;"></div> <h3 class="buylinks__shop-title">Shop visibility</h3> ${shopSettingsHtml} <button id='buylinks-save-button' class='buylinks__savebutton'>Save</button> <div id='buylinks-savesettings-text' class='buylinks--hidden'>Saved!</div> </div> <div class='form'> ${searchSelectorHtml} <div id='buylinks-shoparea'>${shopHtml}</div> </div> </section>`; let template = document.createElement('template'); template.innerHTML = buyBox; document.querySelector("#wide .wrapper section").after(template.content.firstChild); let checkboxes = document.getElementsByClassName('buylinks__termselector__checkbox'); Array.from(checkboxes).forEach((element) => { element.addEventListener('click', updateSearch); }); document.body.addEventListener('click', (event) => { if (event.target.nodeName == 'A' && event.target.getAttribute('aria-disabled') == 'true') { event.preventDefault(); } }); document.getElementById('buylinks-settings-button').addEventListener('click', (event) => { event.preventDefault(); document.getElementById('buylinks-settings').classList.toggle("buylinks--hidden"); }); document.getElementById('buylinks-save-button').addEventListener('click', (event) => { saveSettings(); }); updateSearch(); })();