您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ふたばちゃんねるのスレッド上で貼られたAmazonのURLからtitleとあれば画像を取得する
当前为
// ==UserScript== // @name buyNow! // @namespace http://2chan.net/ // @version 0.1.2 // @description ふたばちゃんねるのスレッド上で貼られたAmazonのURLからtitleとあれば画像を取得する // @author ame-chan // @match https://*.2chan.net/b/res/* // @match https://kako.futakuro.com/futa/* // @match https://tsumanne.net/si/data/* // @icon https://www.google.com/s2/favicons?sz=64&domain=2chan.net // @grant GM_xmlhttpRequest // @connect https://amazon.co.jp/* // @connect https://www.amazon.co.jp/* // @connect https://amzn.to/* // @connect https://amzn.asia/* // @license MIT // ==/UserScript== (function () { 'use strict'; const WHITE_LIST_URLS = [ 'https://amazon.co.jp/', 'https://www.amazon.co.jp/', 'https://amzn.to/', 'https://amzn.asia/', ]; const isAmazon = (path) => ['amazon.co.jp', 'amzn.to', 'amzn.asia'].some((url) => path.includes(url)); const WHITE_LIST_SELECTORS = (() => WHITE_LIST_URLS.map((url) => `a[href^="${url}"]`).join(','))(); const isProductPage = (url) => /https:\/\/(www\.)?amazon\.co\.jp\/.*(?:gp\/product|dp)\/[A-Z0-9]{10}/.test(url) || /https:\/\/amzn.(asia|to)\//.test(url); const addedStyle = `<style id="userjs-get-title-link"> .userjs-title { display: block; margin: 8px 0 16px; padding: 8px 16px; line-height: 1.6 !important; color: #dc143c !important; background-color: #fff; border-radius: 4px; } </style>`; if (!document.querySelector('#userjs-get-title-link')) { document.head.insertAdjacentHTML('beforeend', addedStyle); } class FileReaderEx extends FileReader { /** * FileReaderのPromiseラッパー * @example * ```js * const blob = await (await fetch(url)).blob(); * const result = await new utils.FileReaderEx().readAsArrayBuffer(blob); * const buffer = new Uint8Array(result as ArrayBuffer); * buffer; * ``` */ constructor() { super(); } #readAs(blob, ctx) { return new Promise((res, rej) => { super.addEventListener('load', ({ target }) => target?.result && res(target.result)); super.addEventListener('error', ({ target }) => target?.error && rej(target.error)); super[ctx](blob); }); } readAsArrayBuffer(blob) { return this.#readAs(blob, 'readAsArrayBuffer'); } readAsDataURL(blob) { return this.#readAs(blob, 'readAsDataURL'); } } const getHTMLData = (url) => new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url, timeout: 10000, onload: (result) => { if (result.status === 200) { return resolve(result.responseText); } return resolve(false); }, onerror: (err) => err && resolve(false), ontimeout: () => resolve(false), }); }); const setFailedText = (linkElm) => { linkElm.insertAdjacentHTML('afterend', `<span class="userjs-title">データ取得失敗</span>`); }; const setTitleText = (targetDocument, linkElm) => { const titleElm = targetDocument.querySelector('title'); if (!titleElm || !titleElm?.textContent) return; linkElm.insertAdjacentHTML('afterend', `<span class="userjs-title">${titleElm.textContent}</span>`); }; const setImageElm = async (targetDocument, titleTextElm) => { const imageEventHandler = (e) => { const self = e.currentTarget; if (!(self instanceof HTMLImageElement)) return; if (self.width === 100) { self.width = 600; } else { self.width = 100; } }; const imageElm = targetDocument.querySelector('#landingImage') || targetDocument.querySelector('.unrolledScrollBox li:first-child img'); if (!(imageElm instanceof HTMLImageElement)) return; const blob = await (await fetch(imageElm.src)).blob(); const dataUrl = await new FileReaderEx().readAsDataURL(blob); const img = document.createElement('img'); img.src = dataUrl; img.width = 100; img.classList.add('userjs-image'); titleTextElm.insertAdjacentElement('afterend', img); img.addEventListener('click', imageEventHandler); }; const setLoading = (linkElm) => { const loadingSVG = '<svg data-id="userjs-loading" width="16" height="16" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#000"><g fill="none" fill-rule="evenodd"><g transform="translate(1 1)" stroke-width="2"><circle stroke-opacity=".5" cx="18" cy="18" r="18"/><path d="M36 18c0-9.94-8.06-18-18-18"> <animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="1s" repeatCount="indefinite"/></path></g></g></svg>'; const parentElm = linkElm.parentElement; if (parentElm instanceof HTMLFontElement || !isProductPage(linkElm.href)) { return; } linkElm.insertAdjacentHTML('afterend', loadingSVG); }; const removeLoading = (targetElm) => targetElm.parentElement?.querySelector('[data-id="userjs-loading"]')?.remove(); const insertURLData = async (linkElm) => { const parentElm = linkElm.parentElement; if (parentElm instanceof HTMLFontElement || !isProductPage(linkElm.href)) { return; } const htmlData = await getHTMLData(linkElm.href); if (!htmlData) { setFailedText(linkElm); return; } const parser = new DOMParser(); const targetDocument = parser.parseFromString(htmlData, 'text/html'); setTitleText(targetDocument, linkElm); const titleTextElm = parentElm?.querySelector('.userjs-title'); if (isAmazon(linkElm.href) && titleTextElm) { await setImageElm(targetDocument, titleTextElm); } removeLoading(linkElm); }; const searchLinkElements = (targetElm) => { const linkElms = targetElm.querySelectorAll(WHITE_LIST_SELECTORS); if (!linkElms.length) return; for (const linkElm of linkElms) { if (!(linkElm instanceof HTMLElement)) continue; setLoading(linkElm); void insertURLData(linkElm); } }; const mutationLinkElements = async (mutations) => { for (const mutation of mutations) { for (const addedNode of mutation.addedNodes) { if (!(addedNode instanceof HTMLElement)) continue; searchLinkElements(addedNode); } } }; const threadElm = document.querySelector('.thre'); if (threadElm) { searchLinkElements(threadElm); const observer = new MutationObserver(mutationLinkElements); observer.observe(threadElm, { childList: true, }); } })();