// ==UserScript== // @name pixivイラストページ改善 // @description pixivイラストページのタグに作者マーカーと百科事典アイコン、ユーザー名の列に作品タグを復活させます // @namespace Aime // @match https://www.pixiv.net/* // @version 1.2.7 // @grant none // @run-at document-end // @noframes // @note 2018/06/22 1.0.1 作者アイコンを大サイズに差し替え // @note 2018/07/18 1.0.2 pixiv側のclass変更に対応 // @note 2018/07/26 1.0.3 アイコン差し替えを修正 // @note 2018/09/23 1.1.0 新プロフィールページのサムネイルをトリミングなしに差し替え。作品タグをapiから取得 // @note 2018/10/03 1.1.1 サムネイル差し替え修正 // @note 2018/12/28 1.1.2 アイコン差し替え修正 // @note 2019/01/16 1.1.3 アイコン差し替え修正等 // @note 2019/01/16 1.1.4 タグクラウドの広告ブロックフィルタ誤爆対策 // @note 2019/02/20 1.1.5 article→main // @note 2019/04/23 1.2.0 いろいろ修正 // @note 2019/04/23 1.2.2 アイコン修正 // @note 2019/09/15 1.2.3 // @note 2019/09/24 1.2.4 member_illust.php?mode=medium&illust_id= → artworks/ // @note 2019/09/24 1.2.5 custom_thumb差し替え // @note 2020/01/08 1.2.6 member.php?id= → users/ 等 // @note 2020/07/07 1.2.7 ページ遷移検出簡素化、ダークテーマ対応等 // @downloadURL https://update.greasyfork.icu/scripts/369658/pixiv%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E3%83%9A%E3%83%BC%E3%82%B8%E6%94%B9%E5%96%84.user.js // @updateURL https://update.greasyfork.icu/scripts/369658/pixiv%E3%82%A4%E3%83%A9%E3%82%B9%E3%83%88%E3%83%9A%E3%83%BC%E3%82%B8%E6%94%B9%E5%96%84.meta.js // ==/UserScript== // jshint esversion:6 (function() { "use strict"; const pixivService = { PixpediaIcon : 1, // 百科事典アイコンを付けるか? (0:付けない, 1:付ける, 2:記事の有無でアイコンを変える) UseTagCloud : true, // 作品タグを表示するか? UseLargeIcon : true, // 作者アイコンを大きくする NonTrimThumbnail : true, // サムネイルをトリミングなしにする tagCloudDisplayCount : 30, // 作品タグの表示数(目安) tagCloudSortByName : true, // 作品タグのソート順 (true:名前順, false:多い順) _illustTagCache : {}, _existsPixpedia : {}, _currentAuthorId : -1, _tagCloud : null, run() { const root = document.getElementById("root"); if (!root) return; const style = $C("style"); style.textContent = this._style; document.querySelector("head").appendChild(style); root.addEventListener("click", this, true); this.delayTagMarking(); delayExec(() => { return !!window.__ankpixiv_pushstate_override; }, 200, 5).then(() => { console.log("use AnkPixiv 3.0.13+ history message"); window.addEventListener("message", event => { if (event.data.type === "AnkPixiv.onPushState" || event.data.type === "AnkPiviv.onReplaceState") { this.delayTagMarking(); } }); window.addEventListener("popstate", event => this.delayTagMarking()); }).catch(() => { new HistoryChangeEmitter(type => this.delayTagMarking()); }); this._themeObserver = null; const options = { childList : true, subtree : true, attributes : true, attributeFilter : [ /*"href",*/ "src" ] }; const ob = new MutationObserver(records => { let thumbs = [], iconChange = false; const parseImg = target => { if (target.nodeName === "IMG") { if (this.NonTrimThumbnail && (target.src.includes("_square1200.jpg") || target.src.includes("_custom1200.jpg"))) { thumbs.push(target); } else if (target.getAttribute("width") == 40 && (target.src.includes("/user-profile/") || target.src.includes("no_profile")) && target.closest("ASIDE")) { iconChange = true; } } }; records.forEach(record => { switch (record.type) { case "attributes": { const target = record.target; switch (record.attributeName) { /* case "href": if (target.href.includes("/users/") && target.querySelector(':scope > [role="img"]') && target.closest("ASIDE")) { iconChange = true; } break;*/ case "src": parseImg(target); break; } break; } default: record.addedNodes.forEach(node => parseImg(node)); break; } }); if (!this._themeObserver) this.installThemeObserver(); if (thumbs.length || iconChange) { ob.disconnect(); if (thumbs.length) this.replaceThumbnail(thumbs); if (iconChange) this.onAuthorChange(); ob.observe(root, options); } }); ob.observe(root, options); }, handleEvent(event) { switch (event.type) { case "mouseover": event.stopPropagation(); break; case "click": if (event.target.classList.contains("gm-profile-work-list-tag-filter-click")) { this.openTagPage(event); } break; default: console.log(event); break; } }, openTagPage(event) { const url = location.href; for (let p of [ { re: /users\/(\d+)\/novels|novel\/member\.php\?id=(\d+)/, url: "https://www.pixiv.net/novel/member_tag_all.php?id=" }, { re: /users\/(\d+)\/|member_illust\.php\?id=(\d+)/, url: "https://www.pixiv.net/member_tag_all.php?id=" }, ]) { const match = p.re.exec(url); if (match) { event.stopPropagation(); event.preventDefault(); location.href = p.url + match[1]; return; } } }, installThemeObserver() { const theme = document.getElementById("gtm-var-theme-kind"); if (theme) { this._themeObserver = new MutationObserver(records => this.checkTheme()); this._themeObserver.observe(theme, { characterData: true, subtree: true }); this.checkTheme(); } }, checkTheme() { const theme = document.getElementById("gtm-var-theme-kind"); if (theme) { const html = document.documentElement; if (theme.textContent === "dark") { html.setAttribute("dark-theme", true); } else { html.removeAttribute("dark-theme"); } } }, getIllustId() { const m = /(?:\/artworks\/|illust_id=)(\d+)/.exec(location.href); return m? parseInt(m[1], 10): null; }, getAuthorId() { const a = document.querySelector("main + aside h2 a"); if (!a) return null; const m = /(?:\/users\/|\/member\.php\?id=)(\d+)/.exec(a.href); return m? parseInt(m[1], 10): null; }, async delayTagMarking() { const url = location.href; setTimeout(() => { if (url === location.href) this.tagMarking(); }, 1000); }, async tagMarking() { const illustId = this.getIllustId(); if (!illustId) return; const displayedTags = await delayExec(() => { const nodes =document.body.querySelectorAll("figcaption footer > ul a.gtm-new-work-tag-event-click"); return nodes.length > 0? nodes: false; }); // const displayedTags = document.body.querySelectorAll("figcaption footer > ul a.gtm-new-work-tag-event-click"); if (displayedTags.length === 0) return; if (!(illustId in this._illustTagCache)) { try { this._illustTagCache[illustId] = await fetchJSON("https://www.pixiv.net/ajax/tags/illust/" + illustId); } catch (e) { console.error(e); } } let tagData = this._illustTagCache[illustId]; if (!tagData) tagData = { authorId: 0, tags: [] }; const authorId = tagData.authorId; for (let node of displayedTags) { const cls = node.parentElement.classList; const tag = node.textContent.trim(); const find = tagData.tags.find(t => t.tag == tag); if (find && find.userId == authorId) { cls.add("author-tag-marker"); } else { cls.remove("author-tag-marker"); } this.appendPixpediaIcon(node); } }, async appendPixpediaIcon(node) { if (!this.PixpediaIcon || node.hasAttribute("pixpedia")) return; node.setAttribute("pixpedia", true); node.addEventListener("mouseover", this, true); const eTag = encodeURIComponent(node.textContent.trim()); let cls = "pixpedia-icon"; if (this.PixpediaIcon === 2) { try { if (!(eTag in this._existsPixpedia)) { this._existsPixpedia[eTag] = !!await fetchJSON("https://www.pixiv.net/ajax/tag/info?tag=" + eTag); } if (!this._existsPixpedia[eTag]) cls += " pixpedia-icon-no-item"; } catch (e) { console.error(e); } } $C("a", { class : cls, href : "https://dic.pixiv.net/a/" + eTag }, node.parentElement); }, onAuthorChange() { if (this.UseLargeIcon) this.largeAuthorIcon(); if (this.UseTagCloud) this.appendTagCloud(); }, async appendTagCloud() { const aside = document.querySelector("main + aside"); if (!aside) return; const authorId = this.getAuthorId(); if (!authorId) return; const tagAllUrl = "https://www.pixiv.net/member_tag_all.php?id=" + authorId; if (this._currentAuthorId !== authorId) { this._currentAuthorId = authorId; try { let tags = await fetchJSON("https://www.pixiv.net/ajax/user/" + authorId + "/illustmanga/tags"); tags.sort(this.compareTagByCount); // 多い順にソート const dispCnt = this.tagCloudDisplayCount; if (tags.length > dispCnt) { // とりあえず目安位置以下の値を破棄 const lastCnt = tags[dispCnt - 1].cnt; tags = tags.filter(v => v.cnt >= lastCnt); const tags2 = tags.filter(v => v.cnt > lastCnt); // 目安位置と同数とそれより多いのがどちらが目安位置に近いか if (dispCnt - tags2.length < tags.length - dispCnt && tags2.length > 5) { tags = tags2; } } if (tags.length) { let lv = 1, cur = tags[0].cnt; tags.forEach(tag => { // レベル付け if (lv < 6 && cur !== tag.cnt) { cur = tag.cnt; lv++; } //