您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows if an Itch.io link has been claimed or not
当前为
// ==UserScript== // @name Itch.io Web Integration // @namespace Lex@GreasyFork // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @version 0.1.4 // @author Lex // @description Shows if an Itch.io link has been claimed or not // @connect itch.io // ==/UserScript== // TODO // Add ability to claim the game with one click (function(){ 'use strict'; const INVALIDATION_TIME = 5*60*60*1000; // 5 hour cache time const ITCH_GAME_CACHE_KEY = 'ItchGameCache'; var ItchGameCache; // Promise wrapper for GM_xmlhttpRequest const Request = details => new Promise((resolve, reject) => { details.onerror = details.ontimeout = reject; details.onload = resolve; GM_xmlhttpRequest(details); }); function _clearItchCache() { ItchGameCache = {}; _saveItchCache(); } function loadItchCache() { ItchGameCache = JSON.parse(GM_getValue(ITCH_GAME_CACHE_KEY) || '{}'); } function _saveItchCache() { if (ItchGameCache === undefined) return; GM_setValue(ITCH_GAME_CACHE_KEY, JSON.stringify(ItchGameCache)); } function setItchGameCache(key, game) { loadItchCache(); // refresh our cache in case another tab has edited it ItchGameCache[key] = game; _saveItchCache(); } function deleteItchGameCache(key) { if (key === undefined) return; loadItchCache(); delete ItchGameCache[key]; _saveItchCache(); } function getItchGameCache(link) { if (!ItchGameCache) loadItchCache(); if (Object.prototype.hasOwnProperty.call(ItchGameCache, link)) { return ItchGameCache[link]; } return null; } // Parses a DOM into a game object function parsePage(url, dom) { // Gets the inner text of an element if it can be found otherwise returns undefined const txt = query => { const e = dom.querySelector(query); return e && e.innerText.trim(); }; const game = {}; game.cachetime = (new Date()).getTime(); game.url = url; game.isOwned = dom.querySelector(".purchase_banner_inner .key_row .ownership_reason") !== null; game.isClaimable = [...dom.querySelectorAll(".buy_btn")].filter(e => e.innerText == "Download or claim").length > 0; game.isFree = [...dom.querySelectorAll("span[itemprop=price]")].filter(e => e.innerText == "$0.00 USD").length > 0; game.hasFreeDownload = [...dom.querySelectorAll("a.download_btn,a.buy_btn")].filter(e => e.innerText == "Download" || e.innerText == "Download Now").length > 0; game.original_price = txt("span.original_price"); game.price = txt("span[itemprop=price]"); game.saleRate = txt(".sale_rate"); return game; } // Sends an XHR request and parses the results into a game object async function fetchItchGame(url) { console.log("Fetching url: " + url); const response = await Request({method: "GET", url: url}); if (response.status != 200) { console.log(`Error ${response.status} fetching page ${url}`); return null; } const parser = new DOMParser(); const dom = parser.parseFromString(response.responseText, 'text/html'); return parsePage(url, dom); } // Loads an itch game from cache or fetches the page if needed async function getItchGame(url) { let game = getItchGameCache(url); if (game !== null) { const isExpired = (new Date()).getTime() - game.cachetime > INVALIDATION_TIME; if (isExpired) { game = null; } } if (game === null) { game = await fetchItchGame(url); if (game !== null) setItchGameCache(url, game); } return game; } // Appends the isOwned tag to an anchor link function appendTags(a, game) { const div = document.createElement("div"); a.after(div); let ownMark = ''; if (game.isOwned) { ownMark = `<span title="Game is already claimed on itch.io">✔️</span>`; } else { if (!game.isClaimable) { if (game.hasFreeDownload) { ownMark = `<span title="Game is a free download but not claimable">⛔</span>`; } else if (game.price) { ownMark = `<span title="🛒 Game costs ${game.price}">🛒</span>`; } else { ownMark = `<span title="Status unknown">❓</span>`; } } else { const origPrice = game.original_price ? ` 🛒 Original price: ${game.original_price} 💸 Current Price: ${game.price}` : ''; ownMark = `<span title="Game is claimable but you haven't claimed it.${origPrice}">❌</span>`; } } div.outerHTML = `<div style="margin-left: 5px; background:rgb(200,200,200); border-radius: 5px; display: inline-block;">${ownMark}</div>`; } function addClickHandler(a) { a.addEventListener('mouseup', event => { deleteItchGameCache(event.target.href); }); } // Handles an itch.io link on a page async function handleLink(a) { addClickHandler(a); const game = await getItchGame(a.href); if (game !== null) appendTags(a, game); } function isGameUrl(url) { return /^https:\/\/[^.]+\.itch\.io\/[^/]+$/.test(url); } // Finds all the itch.io links on the current page function getItchLinks() { let links = [...document.querySelectorAll("a[href*='itch.io/']")]; links = links.filter(a => isGameUrl(a.href)); links = links.filter(a => !a.classList.contains("return_link")); links = links.filter(a => { const t = a.textContent.trim(); return t !== "" && t !== "GIF"; }); return links; } function handlePage() { if (isGameUrl(window.location.href)) { const game = parsePage(window.location.href, document); setItchGameCache(window.location.href, game); } const as = getItchLinks(); as.forEach(handleLink); } handlePage(); })();