中文标签 | 界面优化 | 高清大图 | 键盘翻页 | 流体布局
// ==UserScript== // @name Yande.re 简体中文 // @namespace com.coderzhaoziwei.yandere // @version 2.1.47 // @author Coder Zhao [email protected] // @description 中文标签 | 界面优化 | 高清大图 | 键盘翻页 | 流体布局 // @homepage https://greasyfork.org/scripts/421970 // @license MIT // @match https://yande.re/* // @exclude https://yande.re/forum/* // @match https://konachan.com/* // @exclude https://konachan.com/forum/* // @match https://konachan.net/* // @exclude https://konachan.net/forum/* // @supportURL https://github.com/coderzhaoziwei/yande-re-chinese-patch/issues // @grant GM_download // ==/UserScript== /* eslint-env es2022 */ /* global jQuery:readonly */ /* global Vue:readonly */ /* global Vuetify:readonly */ /* global VueMasonry:readonly */ (function () { 'use strict'; const initStyle = function() { document.head.insertAdjacentHTML("beforeend", `<style> body { font-size: 12px; padding: 0 0.5rem; } body::-webkit-scrollbar { display: none; width: 0px !important; } /* 标题居中 */ div#header { margin: 0; } div#header > div#title { display: flex; place-content: center; margin: 0 !important; height: fit-content; } div#header > div#title > h2#site-title { display: flex !important; flex-direction: column; } div#header > div#title > h2#site-title > span { font-size: 12px; font-weight: normal; text-align: right; } div#header > div#main-menu { padding: 0 !important; margin: 0 !important; display: flex !important; justify-content: center; font-size: 14px; line-height: 2rem; height: 2rem; } div#header > div#main-menu > ul { margin: 0; } /* 通知 */ .status-notice { text-align: center; } /* 标签前缀 */ li.tag-type-artist a[href^="/post"]:not(.no-browser-link)::before { content: "[画师]"; } li.tag-type-copyright a[href^="/post"]:not(.no-browser-link)::before { content: "[原作]"; } li.tag-type-character a[href^="/post"]:not(.no-browser-link)::before { content: "[角色]"; } li.tag-type-circle a[href^="/post"]:not(.no-browser-link)::before { content: "[公司]"; } /* 图区 */ #post-list { display: flex; flex-direction: row; } #post-list > .sidebar { width: auto; max-width: 200px; flex: 0 0 auto; } #post-list > .content { width: auto; flex: 1 1 auto; } #post-list > div.lsidebar { display: none; } ul#post-list-posts { display: flex; flex-wrap: wrap; gap: 0.5rem; place-content: center; place-items: center; } ul#post-list-posts > li { width: fit-content !important; height: 100%; margin: 0 !important; border: none; } ul#post-list-posts > li > div.inner { width: auto !important; height: fit-content !important; } ul#post-list-posts > li > a.directlink { font-size: 12px; height: 12px; line-height: 12px; margin: 0; padding: 0; overflow: hidden; background: rgb(16, 16, 16); } ul#post-list-posts > li > a.directlink > span.directlink-res { display: inline; } ul#post-list-posts > li > a.directlink > span.directlink-info { display: none; } ul#post-list-posts > li > div.inner > a.thumb { height: auto; } ul#post-list-posts > li > div.inner > a.thumb > img.preview { margin: 0 !important; /* @konachan */ border: none; } /* 分页器 */ div#paginator { padding: 0; } div#paginator > div.pagination { line-height: 2rem; } /* 页脚 */ #content > div:nth-child(2) > div.sidebar { display: none; } #content div.footer { font-size: 14px; margin: 1rem; } /* show-left-bar */ .sidebar[show-left-bar=false] { display: none !important; } /* show-rating-e */ .javascript-hide[show-rating-e=true] { display: block !important; position: relative; } .javascript-hide[show-rating-e=true]::after { content: ""; position: absolute; width: 100%; height: 100%; top: 0; box-shadow: 0px 0px 12px rgb(255, 0, 0) inset; pointer-events: none; } /* show-image-hd */ #post-list-posts > li > .inner[show-image-hd="1"] { zoom: 2; } #post-list-posts > li > .inner[show-image-hd="2"] { zoom: 3; } #post-list-posts > li > .inner[show-image-hd="3"] { zoom: 4; } </style>`); }; const initHotKey = function() { window.addEventListener("keyup", function(event) { console.log('keyup:', event.key); if (/^(TEXTAREA|INPUT|SELECT|BUTTON)$/.test(document.activeElement.tagName)) return const prev = document.querySelector(".pagination>.previous_page") || jQuery("li:contains('Previous') a[href]")[0]; if (prev && (event.key == "ArrowLeft" || event.key == "a" || event.key == "A")) { prev.click(); return event.preventDefault() } const next = document.querySelector(".pagination>.next_page") || jQuery("li:contains('Next') a[href]")[0]; if (next && (event.key == "ArrowRight" || event.key === "d" || event.key == "D")) { next.click(); return event.preventDefault() } const show = document.querySelector("#png") || document.querySelector("#highres"); if (show && (event.key === "s" || event.key === "S")) { show.click(); return event.preventDefault() } const where = jQuery("li:contains('Source:') a")[0]; if (where && (event.key === "w" || event.key === "W")) { where.click(); return event.preventDefault() } }); const sidebar = document.querySelector("#post-list > div.sidebar") || document.querySelector("#post-view > div.sidebar"); if (sidebar) { sidebar.insertAdjacentHTML("beforeend", "<div>" + "<h5>快捷键说明</h5>" + "<div style='color: #ee8888'>上一页:A / ←</div>" + "<div style='color: #ee8888'>下一页:D / →</div>" + "<div style='color: #ee8888'>显示当前作品原图:S</div>" + "<div style='color: #ee8888'>显示当前作品来源:W</div>" + "</div>"); } }; class Post { constructor(data) { if (typeof data !== "object") data = {}; this.id = data.id || 0; this.score = data.score || 0; this.tags = data.tags || ""; this.source = data.source || ""; this.author = data.author || ""; this.creatorId = data.creator_id || 0; this.createdAt = data.created_at || 0; this.updatedAt = data.updated_at || 0; this.rating = data.rating || "s"; this.fileUrl = data.file_url || ""; this.fileExt = data.file_ext || ""; this.fileSize = data.file_size || 0; this.width = data.width || 0; this.height = data.height || 0; this.jpegUrl = data.jpeg_url || ""; this.jpegSize = data.jpeg_file_size || 0; this.jpegWidth = data.jpeg_width || 0; this.jpegHeight = data.jpeg_height || 0; this.sampleUrl = data.sample_url; this.sampleSize = data.sample_file_size || 0; this.sampleWidth = data.sample_width || 0; this.sampleHeight = data.sample_height || 0; this.previewUrl = data.preview_url; this.previewWidth = data.actual_preview_width || 0; this.previewHeight = data.actual_preview_height || 0; this.favorite = false; } get isRatingS() { return this.rating === "s" } get isRatingQ() { return this.rating === "q" } get isRatingE() { return this.rating === "e" } get aspectRatio() { return this.width / this.height } getSizeText(size) { if (size > 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + "MB" } if (size > 1024) { return (size / 1024).toFixed(2) + "KB" } return (size).toFixed(2) + "B" } get sampleSizeText() { return this.getSizeText(this.sampleSize) } get sampleDownloadText() { return `下载缩略图 ${this.sampleWidth}×${this.sampleHeight} [${this.sampleSizeText}]` } get sampleDownloadName() { return `${location.hostname}.${this.id}.${this.sampleWidth}x${this.sampleHeight}`.replace(/\./g, "_") } get jpegSizeText() { return this.getSizeText(this.jpegSize) } get jpegDownloadText() { return `下载高清图 ${this.jpegWidth}×${this.jpegHeight} [${this.jpegSizeText}]` } get jpegDownloadName() { return `${location.hostname}.${this.id}.${this.jpegWidth}x${this.jpegHeight}`.replace(/\./g, "_") } get fileSizeText() { return this.getSizeText(this.fileSize) } get fileDownloadText() { return `下载原文件 ${this.width}×${this.height} [${this.fileSizeText}] ${this.fileExt.toUpperCase()}` } get fileDownloadName() { return `${location.hostname}.${this.id}.${this.width}x${this.height}`.replace(/\./g, "_") } get createdTime() { const date = new Date(this.createdAt * 1000); return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get updatedTime() { const date = new Date(this.updatedAt * 1000); return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get sourceUrl() { if (/^https:\/\/i\.pximg\.net\/img-original\/img\/[\d\/]{19}\/([\d]{1,})_p[\d]{1,}\.(jpg|png)$/.test(this.source)) { const pid = RegExp.$1; return `https://pixiv.net/artworks/${pid}` } return this.source } } const App = { template: "#app-template", data() { return { showDrawer: false, showImageSelected: false, showImageInfo: true, showRatingQ: JSON.parse(localStorage.getItem("showRatingQ") || "true"), showRatingE: JSON.parse(localStorage.getItem("showRatingE") || "false"), imageList: [], imageSelectedIndex: 0, params: new URLSearchParams(location.search), requestState: false, requestStop: false, innerWidth: window.innerWidth, innerHeight: window.innerHeight, imageCountInRow: JSON.parse(localStorage.getItem("imageCountInRow") || "3"), imageQualityHigh: JSON.parse(localStorage.getItem("imageQualityHigh") || "false"), showFavoriteSuccess: false, } }, computed: { isMobile() { try { return this.$vuetify.breakpoint.mobile } catch(error) { return false } }, title() { return `${this.imageList.length} Posts` }, version() { return GM_info.script.version }, imageSelected() { return this.imageList[this.imageSelectedIndex] || new Post() }, imageSelectedWidth() { const width = parseInt(Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth)); const height = Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight); const width2 = parseInt(height * this.imageSelected.aspectRatio); return Math.min(width, width2) }, imageSelectedHeight() { const width = Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth); const height = parseInt(Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight)); const height2 = parseInt(width / this.imageSelected.aspectRatio); return Math.min(height, height2) }, }, watch: { showRatingQ(value) { localStorage.setItem("showRatingQ", JSON.stringify(value)); }, showRatingE(value) { localStorage.setItem("showRatingE", JSON.stringify(value)); }, imageCountInRow(value) { localStorage.setItem("imageCountInRow", JSON.stringify(value)); }, imageQualityHigh(value) { localStorage.setItem("imageQualityHigh", JSON.stringify(value)); }, showFavoriteSuccess(value) { console.log('showFavoriteSuccess: ', value); }, }, methods: { async request() { this.requestState = true; const url = location.origin + location.pathname + ".json?" + this.params.toString(); const response = await new Promise(resolve => { console.log(url); jQuery.get(url, data => resolve(data)); }); if (response instanceof Array && response.length > 0) { window.history.pushState("", "", location.pathname + "?" + this.params.toString()); response.forEach(item => this.imageList.push(new Post(item))); const page = Number(this.params.get("page")) || 1; this.params.set("page", page + 1); setTimeout(() => (this.requestState = false), 1000); } else { this.requestStop = true; } }, download(src, filename) { const match = src.match(/[.](?<extension>png|jpg|jpeg)$/); if (match) { const extension = match.groups.extension; GM_download(src, filename + "." + extension); } else { GM_download(src, filename); } }, onFavorite(id) { $.ajax({ method: 'POST', url: "https://yande.re/post/vote.json", beforeSend: xhr => xhr.setRequestHeader('x-csrf-token', window.csrfToken), data: { id, score: 3 }, success: data => { if (data.success === true) { this.imageList[this.imageSelectedIndex].favorite = true; } }, }); }, }, mounted() { const timeInterval = setInterval(() => { if (this.requestStop === true) { clearInterval(timeInterval); return } const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; const height = window.innerHeight; if (scrollTop + height >= scrollHeight * 0.75) { if (this.requestState === false) { this.request(); } } }, 1000); window.addEventListener("resize", () => { this.innerWidth = window.innerWidth; this.innerHeight = window.innerHeight; }); }, }; async function enterBrowseMode() { function getScript(url) { return new Promise(resolve => jQuery.getScript(url, () => resolve())) } await getScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"); await getScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.js"); await getScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-masonry.min.js"); await getScript("https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"); window.csrfToken = jQuery('[name="csrf-token"]').attr('content'); document.head.innerHTML = ` <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> <title>Yande.re 简体中文</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/necolas/normalize.css/normalize.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css"> <style> ::-webkit-scrollbar { display: none; width: 0px !important; } </style> `; document.body.innerHTML = ` <div id="app"></div> <script type="text/template" id="app-template"> <v-app> <v-app-bar app dense> <v-app-bar-nav-icon :x-small="isMobile" @click="showDrawer=!showDrawer"></v-app-bar-nav-icon> <v-toolbar-title :style="isMobile ? 'font-size: 12px;' : ''" v-text="title"></v-toolbar-title> <!-- 设置分级制度 --> <v-menu offset-y> <template v-slot:activator="{ on, attrs }"> <v-btn :x-small="isMobile" class="white--text ml-2" dark v-bind="attrs" v-on="on"> S{{ showRatingQ ? 'Q' : '' }}{{ showRatingE ? 'E' : '' }} </v-btn> </template> <v-list dense> <v-list-item dense> <v-list-item-title style="cursor: pointer;" @click="showRatingQ = !showRatingQ;"> {{ showRatingQ ? '隐藏 Q 级内容' : '显示 Q 级内容' }} </v-list-item-title> </v-list-item> <v-list-item dense> <v-list-item-title style="cursor: pointer;" @click="showRatingE = !showRatingE;"> {{ showRatingE ? '隐藏 E 级内容' : '显示 E 级内容' }} </v-list-item-title> </v-list-item> </v-list> </v-menu> <!-- 设置图片质量 --> <v-menu offset-y> <template v-slot:activator="{ on, attrs }"> <v-btn :x-small="isMobile" class="white--text ml-2" dark v-bind="attrs" v-on="on">{{ imageQualityHigh ? 'HD' : '速' }}</v-btn> </template> <v-list dense> <v-list-item dense> <v-list-item-title style="cursor: pointer;" @click="imageQualityHigh = false;"> 图片质量:速览 </v-list-item-title> </v-list-item> <v-list-item dense> <v-list-item-title style="cursor: pointer;" @click="imageQualityHigh = true;"> 图片质量:高清 </v-list-item-title> </v-list-item> </v-list> </v-menu> <!-- 设置每行几张 --> <v-menu offset-y> <template v-slot:activator="{ on, attrs }"> <v-btn :x-small="isMobile" class="white--text ml-2" dark v-bind="attrs" v-on="on">{{imageCountInRow}}列</v-btn> </template> <v-list dense> <v-list-item dense v-for="number in [1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 20]" :key="number"> <v-list-item-title style="cursor: pointer;" @click="imageCountInRow = number;"> {{ number }}列 </v-list-item-title> </v-list-item> </v-list> </v-menu> <v-spacer></v-spacer> <v-btn :style="isMobile ? 'flex: 0 1 auto; overflow: hidden;' : ''" :x-small="isMobile" text v-text="'v' + version" color="#ffffff" disabled> </v-btn> </v-app-bar> <v-navigation-drawer v-model="showDrawer" app temporary> <v-list-item> <v-list-item-content> <v-list-item-title class="title">Yande.re 简体中文</v-list-item-title> <v-list-item-subtitle>浏览器脚本程序</v-list-item-subtitle> </v-list-item-content> </v-list-item> <v-divider></v-divider> <v-list dense nav> <v-list-item-content> <v-list-item-title class="title">设置</v-list-item-title> <v-list-item-subtitle></v-list-item-subtitle> </v-list-item-content> <!-- s --> <v-list-item link> <v-list-item-icon class="mr-2"> <v-icon>mdi-check</v-icon> </v-list-item-icon> <v-list-item-content> <v-list-item-title>显示 S 分级内容</v-list-item-title> <v-list-item-subtitle>S(safe) 安全的全年龄内容</v-list-item-subtitle> </v-list-item-content> </v-list-item> <!-- q --> <v-list-item link @click="showRatingQ=!showRatingQ;"> <v-list-item-icon class="mr-2"> <v-icon v-text="showRatingQ ? 'mdi-check' : 'mdi-close'"></v-icon> </v-list-item-icon> <v-list-item-content> <v-list-item-title v-text="showRatingQ ? '显示 Q 分级内容' : '隐藏 Q 分级内容'"></v-list-item-title> <v-list-item-subtitle>Q(questionable) 疑似的成人内容</v-list-item-subtitle> </v-list-item-content> </v-list-item> <!-- e --> <v-list-item link @click="showRatingE=!showRatingE;"> <v-list-item-icon class="mr-2"> <v-icon v-text="showRatingE ? 'mdi-check' : 'mdi-close'"></v-icon> </v-list-item-icon> <v-list-item-content> <v-list-item-title v-text="showRatingE ? '显示 E 分级内容' : '隐藏 E 分级内容'"></v-list-item-title> <v-list-item-subtitle>E(explicit) 明确的成人内容</v-list-item-subtitle> </v-list-item-content> </v-list-item> </v-list> <v-divider></v-divider> <v-list dense nav> <v-list-item-content> <v-list-item-title class="title">关于</v-list-item-title> <v-list-item-subtitle></v-list-item-subtitle> </v-list-item-content> <v-list-item link @click="window.open('https://github.com/coderzhaoziwei/yande-re-chinese-patch/blob/main/readme.md')"> <v-list-item-icon class="mr-2"><v-icon>mdi-file-document-outline</v-icon></v-list-item-icon> <v-list-item-content> <v-list-item-title>简介</v-list-item-title> <v-list-item-subtitle>说明文档 / 功能介绍</v-list-item-subtitle> </v-list-item-content> </v-list-item> <v-list-item link @click="window.open('https://github.com/coderzhaoziwei/yande-re-chinese-patch/issues')"> <v-list-item-icon class="mr-2"><v-icon>mdi-github</v-icon></v-list-item-icon> <v-list-item-content> <v-list-item-title>反馈</v-list-item-title> <v-list-item-subtitle>发现错误 / 提出建议</v-list-item-subtitle> </v-list-item-content> </v-list-item> <v-list-item link @click="window.open('https://github.com/coderzhaoziwei/yande-re-chinese-patch')"> <v-list-item-icon class="mr-2"><v-icon>mdi-star</v-icon></v-list-item-icon> <v-list-item-content> <v-list-item-title>Github</v-list-item-title> <v-list-item-subtitle>觉得好用就 Star 支持一下</v-list-item-subtitle> </v-list-item-content> </v-list-item> <v-list-item link> <v-list-item-icon class="mr-2"><v-icon>mdi-google-controller</v-icon></v-list-item-icon> <v-list-item-content> <v-list-item-title>QQ</v-list-item-title> <v-list-item-subtitle>3158492760</v-list-item-subtitle> </v-list-item-content> </v-list-item> <v-list-item link> <v-list-item-icon class="mr-2"><v-icon>mdi-email</v-icon></v-list-item-icon> <v-list-item-content> <v-list-item-title>邮箱</v-list-item-title> <v-list-item-subtitle>[email protected]</v-list-item-subtitle> </v-list-item-content> </v-list-item> </v-list> </v-navigation-drawer> <v-main app> <v-container class="pa-2" fluid> <masonry ref="masonry" :cols="imageCountInRow" gutter="8px" :key="imageCountInRow"> <v-card class="mb-2" v-for="(image, index) in imageList" :key="index"> <v-img :src=" image.isRatingS || (image.isRatingQ && showRatingQ) || (image.isRatingE && showRatingE) ? (imageQualityHigh ? image.sampleUrl : image.previewUrl) : '' " :aspect-ratio="image.aspectRatio" @click="if(image.isRatingS||(image.isRatingQ && showRatingQ)||(image.isRatingE && showRatingE)){imageSelectedIndex=index;showImageSelected=true;}" @click.middle="imageSelectedIndex = index; window.open('/post/show/' + imageSelected.id)" > <template v-slot:placeholder> <v-row v-if="image.isRatingS||(image.isRatingQ && showRatingQ)||(image.isRatingE && showRatingE)" class="fill-height ma-0" align="center" justify="center" > <v-progress-circular indeterminate color="#ee8888"></v-progress-circular> </v-row> </template> <v-row v-if="(image.isRatingS||(image.isRatingQ && showRatingQ)||(image.isRatingE && showRatingE))===false" class="fill-height ma-0 text-h5" align="center" justify="center" style="color:#ee8888;" v-text="image.rating.toUpperCase()" ></v-row> </v-img> </v-card> </masonry> <div class="d-flex justify-center"> <v-btn :disabled="requestState===false" color="#ee8888" text v-text="requestStop ? '下面没有了...' : requestState ? '正在加载中...' : ''" ></v-btn> </div> <v-dialog v-model="showImageSelected" :width="imageSelectedWidth" :height="imageSelectedHeight"> <v-img :src="imageSelected.sampleUrl" :lazy-src="imageSelected.previewUrl" @click="showImageInfo = !showImageInfo;" > <div :style="showImageInfo ? 'display: flex; flex-direction: column; height: 100%; padding: 4px; grid-gap: 4px;' : 'display: none !important;'" > <div style="height: 100%; flex: 1 1 auto;"></div> <div style="display: flex; flex-direction: column; grid-gap: 4px;"> <v-chip style="width: fit-content;" color="#009ff088" text-color="#ffffff" small v-text="imageSelected.sampleDownloadText" @click.stop="download(imageSelected.sampleUrl, imageSelected.sampleDownloadName)" ></v-chip> <v-chip style="width: fit-content;" color="#009ff088" text-color="#ffffff" small v-if="imageSelected.jpegSize !== 0" v-text="imageSelected.jpegDownloadText" @click.stop="download(imageSelected.jpegUrl, imageSelected.jpegDownloadName)" ></v-chip> <v-chip style="width: fit-content;" color="#009ff088" text-color="#ffffff" small v-text="imageSelected.fileDownloadText" @click.stop="download(imageSelected.fileUrl, imageSelected.fileDownloadName)" ></v-chip> </div> <div style="display: flex; grid-gap: 4px; flex-wrap: wrap;"> <v-chip style="width: fit-content;" color="#ee888888" text-color="#ffffff" small v-text="imageSelected.id + ' ' + imageSelected.rating.toUpperCase()" @click.stop ></v-chip> <v-chip class="mr-1" style="width: fit-content;" color="#009ff088" text-color="#ffffff" small v-if="imageSelected.sourceUrl !== ''" v-text="'来源链接'" @click.stop="window.open(imageSelected.sourceUrl)" ></v-chip> <v-chip class="mr-1" style="width: fit-content;" color="#009ff088" text-color="#ffffff" small v-text="'本站链接'" @click.stop="window.open('/post/show/' + imageSelected.id)" ></v-chip> <v-chip class="mr-1" style="width: fit-content;" text-color="#ffffff" small :color="imageSelected.favorite ? '#00900088' : '#009ff088'" v-text="imageSelected.favorite ? '收藏成功' : '添加收藏'" @click.stop="imageSelected.favorite ? (void 0) : onFavorite(imageSelected.id)" ></v-chip> </div> </div> </v-img> </v-dialog> </v-container> </v-main> </v-app> </script> `; Vue.use(VueMasonry); new Vue({ vuetify: new Vuetify({ theme: { dark: true }, }), render: h => h(App) }).$mount("#app"); } const onChangeLeftBar = function() { const value = Boolean(document.getElementById("showLeftBar").selectedIndex); localStorage.setItem("showLeftBar", JSON.stringify(value)); const element = document.querySelector("#post-list > .sidebar"); element.setAttribute("show-left-bar", value); console.log("showLeftBar", value); }; const onChangeRatingE = function() { const value = Boolean(document.getElementById("showRatingE").selectedIndex); localStorage.setItem("showRatingE", JSON.stringify(value)); const elementList = document.querySelectorAll(".javascript-hide"); elementList.forEach(element => element.setAttribute("show-rating-e", value)); console.log("showRatingE", value); }; const onChangeImageHD = function() { const index = document.getElementById("showImageHD").selectedIndex; const elementList = document.querySelectorAll("#post-list-posts > li > .inner"); elementList.forEach(element => element.setAttribute("show-image-hd", index)); localStorage.setItem("showImageHD", JSON.stringify(index)); console.log("showImageHD", index); }; let taskArray = []; let maxLoadingSampleNum = 4; let doLoadSampleUrl = () => { let loadingNum = 0; let loadSampleUrl = () => { if (taskArray.length == 0) return loadingNum++; let { element, sampleUrl } = taskArray.shift(); element.onerror = () => { element.src = sampleUrl; }; element.onload = () => { loadingNum--; }; element.src = sampleUrl; }; setInterval(() => { if (taskArray.length == 0) return let needloadNum = maxLoadingSampleNum - loadingNum; while (needloadNum--) { loadSampleUrl(); } }, 1000); }; const initOptions = function() { if (/^\/user\/show\/[\d]{1,}/.test(location.pathname)) return if (document.getElementById("post-list-posts") === null) return document.getElementById("post-list-posts").insertAdjacentHTML("beforebegin", ` <div style="padding: 1rem; user-select: none; text-align: center;"> <select id="showLeftBar" style="height: 1.5rem; line-height: 1.5rem;"> <option>隐藏左栏</option> <option>显示左栏</option> </select> <select id="showRatingE" style="height: 1.5rem; line-height: 1.5rem; margin-left: 0.25rem;"> <option>隐藏默认</option> <option>显示全部</option> </select> <select id="showImageHD" style="height: 1.5rem; line-height: 1.5rem; margin-left: 0.25rem;"> <option>默认尺寸</option> <option>二倍尺寸</option> <option>三倍尺寸</option> <option>四倍尺寸</option> </select> <button id="enterBrowseMode" style="margin-left: 0.25rem;">进入浏览模式</button> </div> `); const imageList = document.querySelectorAll("img.preview"); const samples = JSON.parse(localStorage.getItem("sample_urls")); imageList.forEach(element => { if (/\/post\/show\/([\d]{1,})/.test(element.nextElementSibling.innerText)) { const id = RegExp.$1; const sampleUrl = samples[id]; if (sampleUrl !== undefined) { element.src = sampleUrl; } } }); doLoadSampleUrl(); document.getElementById("showLeftBar").addEventListener("change", onChangeLeftBar); document.getElementById("showRatingE").addEventListener("change", onChangeRatingE); document.getElementById("showImageHD").addEventListener("change", onChangeImageHD); const showLeftBar = JSON.parse(localStorage.getItem("showLeftBar") || "true"); const showRatingE = JSON.parse(localStorage.getItem("showRatingE") || "true"); const showImageHD = JSON.parse(localStorage.getItem("showImageHD") || "0"); document.getElementById("showLeftBar").selectedIndex = showLeftBar; document.getElementById("showRatingE").selectedIndex = showRatingE; document.getElementById("showImageHD").selectedIndex = showImageHD; onChangeLeftBar(); onChangeRatingE(); onChangeImageHD(); document.getElementById("enterBrowseMode").addEventListener("click", enterBrowseMode); }; const tags = { "4koma": "四格漫画", "5-toubun_no_hanayome": "五等分的新娘", "anal": "肛交", "angel": "天使", "angel_beats!": "Angel Beats!", "animal_ears": "兽耳", "anthropomorphization": "拟人化", "anus": "肛门露出", "areola": "乳晕", "arknights": "明日方舟", "armor": "盔甲/装甲", "artist_revision": "画师修改", "ass": "臀部", "ass_grab": "持股/捏臀", "atelier": "炼金工房系列", "autographed": "亲笔签名", "azur_lane": "碧蓝航线", "bakemonogatari": "化物语", "bandages": "绷带", "bandaid": "创可贴/绷带", "bang_dream!": "BanG Dream!", "baseball": "棒球", "basketball": "篮球", "bathing": "沐浴", "benghuai_xueyuan": "崩坏学园", "bike_shorts": "自行车短裤", "bikini": "比基尼", "bikini_armor": "比基尼装甲/轻薄盔甲", "bikini_top": "比基尼乳罩", "black_rock_shooter": "黑岩射手", "blood": "血腥", "bloomers": "灯笼裤/宽松短裤", "blue_archive": "碧蓝档案", "bodysuit": "紧身衣裤", "boku_wa_tomodachi_ga_sukunai": "我的朋友很少", "bondage": "束缚", "bottomless": "下身露出", "bra": "乳罩", "breast_grab": "握乳", "breast_hold": "托乳", "breasts": "乳", "bukkake": "颜射", "bunny_ears": "兔耳", "bunny_girl": "兔女郎", "buruma": "运动短裤", "business_suit": "西装/职业服", "calendar": "日历", "cameltoe": "阴户凸显", "card": "卡牌", "card_captor_sakura": "魔卡少女樱", "censored": "有码", "cg": "CG/计算机动画", "chainsaw": "电锯", "character_design": "角色设计", "cheerleader": "啦啦队队员", "chibi": "Q版", "chinadress": "旗袍", "choujigen_game_neptune": "超次元游戏海王星", "christmas": "圣诞", "cleavage": "乳沟", "code_geass": "反叛的鲁路修", "condom": "避孕套", "corset": "(束腰)紧身内衣", "cosplay": "角色扮演", "cream": "奶油", "cropped": "裁剪图", "crossdress": "变装", "crossover": "作品联动/混合同人", "cum": "精液", "cunnilingus": "品玉/舔阴", "dakimakura": "抱枕", "darling_in_the_franxx": "DARLING in the FRANXX", "date_a_live": "约会大作战", "detexted": "去字图片", "devil": "魔鬼/恶魔", "digital_version": "数字版", "dildo": "假阳具", "disc_cover": "光盘封面", "dress": "连衣裙", "dress_shirt": "衬衫", "duplicate": "重复图片", "elf": "精灵", "endcard": "片尾插图", "erect_nipples": "乳尖", "expression": "角色展示/立绘", "extreme_content": "极端", "eyepatch": "眼罩", "fairy": "精灵/小精灵", "fate/kaleid_liner_prisma_illya": "Fate/kaleid liner 魔法少女☆伊莉雅", "feet": "足", "fellatio": "口交", "final_fantasy": "最终幻想", "final_fantasy_vii": "最终幻想 VII", "final_fantasy_xiv": "最终幻想 14", "fingering": "指交", "fire_emblem": "火焰纹章", "fire_emblem_heroes": "火焰之纹章:英雄云集", "fire_emblem_kakusei": "火焰之纹章:觉醒", "fire_emblem_three_houses": "火焰之纹章:风花雪月", "fishnets": "鱼网袜", "fixed": "修改", "footjob": "足交", "fundoshi": "褌/兜裆布", "futanari": "扶她", "game_cg": "游戏CG", "gangbang": "乱交", "garter": "袜带", "garter_belt": "吊袜腰带", "genderswap": "性转", "genshin_impact": "原神", "girls_frontline": "少女前线", "girls_und_panzer": "少女与战车", "gochuumon_wa_usagi_desu_ka?": "请问您今天要来点兔子吗?", "gothic_lolita": "哥特式洛丽塔", "granblue_fantasy": "碧蓝幻想", "guitar": "吉他", "gun": "枪炮", "gundam": "高达", "guro": "猎奇", "halloween": "万圣节前夜", "handjob": "打手枪", "headphones": "耳机", "heels": "高跟鞋", "heterochromia": "虹膜异色", "hibike!_euphonium": "吹响吧!上低音号", "highschool_dxd": "恶魔高校D×D", "honkai_impact": "崩坏 3", "horns": "角", "index_page": "索引页面", "infinite_stratos": "IS/无限斯特拉托斯", "inumimi": "犬耳", "japanese_clothes": "日式服装", "k-on!": "轻音少女", "kaguya-sama_wa_kokurasetai_~tensai-tachi_no_renai_zunousen~": "辉夜大小姐想让我告白~天才们的恋爱头脑战~", "kantai_collection": "舰队 Collection", "kemono_friends": "兽娘动物园", "kimetsu_no_yaiba": "鬼灭之刃", "kimono": "和服", "kitsune": "狐狸", "kobayashi-san_chi_no_maid_dragon": "小林家的龙女仆", "kono_subarashii_sekai_ni_shukufuku_wo!": "为美好的世界献上祝福!", "lactation": "泌乳", "landscape": "风景画", "league_of_legends": "英雄联盟", "leotard": "紧身连衣裤", "line_art": "线条画", "lingerie": "贴身内衣", "little_busters!": "Little Busters!", "loli": "萝莉", "lolita_fashion": "洛丽塔", "love_live!_nijigasaki_high_school_idol_club": "Love Live! 虹咲学园学园偶像同好会", "lucky_star": "幸运星", "maebari": "前貼り/遮盖私处", "mahou_shoujo_lyrical_nanoha": "魔法少女奈叶", "mahou_shoujo_lyrical_nanoha_strikers": "魔法少女奈叶 StrikerS", "maid": "女仆", "male": "男性", "masturbation": "自摸/手淫", "mecha": "机甲", "mecha_musume": "机甲娘", "megane": "眼镜", "megaten": "女神转生系列", "mermaid": "美人鱼", "miko": "巫女", "monochrome": "单色", "monster": "怪物", "monster_girl": "怪物女孩", "monster_musume_no_iru_nichijou": "魔物娘的相伴日常", "naked": "裸体", "naked_apron": "裸体围裙", "naked_cape": "裸体披风", "naked_ribbon": "裸体丝带", "neko": "猫", "nekomimi": "猫耳", "neon_genesis_evangelion": "新世纪福音战士", "nier_automata": "尼尔:自动人形", "nijisanji": "彩虹社", "ninja": "忍者", "nipple_slip": "露点", "nipples": "乳头", "no_bra": "无乳罩", "nopan": "无胖次", "nun": "修女", "nurse": "护士", "official_watermark": "官方水印", "onsen": "温泉", "open_shirt": "衬衫敞开", "ore_no_imouto_ga_konnani_kawaii_wake_ga_nai": "我的妹妹哪有这么可爱!", "overalls": "工装连衣裤", "overwatch": "守望先锋", "paizuri": "乳交", "pajama": "睡衣", "panties": "内裤", "pantsu": "胖次", "panty_pull": "胖次脱下", "pantyhose": "吊带袜", "parody": "仿拟/谐拟", "partial_scan": "局部扫描", "pasties": "乳贴", "pee": "尿尿", "penguin": "企鹅", "penis": "阴茎", "photo": "照片/现实背景", "photoshop": "PS 改图", "pirate": "海盗", "pointy_ears": "尖耳朵", "pokemon": "精灵宝可梦", "possible_duplicate": "可能重复", "pregnant": "孕妇", "pretty_cure": "光之美少女", "princess_connect": "公主连结", "princess_connect!_re:dive": "公主连结 Re:Dive", "profile_page": "角色资料页", "pubic_hair": "阴毛", "puella_magi_madoka_magica": "魔法少女小圆", "pussy": "阴户", "pussy_juice": "妹汁", "queen's_blade": "女王之刃", "raw_scan": "扫描原图", "re_zero_kara_hajimeru_isekai_seikatsu": "Re:从零开始的异世界生活", "robe": "长袍/礼服/睡袍", "saenai_heroine_no_sodatekata": "路人女主的养成方法", "sailor_moon": "美少女战士", "sake": "日本清酒", "sample": "样品图", "sarashi": "晒し/缠胸布", "school_swimsuit": "学校泳衣", "see_through": "透视", "seifuku": "制服", "selfie": "自拍", "senran_kagura": "闪乱神乐", "sex": "性交", "sheets": "床单", "shimapan": "条纹胖次", "shirt_lift": "衬衫掀起", "shota": "正太", "silhouette": "剪影/暗色轮廓/体形", "sketch": "素描", "skirt_lift": "裙摆掀起", "sling_bikini": "吊带比基尼", "smoking": "吸烟", "soccer": "足球", "sono_bisque_doll_wa_koi_wo_suru": "更衣人偶坠入爱河", "spy_x_family": "间谍过家家", "ssss.gridman": "SSSS.古立特", "stick_poster": "海报", "stockings": "长筒袜", "strike_witches": "强袭魔女", "string_panties": "细绳胖次", "summer_dress": "夏装", "suzumiya_haruhi_no_yuuutsu": "凉宫春日的忧郁", "sweater": "毛衣", "swimsuits": "泳衣", "sword": "刀剑", "sword_art_online": "刀剑神域", "symmetrical_docking": "乳乳相接", "tagme": "标签", "tail": "兽尾", "tan_lines": "日晒线", "tattoo": "文身", "tennis": "网球", "tentacles": "触手", "text": "文本", "the_idolm@ster": "偶像大师", "the_idolm@ster_cinderella_girls": "偶像大师灰姑娘女孩", "the_idolm@ster_million_live!": "偶像大师百万现场", "the_idolm@ster_shiny_colors": "偶像大师闪耀色彩", "thighhighs": "过膝袜", "thong": "丁字裤", "to_aru_kagaku_no_railgun": "某科学的超电磁炮", "to_aru_majutsu_no_index": "魔法禁书目录", "to_heart_(series)": "To Heart 系列", "to_heart_2": "To Heart 2", "to_love_ru": "出包王女", "to_love_ru_darkness": "出包王女 Darkness", "topless": "上身露出", "torn_clothes": "破衣", "touhou": "东方", "towel": "浴巾", "translated": "文字已翻译(英文)", "transparent_png": "背景透明", "trap": "伪娘", "tribadism": "磨豆腐/交叉体位", "tutorial": "教程", "uma_musume_pretty_derby": "赛马娘", "umbrella": "伞", "uncensored": "无码", "underboob": "南半球", "underwear": "内衣", "undressing": "脱衣", "uniform": "制服", "valentine": "情人节", "vibrator": "跳蛋", "wa_maid": "和风女仆", "waitress": "女侍", "wallpaper": "壁纸", "wardrobe_malfunction": "走光", "weapon": "武器", "wedding_dress": "婚纱", "wet": "湿身", "wet_clothes": "湿衣", "wings": "翅膀", "witch": "女巫", "xenoblade": "异度神剑", "xenoblade_chronicles_2": "异度神剑 2", "yahari_ore_no_seishun_lovecome_wa_machigatteiru.": "我的青春恋爱喜剧果然有问题", "yaoi": "蔷薇/男同", "yukata": "浴衣", "yuri": "百合", "zhanjianshaonv": "战舰少女" }; const menus = { "My Account": "账户", "Posts": "作品", "Comments": "评论", "Notes": "笔记", "Artists": "画师", "Tags": "标签", "Forum": "论坛", "Help": "帮助", "More »": "更多>>", "New Mail": "新消息", "My Profile": "我的资料", "My Mail": "我的消息", "My Favorites": "我的收藏", "Settings": "设置", "Change Password": "修改密码", "Logout": "退出登录", "View Posts": "浏览作品", "Search Posts": "搜索作品", "Upload": "上传", "Random": "随机浏览", "Popular": "热门", "Image Search": "搜索图片", "History": "历史", "View Comments": "浏览评论", "Search Comments": "搜索评论", "View Notes": "浏览笔记", "Search Notes": "搜索笔记", "View Artists": "浏览画师", "Search Artists": "搜索画师", "Create": "创建", "View Tags": "浏览标签", "Search Tags": "搜索标签", "Aliases": "别名", "Implications": "含义", "View Pools": "浏览 Pools", "Search Pools": "搜索 Pools", "Create New Pool": "创建 Pool", "View Wiki Index": "浏览 Wiki 主页", "Search Wiki": "搜索 Wiki", "Create New Page": "创建新页面", "Mark All Read": "全部标记已读" } ; const footers = { "List": "首页", "Browse": "翻阅", "Upload": "上传", "Random": "随机", "Popular": "热门", "Image Search": "寻图", "History": "历史", "Help": "帮助" } ; const translateTags = function() { const elementList = Array.from(document.getElementsByTagName("a")); elementList.forEach(element => { const href = element.getAttribute("href"); if (typeof href === "string" && /^\/post\?tags=(\S+)$/.test(href)) { const en = RegExp.$1; const cn = tags[en]; if (cn) { element.innerText = `[${cn}]${en.replace(/_/g, " ")}`; } } }); }; const translateMenus = function() { const mainMenuList = Array.from(document.querySelectorAll("#main-menu>ul>li>a")); const subMenuList = Array.from(document.querySelectorAll("ul.submenu>li>a")); const elementList = [...mainMenuList, ...subMenuList]; elementList.forEach(element => { if (element.getAttribute("href") === "#") return const en = element.innerText; const cn = menus[en]; if (cn) { element.innerText = cn; } }); }; const translateNotice = function() { const elementList = Array.from(document.querySelectorAll(".status-notice")); elementList.forEach(element => { console.log(element.innerHTML); element.innerHTML = element.innerHTML .replace(/^[\s]+This image has been resized. Click on the /, "这张图片已经被压缩,单击侧边栏中的") .replace(/View larger version/, "显示高清图") .replace(/ link in the sidebar for a high-quality version./, "可以获取更高质量的版本。") .replace(/Hide this message<\/a>\./, "不再提醒</a>") .replace(/This post belongs to a /, "这张图片从属于一个") .replace(/parent post<\/a>\./, "相关父作品</a>。") .replace(/This post has /, "这张图片从属于一个") .replace(/child posts<\/a>\. \(post #/, "作品集</a>。相关子作品:") .replace(/a child post<\/a>\. \(post #/, "作品集</a>。相关子作品:") .replace(/<\/a>, <a /, "</a> | <a ") .replace(/<\/a>\)/, "</a>"); }); }; const translateButtons = function() { [ ['#highres-show', 'View larger version', '显示高清图'], ['#highres', 'Download larger version', '下载高清图'], ['#png', 'Download PNG', '下载 PNG 图'], ['li#add-to-favs>a', 'Add to favorites', '添加收藏'], ['li#set-avatar>a', 'Set avatar', '设置头像'], ['h4>a.js-posts-show-edit-tab', 'Edit', '编辑'], ['h4>a.js-posts-show-comments-tab', 'Respond', '评论'], ['.pagination>.previous_page', '← Previous', '上一页'], ['.pagination>.next_page', 'Next →', '下一页'], ].forEach(data => { const [selector, en, cn] = data; const element = document.querySelector(selector); if (element) { element.innerText = element.innerText.replace(en, cn); } }); }; const translateFooters = function() { const elementList = Array.from(document.querySelectorAll('#subnavbar>li>a')); elementList.forEach(element => { const en = element.innerText; const cn = footers[en]; if (cn) { element.innerText = cn; } }); }; const initTranslate = function() { translateTags(); translateMenus(); translateNotice(); translateButtons(); translateFooters(); }; jQuery(document).ready(function() { initStyle(); initHotKey(); initOptions(); initTranslate(); if (document.cookie.includes('locale=zh_CN') === false) { document.cookie = "locale=zh_CN"; location.href = location.href; } }); }());