您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Loads all geeklist items into a single view simmilar to Peyo61's external tool.
当前为
// ==UserScript== // @name Board Game Geek - One page auction geeklists // @namespace http://tampermonkey.net/ // @version 0.6 // @description Loads all geeklist items into a single view simmilar to Peyo61's external tool. // @author Kempeth @ boardgamegeek // @match https://boardgamegeek.com/geeklist/* // @icon https://cf.geekdo-static.com/icons/favicon2.ico // @grant GM_setValue // @grant GM_getValue // @license MIT // ==/UserScript== const FETCHLIMIT = 20000; var nodeBtnConvert; var nodeProgressFull; var nodeProgressEmpty; var nodeItemList; var nodeFirstItem; var geeklistid; var taggedItems = []; // list of ids var items = []; // data from api (function() { 'use strict'; //console.log('root function'); init(); })(); function retry(f) { window.setTimeout(f, 500); } function init() { var els = document.getElementsByTagName('gg-geeklist-page-ui'); if (els.length == 0) { retry(init); return; } els = els[0].getElementsByTagName('header'); if (els.length == 0) { retry(init); return; } els = els[0].getElementsByTagName('nav'); if (els.length == 0) { retry(init); return; } var el = els[0]; nodeBtnConvert = document.createElement('button'); nodeBtnConvert.innerHTML = "One Page Auction"; nodeBtnConvert.className = "btn btn-warning"; nodeBtnConvert.onclick = convert; el.append(nodeBtnConvert); } function setProgress(done, total) { var percentage = total > 0 ? (done * 100.0 / total) : 0; nodeProgressFull.style.width = percentage + '%'; nodeProgressEmpty.style.width = (100 - percentage) + '%'; nodeProgressFull.setAttribute('aria-valuemax', total); nodeProgressFull.setAttribute('aria-valuenow', done); if (percentage > 50) { nodeProgressFull.innerHTML = done + ' / ' + total; nodeProgressEmpty.innerHTML = ''; } else { nodeProgressEmpty.innerHTML = done + ' / ' + total; nodeProgressFull.innerHTML = ''; } } function setProgressAsync(done, total) { return new Promise(res => { setProgress(done, total); window.setTimeout(() => { res(); }, 0); }); } function convert() { var els = document.getElementsByTagName('gg-geeklist-page-ui'); if (els.length == 0) { retry(convert); return; } els = els[0].getElementsByTagName('div'); if (els.length == 0) { retry(convert); return; } var footerNav = els[0].getElementsByTagName('nav'); if (els.length == 0) { retry(convert); return; } footerNav = footerNav[footerNav.length - 1]; els = els[0].getElementsByTagName('header'); if (els.length == 0) { retry(convert); return; } els = els[0].getElementsByTagName('nav'); if (els.length == 0) { retry(convert); return; } var headerNav = els[0]; els = document.getElementsByTagName('gg-geeklist-items-ui'); if (els.length == 0) { retry(convert); return; } els = els[0].getElementsByTagName('div'); if (els.length == 0) { retry(convert); return; } nodeItemList = els[0]; // remove header items for (var child of headerNav.childNodes) { if (child.tagName != 'GG-THUMBS-GEEKGOLD-GIVEN' && child.tagName != 'GG-SUBSCRIPTION-BUTTON') { headerNav.removeChild(child); } } // add header items var progress = document.createElement('div'); progress.className = "progress"; progress.style.height = "32px"; progress.style.width = "256px"; headerNav.appendChild(progress); nodeProgressFull = document.createElement('div'); nodeProgressFull.className = "progress-bar"; nodeProgressFull.setAttribute('role', 'progressbar'); nodeProgressFull.setAttribute('aria-valuemin', 0); nodeProgressFull.setAttribute('aria-valuemax', 0); nodeProgressFull.setAttribute('aria-valuenow', 0); nodeProgressFull.style.width = '0%'; nodeProgressFull.innerHTML = ''; progress.appendChild(nodeProgressFull); nodeProgressEmpty = document.createElement('div'); nodeProgressEmpty.className = "progress-bar bg-secondary"; nodeProgressEmpty.setAttribute('role', 'progressbar'); nodeProgressEmpty.style.width = '100%'; nodeProgressEmpty.innerHTML = '0 / ???'; progress.appendChild(nodeProgressEmpty); var btn = document.createElement('button'); btn.innerHTML = "Refresh"; btn.className = "btn btn-primary"; headerNav.appendChild(btn); // Sort Dropdown var ddSort = document.createElement('div'); ddSort.className = "dropdown"; headerNav.appendChild(ddSort); var ddSortBtn = document.createElement('button'); ddSortBtn.innerText = "Sort "; ddSortBtn.className = "btn btn-secondary dropdown-toggle"; ddSort.appendChild(ddSortBtn); var ddSortList = document.createElement('ul'); ddSortList.className = "dropdown-menu"; ddSort.appendChild(ddSortList); ddSortBtn.onclick = () => { ddSortList.style.display = (ddSortList.style.display == "block") ? "none" : "block"; }; ddSortBtn.onblur = () => { window.setTimeout(() => { ddSortList.style.display = "none"; }, 200); }; var ddSortPosted = document.createElement('li'); ddSortPosted.innerHTML = "<button type=\"button\" class=\"dropdown-item\">Posted</button>"; ddSortPosted.onclick = () => { console.log('sorting by default', new Date()); sortBy(''); }; ddSortList.appendChild(ddSortPosted); var ddSortName = document.createElement('li'); ddSortName.innerHTML = "<button type=\"button\" class=\"dropdown-item\">Name</button>"; ddSortName.onclick = () => { console.log('sorting by name', new Date()); sortBy('name'); }; ddSortList.appendChild(ddSortName); // Filter Dropdown var ddFilter = document.createElement('div'); ddFilter.className = "dropdown"; headerNav.appendChild(ddFilter); var ddFilterBtn = document.createElement('button'); ddFilterBtn.innerText = "Filter "; ddFilterBtn.className = "btn btn-secondary dropdown-toggle"; ddFilter.appendChild(ddFilterBtn); var ddFilterList = document.createElement('ul'); ddFilterList.className = "dropdown-menu"; ddFilter.appendChild(ddFilterList); ddFilterBtn.onclick = () => { ddFilterList.style.display = (ddFilterList.style.display == "block") ? "none" : "block"; }; ddFilterBtn.onblur = () => { window.setTimeout(() => { ddFilterList.style.display = "none"; }, 200); }; var ddFilterBoardgames = document.createElement('li'); ddFilterBoardgames.className = "dropdown-item"; ddFilterBoardgames.innerHTML = "<input class=\"form-check-input\" type=\"checkbox\" value=\"\" checked id=\"filterBoardgames\"> " + "<label class=\"form-check-label\" for=\"filterBoardgames\">Boardgames</label>"; ddFilterList.appendChild(ddFilterBoardgames); document.getElementById('filterBoardgames').onclick = () => { window.setTimeout(() => { filterList(); }, 400); }; var ddFilterExpansions = document.createElement('li'); ddFilterExpansions.className = "dropdown-item"; ddFilterExpansions.innerHTML = "<input class=\"form-check-input\" type=\"checkbox\" value=\"\" checked id=\"filterExpansions\"> " + "<label class=\"form-check-label\" for=\"filterExpansions\">Expansions</label>"; ddFilterList.appendChild(ddFilterExpansions); document.getElementById('filterExpansions').onclick = () => { window.setTimeout(() => { filterList(); }, 400); }; var ddFilterDivider = document.createElement('li'); ddFilterDivider.innerHTML = "<hr class=\"dropdown-divider\">"; ddFilterList.appendChild(ddFilterDivider); var ddFilterUntagged = document.createElement('li'); ddFilterUntagged.className = "dropdown-item"; ddFilterUntagged.innerHTML = "<input class=\"form-check-input\" type=\"checkbox\" value=\"\" checked id=\"filterUntagged\"> " + "<label class=\"form-check-label\" for=\"filterUntagged\">Untagged</label>"; ddFilterList.appendChild(ddFilterUntagged); document.getElementById('filterUntagged').onclick = () => { window.setTimeout(() => { filterList(); }, 400); }; var ddFilterTagged = document.createElement('li'); ddFilterTagged.className = "dropdown-item"; ddFilterTagged.innerHTML = "<input class=\"form-check-input\" type=\"checkbox\" value=\"\" checked id=\"filterTagged\"> " + "<label class=\"form-check-label\" for=\"filterTagged\">Tagged</label>"; ddFilterList.appendChild(ddFilterTagged); document.getElementById('filterTagged').onclick = () => { window.setTimeout(() => { filterList(); }, 400); }; // remove convert button nodeBtnConvert.parentNode.removeChild(nodeBtnConvert); // remove geeklist items nodeItemList.innerHTML = ''; nodeFirstItem = nodeItemList.childNodes[0]; nodeItemList.className = "tw-grid tw-gap-2"; // remove footer nav //footerNav.parentNode.removeChild(footerNav); footerNav.innerHTML = ''; geeklistid = window.location.pathname.split('/')[2]; getTaggedItems(); items = []; fetchPage(1); } function fetchPage(page) { // https://api.geekdo.com/api/listitems?page=1&listid=301669 var url = "https://api.geekdo.com/api/listitems?page=" + page + "&listid=" + geeklistid; fetch(url) .then(response => response.json()) .then( data => { var nr = (data.pagination.pageid - 1) * data.pagination.perPage; for (var item of data.data) { items[nr] = item; nr++; makeRow(nr, item); } setProgress(nr, data.pagination.total); if (data.data.length < data.pagination.perPage) { //console.log("last page"); } else if (page < FETCHLIMIT) { //console.log("more pages"); fetchPage(page + 1); } } ); } function makeRow(nr, item) { var row = document.createElement('span'); row.className = "tw-relative tw-flex tw-flex-wrap tw-items-center tw-gap-x-1.5 tw-gap-y-2 tw-rounded-md tw-border tw-border-gray-400 tw-bg-gray-100 tw-p-2"; const thingtype = item.item.href.split('/')[1]; const bodylower = item.body.toLowerCase(); const notsold = bodylower.includes("not sold"); const soldto = bodylower.includes("sold to"); const sold = bodylower.includes("sold"); const auctionclosed = bodylower.includes("auction closed"); var struckout = 0; var struckoutstart = bodylower.indexOf("[-]"); var struckoutend = bodylower.indexOf("[/-]"); while (struckoutstart >= 0 && struckoutend >= 0) { struckout += struckoutend - struckoutstart; struckoutstart = bodylower.indexOf("[-]", struckoutstart + 1); struckoutend = bodylower.indexOf("[/-]", struckoutend + 1); } var tagButton = document.createElement("button"); if (taggedItems.includes(item.id)) { tagButton.className = "btn btn-sm btn-primary"; tagButton.innerText = "Tagged"; } else { tagButton.className = "btn btn-sm btn-outline-primary"; tagButton.innerText = "Tag"; } tagButton.onclick = () => { toggleTaggedItem(tagButton, item.id); }; row.appendChild(tagButton); var tagGeeklistLink = document.createElement("a"); tagGeeklistLink.href = "/geeklist/" + geeklistid + "/test?itemid=" + item.id + "#" + item.id; tagGeeklistLink.target = "_blank"; tagGeeklistLink.innerText = nr + "."; row.appendChild(tagGeeklistLink); var txtThingType = document.createTextNode(thingtype == "boardgame" ? "Boardgame" : thingtype == "boardgameexpansion" ? "Expansion" : "Other"); row.appendChild(txtThingType); var tagGeeklistItemLink = document.createElement("a"); tagGeeklistItemLink.href = item.item.href; tagGeeklistItemLink.target = "_blank"; tagGeeklistItemLink.style.maxWidth = "50%"; tagGeeklistItemLink.style.textOverflow = "ellipsis"; tagGeeklistItemLink.style.overflow = "hidden"; tagGeeklistItemLink.style.whiteSpace = "nowrap"; tagGeeklistItemLink.innerText = item.item.name; row.appendChild(tagGeeklistItemLink); var tagState = document.createElement("span"); if (notsold) { tagState.innerText = "not sold"; tagState.className = "badge bg-danger"; } else if (soldto || sold) { tagState.innerText = "sold"; tagState.className = "badge bg-danger"; } else if (auctionclosed || struckout > 100) { tagState.innerText = "closed"; tagState.className = "badge bg-secondary"; } else { tagState.innerText = "open"; tagState.className = "badge bg-success"; } row.appendChild(tagState); var spanRight = document.createElement('span'); spanRight.className = "tw-ml-auto tw-hidden tw-pr-0.5 tw-text-sm tw-text-muted md:tw-inline"; row.appendChild(spanRight); var btnPreview = document.createElement('button'); btnPreview.className = "btn btn-sm text-bg-secondary"; btnPreview.innerText = "Preview"; btnPreview.onclick = () => { if (btnPreview.data != null) { btnPreview.data.parentNode.removeChild(btnPreview.data); btnPreview.data = null; } else { makePreview(item, btnPreview); } }; spanRight.appendChild(btnPreview); nodeItemList.insertBefore(row, nodeFirstItem); item.node = row; } function makePreview(item, btn) { var gli = document.createElement('div'); gli.className = "geeklist-item tw-grid tw-items-start tw-gap-2.5 sm:tw-grid-cols-[minmax(auto,_185px)_minmax(65%,_1fr)] lg:tw-gap-3 lg:tw-gap-x-4"; item.node.parentNode.insertBefore(gli, item.node.nextSibling); btn.data = gli; const gliImg = makeItemImage(item); gli.appendChild(gliImg); var gliDiv = document.createElement('div'); gliDiv.className = "tw-min-w-0 sm:tw-mt-0"; gli.appendChild(gliDiv); const gliDivBody = makeItemBody(item); gliDiv.appendChild(gliDivBody); } function makeItemUser(item) { } function makeItemBody(item) { var body = document.createElement('div'); var parser = new DOMParser(); var xmlDoc = parser.parseFromString(item.bodyXml, "text/xml"); for (var child of xmlDoc.firstChild.childNodes) { console.log(child); if (child.nodeName == 'safehtml') { var safehtml = document.createElement('div'); safehtml.innerHTML = child.firstChild.data; body.appendChild(safehtml); } else { var other = document.createElement('div'); other.className = "alert alert-warning"; other.innerText = "displaying " + child.nodeName + " is not yet supported by one page auction userscript."; body.appendChild(other); } } return body; } function makeItemImage(item) { var gliImg = document.createElement('a'); gliImg.className = "geeklist-item__img-link tw-relative tw-block tw-overflow-hidden tw-rounded-md tw-bg-gray-100"; gliImg.href = item.linkedImage.href; var gliImgDiv = document.createElement('div'); gliImgDiv.className = "tw-h-44 tw-w-full sm:tw-aspect-w-1 sm:tw-aspect-h-1"; gliImg.appendChild(gliImgDiv); var gliImgDivImg = document.createElement('img'); gliImgDivImg.className = "img-fluid geeklist-item__img tw-absolute tw-w-full tw-h-full tw-p-2.5 md:tw-p-3.5 tw-object-contain tw-z-10"; gliImgDivImg.loading = "lazy"; gliImgDivImg.style.display = "inherit"; gliImgDivImg.src = item.item.imageSets.square100.src; gliImgDivImg.srcset = item.item.imageSets.square100.src + " 1x," + item.item.imageSets.square100["src@2x"] + " 2x"; gliImgDivImg.alt = item.linkedImage.alt; gliImgDivImg.sizes = ""; gliImgDiv.appendChild(gliImgDivImg); gliImgDivImg = document.createElement('img'); gliImgDivImg.className = "img-fluid tw-absolute tw-w-full tw-h-full tw-object-cover tw-scale-125 tw-opacity-30 tw-blur-xl tw-bg-white"; gliImgDivImg.loading = "lazy"; gliImgDivImg.style.display = "inherit"; gliImgDivImg.src = item.item.imageSets.square100.src; gliImgDivImg.srcset = item.item.imageSets.square100.src + " 1x," + item.item.imageSets.square100["src@2x"] + " 2x"; gliImgDivImg.sizes = ""; gliImgDiv.appendChild(gliImgDivImg); gliImgDiv = document.createElement('div'); gliImgDiv.className = "tw-absolute tw-right-2 tw-bottom-2 tw-z-20"; gliImgDiv.innerHTML = "<gg-rating-indicator tooltip=\"Avg. Rating\" shape=\"hex\" size=\"md\">" + "<span container=\"body\" class=\"hex rating--" + Math.floor(item.stats.average) + " tw-flex tw-font-semibold tw-h-[var(--hex-h)] tw-items-center tw-justify-center tw-text-white tw-text-xs tw-w-[var(--hex-w)]\"" + " style=\"background:" + getColorFromRating(item.stats.average) + "; --hex-w: 1.625rem; --hex-h: 1.875rem; -webkit-clip-path: polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%); clip-path: polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%);\">" + "<span class=\"tw-pb-px\"> " + (Math.round(item.stats.average * 10) / 10) + " </span>" + "</span>" + "</gg-rating-indicator>"; gliImg.appendChild(gliImgDiv); return gliImg; } function getColorFromRating(rating) { const i = Math.floor(rating); switch (i) { case 1: case 2: return "#b2151f"; case 3: case 4: return "#d71925"; case 5: case 6: return "#5369a2"; case 7: return "#1978b3"; case 8: return "#1d804c"; case 9: case 10: return "#186b40"; default: return "#666e75"; } } function encodeWithTextNode(htmlstring) { let textarea = document.createElement('textarea'); let text = document.createTextNode(htmlstring); textarea.appendChild(text); return textarea.innerHTML; } function getTaggedItems() { taggedItems = JSON.parse(GM_getValue("TaggedItems_" + geeklistid, "[]")); } function toggleTaggedItem(btn, itemid) { if (taggedItems.includes(itemid)) { taggedItems = taggedItems.filter(i => i != itemid); btn.className = "btn btn-sm btn-outline-primary"; btn.innerText = "Tag"; } else { taggedItems[taggedItems.length] = itemid; btn.className = "btn btn-sm btn-primary"; btn.innerText = "Tagged"; } GM_setValue("TaggedItems_" + geeklistid, JSON.stringify(taggedItems)); } async function sortBy(type) { await setProgressAsync(0, items.length); var sorted; if (type == null || type == '' || type == 'default') { sorted = items; } else if (type == 'name') { sorted = [...items]; sorted = sorted.sort((a,b) => { var r = a.item.name > b.item.name ? 1 : -1; return r; }); } sortChunks(sorted, 0); } function sortChunks(sorted, start) { sortAChunk(sorted, start) .then(async data => { //console.log(data.next, data.sorted.length); await setProgressAsync(data.next, data.sorted.length); if (data.next >= data.sorted.length) { console.log("sorting finished", new Date()); } else { sortChunks(data.sorted, data.next); } } ); } function sortAChunk(sorted, start) { const chunk = 250; return new Promise(res => { window.setTimeout(() => { //console.log(start); for (var i = 0; i < chunk && start + i < sorted.length; i++) { var item = sorted[start + i]; nodeItemList.removeChild(item.node); nodeItemList.insertBefore(item.node, nodeFirstItem); } res({ sorted: sorted, next: start + i }); }, 0); }); } function filterList() { console.log("filtering list"); const fBg = document.getElementById('filterBoardgames').checked; const fEx = document.getElementById('filterExpansions').checked; const fUT = document.getElementById('filterUntagged').checked; const fTg = document.getElementById('filterTagged').checked; for (var i = 0; i < items.length; i++) { var show = true; var item = items[i]; const thingtype = item.item.href.split('/')[1]; const tagged = taggedItems.includes(item.id); if (!fBg && thingtype == 'boardgame') show = false; if (!fEx && thingtype == 'boardgameexpansion') show = false; if (!fUT && !tagged) show = false; if (!fTg && tagged) show = false; var cls = (show ? "tw-relative tw-flex tw-flex-wrap tw-items-center tw-gap-x-1.5 tw-gap-y-2 tw-rounded-md tw-border tw-border-gray-400 tw-bg-gray-100 tw-p-2" : "tw-relative d-none tw-flex-wrap tw-items-center tw-gap-x-1.5 tw-gap-y-2 tw-rounded-md tw-border tw-border-gray-400 tw-bg-gray-100 tw-p-2"); item.node.className = cls; /*if ((i + 1) % 100 == 0 || i == items.length - 1) { setProgress(i+1, items.length); }*/ } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址