缓存链接时,同时抓取插画的来源链接和标题,并在牌堆导出时包含所有信息,用 \n 分隔。小键盘可以实现翻页,有更换需求请自行处理。可以打tag!理论上适配所有pixiv的界面(包括背景图)
// ==UserScript==
// @name Pixiv图片牌堆生成器
// @version 1.37
// @description 缓存链接时,同时抓取插画的来源链接和标题,并在牌堆导出时包含所有信息,用 \n 分隔。小键盘可以实现翻页,有更换需求请自行处理。可以打tag!理论上适配所有pixiv的界面(包括背景图)
// @author Yog-Sothoth
// @match *://www.pixiv.net/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @license MIT
// @run-at document-idle
// @namespace https://greasyfork.org/users/1397928
// ==/UserScript==
(function() {
'use strict';
const STORAGE_KEY = 'pixivCatTaggedLinks_JSON';
const CACHE_NAME = 'PixivCat_Tagged_Export.json';
const PIXIV_BASE_URL = 'https://www.pixiv.net';
const IMG_SELECTOR_LIST = 'img[src*="i.pximg.net/c/"]:not([data-tag-processed])';
const IMG_SELECTOR_ARTWORK = 'img[src*="i.pximg.net/img-original/img"]:not([data-tag-processed])';
const ANCESTOR_LEVEL = 6;
const IS_ARTWORK_PAGE = window.location.pathname.includes('/artworks/');
const IS_TAG_SEARCH_PAGE = window.location.pathname.startsWith('/tags/') && window.location.pathname.includes('/artworks');
let linkCacheIndex = new Map();
function parseImgSrc(src) {
const regex = /img\/(\d{4}\/\d{2}\/\d{2}\/\d{2}\/\d{2}\/\d{2})\/(\d+)_p(\d+)/;
const match = src.match(regex);
if (match) {
return {
timestamp: match[1],
illustId: match[2],
pageIndex: parseInt(match[3], 10),
pageString: `p${match[3]}`,
linkKey: `${match[2]}_p${match[3]}`
};
}
if (IS_ARTWORK_PAGE) {
const parts = src.match(/img\/(\d{4}\/\d{2}\/\d{2}\/\d{2}\/\d{2}\/\d{2})\/(\d+)_p(\d+)/);
const artworkMatch = window.location.pathname.match(/\/artworks\/(\d+)/);
if (parts && artworkMatch) {
const illustId = artworkMatch[1];
const pageString = `p${parts[3]}`;
return {
timestamp: parts[1],
illustId: illustId,
pageIndex: parseInt(parts[3], 10),
pageString: pageString,
linkKey: `${illustId}_${pageString}`
};
}
}
return null;
}
function buildCacheIndex() {
linkCacheIndex.clear();
const data = getCachedData();
for (const tag in data) {
for (const item of data[tag]) {
try {
const parsedItem = JSON.parse(item);
const realUrl = parsedItem.catLink;
const parsedInfo = parseImgSrc(realUrl);
if (!parsedInfo) continue;
const linkKey = parsedInfo.linkKey;
if (!linkCacheIndex.has(linkKey)) {
linkCacheIndex.set(linkKey, { realUrl: realUrl, tags: [] });
}
linkCacheIndex.get(linkKey).tags.push(tag);
} catch (e) {
}
}
}
}
function formatToCQImage(cachedItem) {
try {
const data = JSON.parse(cachedItem);
return (
`[CQ:image,file=${data.catLink}]` +
`${data.sourceLink}` +
`\n${data.title}`
);
} catch (e) {
return `[CQ:image,file=N/A] (Error reading source info for: ${cachedItem})`;
}
}
function getIllustrationSourceInfo(imgElement) {
if (IS_ARTWORK_PAGE) {
const urlMatch = window.location.pathname.match(/\/artworks\/(\d+)/);
const sourceLink = urlMatch ? PIXIV_BASE_URL + urlMatch[0] : 'Source: N/A (URL)';
let title = document.title.replace(/ | - pixiv$/, '').trim();
if (!title || title.includes('pixiv')) {
title = imgElement.alt.trim() || 'No Title Found (Alt)';
}
return { sourceLink, title };
} else {
try {
const aElement = imgElement.closest('li')
?.querySelector(':scope > div:first-child > div:nth-of-type(2) > a[href*="/artworks/"]');
if (!aElement) {
return { sourceLink: 'Source: N/A (Selector)', title: 'Title: N/A (Selector)' };
}
const relativeHref = aElement.getAttribute('href') || '';
const sourceLink = relativeHref.startsWith(PIXIV_BASE_URL) ? relativeHref : PIXIV_BASE_URL + relativeHref;
const title = aElement.textContent.trim() || imgElement.alt || 'No Title Found';
return { sourceLink, title };
} catch (e) {
console.error("Error retrieving list source info:", e);
}
return { sourceLink: 'Source: Error', title: 'Title: Error' };
}
}
function getCachedData() {
try {
return JSON.parse(GM_getValue(STORAGE_KEY, '{}'));
} catch (e) {
return {};
}
}
function setCachedData(data) {
GM_setValue(STORAGE_KEY, JSON.stringify(data));
}
function getLinkTags(linkKey) {
return linkCacheIndex.get(linkKey)?.tags || [];
}
function isLinkCached(linkKey) {
return linkCacheIndex.has(linkKey);
}
function addLinkToTag(itemJsonString, realUrl, tag) {
const data = getCachedData();
const finalTag = tag || '未分类';
let isAdded = false;
const parsedInfo = parseImgSrc(realUrl);
if (!parsedInfo) {
console.error("Could not parse realUrl in addLinkToTag", realUrl);
return false;
}
const linkKey = parsedInfo.linkKey;
if (!data[finalTag]) {
data[finalTag] = [];
}
const existingInTag = data[finalTag].some(item => {
try {
return JSON.parse(item).catLink === realUrl;
} catch (e) {
return false;
}
});
if (!existingInTag) {
data[finalTag].push(itemJsonString);
setCachedData(data);
isAdded = true;
if (!linkCacheIndex.has(linkKey)) {
linkCacheIndex.set(linkKey, { realUrl: realUrl, tags: [] });
}
const tags = linkCacheIndex.get(linkKey).tags;
if (!tags.includes(finalTag)) {
tags.push(finalTag);
}
}
return isAdded;
}
function removeLinkFromAllCache(linkKey) {
const cacheEntry = linkCacheIndex.get(linkKey);
if (!cacheEntry) return false;
const realUrl = cacheEntry.realUrl;
const data = getCachedData();
let removed = false;
for (const tag in data) {
const originalLength = data[tag].length;
data[tag] = data[tag].filter(item => {
try {
return JSON.parse(item).catLink !== realUrl;
} catch (e) {
return true;
}
});
if (data[tag].length < originalLength) {
removed = true;
}
if (data[tag].length === 0) {
delete data[tag];
}
}
if (removed) {
setCachedData(data);
linkCacheIndex.delete(linkKey);
}
return removed;
}
function clearCache() {
if (confirm('确定要清空所有缓存的 Pixiv Cat 链接和标签吗?')) {
GM_deleteValue(STORAGE_KEY);
linkCacheIndex.clear();
alert('缓存已清空。页面需要刷新以清除按钮状态。');
location.reload();
}
}
function createTagButton(isCached) {
const button = document.createElement('div');
button.style.cssText = 'position:absolute;top:50%;right:0;transform:translateY(-50%);width:24px;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;color:white;font-weight:bold;cursor:pointer;z-index:1000;transition:background-color .2s,transform .1s;font-family:sans-serif;font-size:16px;';
updateButtonState(button, isCached);
return button;
}
function updateButtonState(button, isCached) {
button.style.backgroundColor = isCached ? '#4CAF50' : '#2196F3';
button.innerHTML = '+';
}
function updateButtonTitle(button) {
const linkKey = button.linkKey;
if (!linkKey) {
button.title = '无法解析图片ID';
button.style.backgroundColor = '#FF0000';
return;
}
const tags = getLinkTags(linkKey);
let title = '';
if (tags.length === 0) {
title = '未被任何标签缓存 | 单击添加 | 双击取消所有标签';
} else {
title = `已缓存到: ${tags.join(', ')} | 单击添加 | 双击取消所有标签`;
}
button.title = title;
updateButtonState(button, tags.length > 0);
}
function createTagInputMenu(button, imgElement, linkKey, illustId, pageIndex) {
const cachedData = getCachedData();
const existingTags = Object.keys(cachedData);
const sourceInfo = getIllustrationSourceInfo(imgElement);
const menu = document.createElement('div');
menu.id = 'tag-select-menu';
menu.style.width = '280px';
menu.style.background = '#333333';
menu.style.border = '1px solid #222222';
menu.style.boxShadow = '0 4px 8px rgba(0,0,0,0.5)';
menu.style.padding = '10px';
menu.style.borderRadius = '4px';
menu.style.zIndex = '10001';
menu.style.color = 'white';
menu.style.boxSizing = 'border-box';
menu.style.textAlign = 'left';
menu.style.fontSize = '13px';
const currentTags = linkKey ? getLinkTags(linkKey).join(', ') : '无';
const infoText = linkKey ? `(已存在标签: ${currentTags || '无'})` : '(新图片, 未缓存)';
const info = document.createElement('div');
info.innerHTML = `
<strong style="color: #f0f0f0;">来源:</strong> <span style="word-break: break-all;">${sourceInfo.sourceLink}</span><br>
<strong style="color: #f0f0f0;">标题:</strong> ${sourceInfo.title}
<hr style="border-top: 1px solid #999; margin: 8px 0;">
<i style="font-size: 11px;">${infoText}</i>
`;
menu.appendChild(info);
const input = document.createElement('input');
input.type = 'text';
input.placeholder = '选择或输入新标签名';
input.setAttribute('list', 'pixiv-tag-list');
input.style.width = '100%';
input.style.padding = '4px';
input.style.marginTop = '10px';
input.style.marginBottom = '8px';
input.style.border = '1px solid #495057';
input.style.backgroundColor = '#f8f9fa';
input.style.color = '#212529';
input.style.borderRadius = '3px';
input.style.boxSizing = 'border-box';
const datalist = document.createElement('datalist');
datalist.id = 'pixiv-tag-list';
existingTags.forEach(tag => {
const option = document.createElement('option');
option.value = tag;
datalist.appendChild(option);
});
const confirmButton = document.createElement('button');
confirmButton.textContent = '添加并缓存到标签';
const baseButtonColor = '#4CAF50';
const hoverButtonColor = '#45a049';
const disabledButtonColor = '#555';
confirmButton.style.width = '100%';
confirmButton.style.background = baseButtonColor;
confirmButton.style.color = 'white';
confirmButton.style.border = 'none';
confirmButton.style.padding = '5px 10px';
confirmButton.style.cursor = 'pointer';
confirmButton.style.borderRadius = '3px';
confirmButton.style.transition = 'background-color .2s';
confirmButton.style.marginTop = '9px';
confirmButton.onmouseover = () => {
if (!confirmButton.disabled) {
confirmButton.style.background = hoverButtonColor;
}
};
confirmButton.onmouseout = () => {
if (!confirmButton.disabled) {
confirmButton.style.background = baseButtonColor;
}
};
menu.appendChild(datalist);
menu.appendChild(input);
menu.appendChild(confirmButton);
confirmButton.onclick = async () => {
input.style.borderColor = '#495057';
const tag = input.value.trim();
if (!tag) {
input.style.borderColor = 'red';
return;
}
if (!illustId) {
console.error("没有 illustId, 无法获取真实链接。");
alert("错误:无法从此图片解析 illustId。");
return;
}
confirmButton.disabled = true;
confirmButton.style.background = disabledButtonColor;
confirmButton.style.cursor = 'wait';
confirmButton.textContent = '正在获取真实链接...';
let finalUrlToCache;
try {
const response = await new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: `/ajax/illust/${illustId}/pages`,
onload: (res) => resolve(res),
onerror: (err) => reject(err)
});
});
const json = JSON.parse(response.responseText);
if (json.error || !json.body) throw new Error(json.message || 'API error');
const pageData = json.body[pageIndex];
if (!pageData) throw new Error(`Page index ${pageIndex} not found in API response`);
const realOriginalUrl = pageData.urls.original;
finalUrlToCache = realOriginalUrl.replace('i.pximg.net', 'i.pixiv.cat');
} catch (e) {
console.error('Failed to fetch real URL', e);
alert(`获取真实链接失败: ${e.message}\n请稍后再试。`);
confirmButton.disabled = false;
confirmButton.style.background = baseButtonColor;
confirmButton.style.cursor = 'pointer';
confirmButton.textContent = '添加并缓存到标签';
return;
}
confirmButton.disabled = false;
confirmButton.style.background = baseButtonColor;
confirmButton.style.cursor = 'pointer';
confirmButton.textContent = '添加并缓存到标签';
const itemToCache = JSON.stringify({
catLink: finalUrlToCache,
sourceLink: sourceInfo.sourceLink,
title: sourceInfo.title
});
addLinkToTag(itemToCache, finalUrlToCache, tag);
const newParsedInfo = parseImgSrc(finalUrlToCache);
if (newParsedInfo) {
button.linkKey = newParsedInfo.linkKey;
}
updateButtonTitle(button);
button.innerHTML = '✓';
setTimeout(() => {
button.innerHTML = '+';
}, 1000);
menu.remove();
};
setTimeout(() => {
const clickHandler = (e) => {
if (!menu.contains(e.target) && e.target !== button && e.target !== input) {
menu.remove();
document.removeEventListener('click', clickHandler);
}
};
document.addEventListener('click', clickHandler);
}, 10);
return menu;
}
function showTagInputMenu(button, imgElement, linkKey, illustId, pageIndex) {
if (document.getElementById('tag-select-menu')) {
document.getElementById('tag-select-menu').remove();
}
const menu = createTagInputMenu(button, imgElement, linkKey, illustId, pageIndex);
const rect = button.getBoundingClientRect();
document.body.appendChild(menu);
const menuWidth = menu.offsetWidth;
let left = rect.left - menuWidth - 10;
if (left < 10) {
left = rect.right + 10;
}
menu.style.position = 'fixed';
menu.style.top = `${rect.top + rect.height / 2}px`;
menu.style.left = `${left}px`;
menu.style.right = 'auto';
menu.style.background = '#333333';
menu.querySelector('input').focus();
}
function handleButtonClick(event) {
event.stopPropagation();
const button = event.currentTarget;
const img = button.imgElement;
const linkKey = button.linkKey;
const illustId = button.illustId;
const pageIndex = button.pageIndex;
//if (event.detail === 1) {
// showTagInputMenu(button, img, linkKey, illustId, pageIndex);
//}
}
function handleButtonMouseenter(event) {
// 阻止事件冒泡,尽管在这里可能不那么必要,但保持一致性
event.stopPropagation();
const button = event.currentTarget;
const img = button.imgElement;
const linkKey = button.linkKey;
const illustId = button.illustId;
const pageIndex = button.pageIndex;
// 直接调用显示菜单的函数
showTagInputMenu(button, img, linkKey, illustId, pageIndex);
}
function handleButtonDoubleClick(event) {
event.stopPropagation();
const button = event.currentTarget;
const linkKey = button.linkKey;
if (!linkKey) return;
const realUrl = linkCacheIndex.get(linkKey)?.realUrl || "未知链接 (Key: " + linkKey + ")";
if (confirm(`确定要从所有标签中移除该链接吗?\n(链接: ${realUrl})\n当前标签: ${getLinkTags(linkKey).join(', ') || '无'}`)) {
if (removeLinkFromAllCache(linkKey)) {
} else {
}
updateButtonTitle(button);
}
}
function handleButtonMouseOver(event) {
const button = event.currentTarget;
updateButtonTitle(button);
}
function findAncestor(element, n) {
let ancestor = element;
for (let i = 0; i < n; i++) {
if (ancestor) {
ancestor = ancestor.parentElement;
} else {
return null;
}
}
return ancestor;
}
function getIllustIdFromImg(img) {
if (IS_ARTWORK_PAGE) {
const match = window.location.pathname.match(/\/artworks\/(\d+)/);
if (match) return match[1];
}
try {
let ancestor = img.closest('li') || img.closest('a[href*="/artworks/"]');
if (ancestor && !ancestor.href) {
ancestor = ancestor.querySelector('a[href*="/artworks/"]');
}
if (ancestor && ancestor.href) {
const match = ancestor.href.match(/\/artworks\/(\d+)/);
if (match) return match[1];
}
let parent = img.parentElement;
for (let i = 0; i < 6; i++) {
if (!parent) break;
let aTag = parent.querySelector('a[href*="/artworks/"]');
if (aTag && aTag.href) {
const match = aTag.href.match(/\/artworks\/(\d+)/);
if (match) return match[1];
}
parent = parent.parentElement;
}
} catch(e) {}
const parsedInfo = parseImgSrc(img.src);
if (parsedInfo) return parsedInfo.illustId;
return null;
}
function processImages() {
let selectors = [IMG_SELECTOR_LIST];
if (IS_ARTWORK_PAGE) {
selectors.push(IMG_SELECTOR_ARTWORK);
}
let images = [];
selectors.forEach(selector => {
images.push(...document.querySelectorAll(selector));
});
images.forEach(img => {
img.setAttribute('data-tag-processed', 'true');
let targetAncestor;
if (IS_ARTWORK_PAGE) {
let aParent = img.closest('a');
if (aParent) {
targetAncestor = findAncestor(aParent, 2);
}
if (!targetAncestor) {
targetAncestor = img.parentElement;
if (targetAncestor && window.getComputedStyle(targetAncestor).position === 'static') {
targetAncestor = targetAncestor.parentElement;
}
}
} else {
targetAncestor = findAncestor(img, ANCESTOR_LEVEL);
}
if (!targetAncestor) {
return;
}
const originalSrc = img.src;
const parsedInfo = parseImgSrc(originalSrc);
const illustId = (parsedInfo ? parsedInfo.illustId : null) || getIllustIdFromImg(img);
const pageIndex = parsedInfo ? parsedInfo.pageIndex : 0;
const linkKey = parsedInfo ? parsedInfo.linkKey : (illustId ? `${illustId}_p${pageIndex}` : null);
const isCached = linkKey ? isLinkCached(linkKey) : false;
const button = createTagButton(isCached);
button.imgElement = img;
button.linkKey = linkKey;
button.illustId = illustId;
button.pageIndex = pageIndex;
if (IS_ARTWORK_PAGE) {
button.style.width = '34px';
button.style.height = '34px';
button.style.fontSize = '20px';
}
updateButtonTitle(button);
if (window.getComputedStyle(targetAncestor).position === 'static') {
targetAncestor.style.position = 'relative';
}
if (!targetAncestor.querySelector('div[data-state]')) {
//button.addEventListener('click', handleButtonClick);
button.addEventListener('mouseenter', handleButtonMouseenter);
button.addEventListener('dblclick', handleButtonDoubleClick);
button.addEventListener('mouseover', handleButtonMouseOver);
targetAncestor.appendChild(button);
}
});
}
function exportTagToJson(tagToExport) {
const data = getCachedData();
const linksData = data[tagToExport];
if (!linksData || linksData.length === 0) {
alert(`标签 "${tagToExport}" 下没有缓存链接可导出。`);
return;
}
const jsonContent = {
"_title": ["pixiv图片转载"],
"_author": ["Yog-Sothoth"],
"_date": [new Date().toISOString().slice(0, 10).replace(/-/g, '/')],
"_version": ["1.0"],
};
jsonContent[tagToExport] = linksData.map(formatToCQImage);
const content = JSON.stringify(jsonContent, null, 2);
const blob = new Blob([content], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${tagToExport}_${CACHE_NAME}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
const menu = document.getElementById('tag-export-menu');
if (menu) menu.remove();
alert(`标签 "${tagToExport}" 的 JSON 文件已生成并开始下载。`);
}
function exportAllTagsToJson() {
const data = getCachedData();
const allTags = Object.keys(data);
if (allTags.length === 0) {
alert('缓存中没有任何数据可导出。');
return;
}
const jsonContent = {
"_title": ["pixiv图片转载"],
"_author": ["Yog-Sothoth"],
"_date": [new Date().toISOString().slice(0, 10).replace(/-/g, '/')],
"_version": ["1.0"],
};
allTags.forEach(tag => {
const linksData = data[tag];
if (linksData && linksData.length > 0) {
jsonContent[tag] = linksData.map(formatToCQImage);
}
});
const content = JSON.stringify(jsonContent, null, 2);
const blob = new Blob([content], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `All_Tags_${CACHE_NAME}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
const menu = document.getElementById('tag-export-menu');
if (menu) menu.remove();
alert('包含所有标签的 JSON 文件已生成并开始下载。');
}
function createExportMenuUI() {
const existingMenu = document.getElementById('tag-export-menu');
if (existingMenu) {
existingMenu.remove();
return;
}
const data = getCachedData();
const tags = Object.keys(data).filter(tag => data[tag].length > 0);
if (Object.keys(data).length === 0) {
alert('缓存中没有可导出的标签。');
return;
}
const menu = document.createElement('div');
menu.id = 'tag-export-menu';
menu.style.cssText = 'position: fixed; top: 10%; left: 50%; transform: translateX(-50%); background: #f9f9f9; border: 1px solid #ccc; box-shadow: 0 5px 15px rgba(0,0,0,0.3); padding: 15px; border-radius: 8px; z-index: 9999; max-height: 80vh; overflow-y: auto;';
const title = document.createElement('h3');
title.textContent = '⬇️ 选择要导出的标签 (JSON)';
title.style.cssText = 'margin-top: 0; border-bottom: 1px solid #ddd; padding-bottom: 5px; color: #333;';
menu.appendChild(title);
const exportAllButton = document.createElement('button');
exportAllButton.textContent = '➡️ 导出所有标签 (JSON)';
exportAllButton.style.cssText = 'display: block; width: 100%; padding: 10px; margin: 10px 0; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; transition: background-color 0.2s;';
exportAllButton.onmouseover = () => exportAllButton.style.backgroundColor = '#218838';
exportAllButton.onmouseout = () => exportAllButton.style.backgroundColor = '#28a745';
exportAllButton.onclick = exportAllTagsToJson;
menu.appendChild(exportAllButton);
const separator = document.createElement('hr');
separator.style.cssText = 'border: 0; border-top: 1px solid #ddd; margin: 10px 0;';
menu.appendChild(separator);
if (tags.length > 0) {
tags.forEach(tag => {
const count = data[tag].length;
const button = document.createElement('button');
button.textContent = `${tag} (${count} links)`;
button.style.cssText = 'display: block; width: 100%; padding: 8px; margin: 5px 0; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; transition: background-color 0.2s;';
button.onmouseover = () => button.style.backgroundColor = '#0056b3';
button.onmouseout = () => button.style.backgroundColor = '#007bff';
button.onclick = () => exportTagToJson(tag);
menu.appendChild(button);
});
} else {
const noTagsText = document.createElement('p');
noTagsText.textContent = '没有找到单独的标签。';
noTagsText.style.cssText = 'color: #555; font-style: italic; text-align: center;';
menu.appendChild(noTagsText);
}
const closeButton = document.createElement('button');
closeButton.textContent = '关闭';
closeButton.style.cssText = 'display: block; width: 100%; padding: 8px; margin-top: 15px; background-color: #6c757d; color: white; border: none; border-radius: 4px; cursor: pointer;';
closeButton.onclick = () => menu.remove();
menu.appendChild(closeButton);
document.body.appendChild(menu);
}
function registerMenuCommands() {
GM_registerMenuCommand('⬇️ 导出标签链接为 JSON', createExportMenuUI);
GM_registerMenuCommand('🗑️ 清空所有缓存链接和标签', clearCache);
}
function quickPageTurn(step) {
const url = new URL(window.location.href);
const params = url.searchParams;
let currentPage = parseInt(params.get('p'), 10);
if (isNaN(currentPage) || currentPage < 1) {
currentPage = 1;
}
let newPage = currentPage + step;
if (newPage < 1) {
newPage = 1;
}
params.set('p', newPage);
const newUrl = url.origin + url.pathname + '?' + params.toString() + url.hash;
window.location.href = newUrl;
}
function handleKeydown(event) {
const activeElement = document.activeElement;
if (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA') {
return;
}
switch (event.code) {
case 'Numpad4':
event.preventDefault();
quickPageTurn(-1);
break;
case 'Numpad6':
event.preventDefault();
quickPageTurn(1);
break;
}
}
function observeDOMChanges() {
processImages();
setTimeout(processImages, 2000);
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
setTimeout(processImages, 100);
break;
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
buildCacheIndex();
registerMenuCommands();
observeDOMChanges();
window.addEventListener('keydown', handleKeydown);
})();