pixiv Tabs Restorer

Adds “All”, “Follow”, “My pixiv”, and “Tag Index” tabs to user pages etc. and “All” and “Ugoira” tabs to search result pages.

目前为 2019-11-25 提交的版本。查看 最新版本

// ==UserScript==
// @name        pixiv Tabs Restorer
// @name:ja     pixiv タブを復活
// @description Adds “All”, “Follow”, “My pixiv”, and “Tag Index” tabs to user pages etc. and “All” and “Ugoira” tabs to search result pages.
// @description:ja ユーザーページなどに「すべて」「フォロー」「マイピク」「タグ一覧」タブを、検索結果に「すべて」「うごイラ」タブを補完します。
// @namespace   https://gf.qytechs.cn/users/137
// @version     2.0.0
// @match       https://www.pixiv.net/*
// @require     https://gf.qytechs.cn/scripts/19616/code/utilities.js?version=752462
// @require     https://gf.qytechs.cn/scripts/17896/code/start-script.js?version=112958
// @license     MPL-2.0
// @contributionURL https://www.amazon.co.jp/registry/wishlist/E7PJ5C3K7AM2
// @compatible  Edge 最新安定版 / Latest stable (非推奨 / Deprecated)
// @compatible  Firefox
// @compatible  Opera
// @compatible  Chrome
// @grant       dummy
// @noframes
// @run-at      document-start
// @icon        data:image/png;base64,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
// @author      100の人
// @homepageURL https://gf.qytechs.cn/scripts/373026
// ==/UserScript==

// 当スクリプトはpixivが作成、配布しているアプリケーションではありません。
// <https://www.pixiv.net/terms/?page=brand>

'use strict';

// L10N
Gettext.setLocalizedTexts({
	/*eslint-disable quote-props, max-len */
	'en': {
		'すべて': 'All',
		'フォロー': 'Follow',
		'マイピク': 'My pixiv',
		'タグ一覧': 'Tag Index',
		'うごイラ': 'Ugoira',
	},
	'ko': {
		'すべて': '전체',
		'フォロー': '팔로우',
		'マイピク': '마이픽',
		'タグ一覧': '태그 목록',
		'うごイラ': '움직이는 일러스트',
	},
	'zh': {
		'すべて': '全部',
		'フォロー': '关注',
		'マイピク': '好P友',
		'タグ一覧': '标签一览',
		'うごイラ': '动图',
	},
	'zh-tw': {
		'すべて': '全部',
		'フォロー': '關注',
		'マイピク': '好P友',
		'タグ一覧': '標籤一覽',
		'うごイラ': '動圖',
	},
	/*eslint-enable quote-props, max-len */
});

class TabCompleter
{
	/**
	 * @access private
	 * @constant {number}
	 */
	static get URLS_AND_LABLES() {return {
		search: [
			{ path: 'artworks', label: _('すべて'), position: 1 },
			{ path: 'illustrations', type: 'ugoira', label: _('うごイラ'), position: 4 },
		],
		user: [
			{ path: '/member_illust.php', label: _('すべて'), position: 1 },
			{ path: '/bookmark.php', type: 'user', label: _('フォロー') },
			{ path: '/mypixiv_all.php', label: _('マイピク') },
			{ path: '/member_tag_all.php', label: _('タグ一覧') },
		],
	};}

	constructor()
	{
		const root = document.getElementById('root');
		if (!root) {
			return;
		}

		Gettext.setLocale(document.documentElement.lang);

		addEventListener('click', event => {
			const tab = event.target.closest('a');
			if (!tab || !tab.matches('#root > :not(header) nav > a')) {
				return;
			}

			if (event.defaultPrevented) {
				// 既存のタブ
				this.markCurrentTab();
				return;
			}

			if (tab.getAttribute('href').startsWith('/') || !location.pathname.startsWith('/tags/')
				|| location.pathname.endsWith('/novels')) {
				// 別サービスへのタブ
				return;
			}
			
			// イラスト検索結果ページで「すべて」「うごイラ」タブをクリックした場合
			event.preventDefault();

			// 検索オプションボタンをクリック
			document.querySelector('[d^="M0 1C0 0.447754"]').closest('button').click();
			new MutationObserver(function (mutations, observer) {
				let dialog;
				for (const mutation of mutations) {
					dialog = Array.from(mutation.addedNodes).find(
						node => node.nodeType === Node.ELEMENT_NODE && node.getAttribute('role') === 'presentation'
					);
					if (dialog) {
						break;
					}
				}
				if (!dialog) {
					return;
				}
				observer.disconnect();

				dialog.hidden = true;

				// 対象
				const input = dialog.querySelector('[class$="-dummyInput"]');
				// プルダウンメニューを開く
				input.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: ' ' }));
				new MutationObserver(function (mutations, observer) {
					let select, options;
					mutations: for (const mutation of mutations) {
						for (const node of mutation.addedNodes) {
							options = node.querySelectorAll('[role="option"]');
							if (options.length === 0) {
								continue;
							}
							select = node;
							break mutations;
						}
					}
					if (!select) {
						return;
					}
					observer.disconnect();

					// 項目を選択する
					options[tab.pathname.endsWith('/artworks') ? /* すべて */0 : /* うごイラ */4].click();
					new MutationObserver(function (mutations, observer) {
						if (!mutations.some(mutation => Array.from(mutation.removedNodes).includes(select))) {
							return;
						}
						observer.disconnect();
						
						setTimeout(function () {
							// 「適用する」ボタンを押す
							dialog.querySelector('[type="submit"]').click();
							this.markCurrentTab();
						}, 100);
					}).observe(select.parentElement, { childList: true });
				}).observe(dialog, { subtree: true, childList: true });
			}).observe(document.body, { childList: true });
		});

		new MutationObserver(mutations => {
			for (const mutation of mutations) {
				if (mutation.target.matches('#root > div[class]')) {
					// 検索結果ページ
					if (!Array.from(mutation.addedNodes).some(
						node => node.nodeType === Node.ELEMENT_NODE && node.querySelector('nav > a[href^="/tags/"]')
					)) {
						continue;
					}
					this.complete();
					return;
				}
				
				let findChild;
				if (mutation.target.matches('#root > div[class] > div[class]')) {
					findChild = node => node.localName === 'div' && node.hasAttribute('class');
				} else if (mutation.target.matches('#root > div[class] > div[class] > div[class]')) {
					findChild = node => node.localName === 'nav';
				}
				if (!findChild) {
					continue;
				}

				const parent = Array.from(mutation.addedNodes).find(findChild);
				if (parent) {
					if (parent.querySelector('[href*="/mypixiv_all.php?"]')) {
						return;
					}
					this.complete();
					return;
				}
			}
		}).observe(root, {childList: true, subtree: true});
	}

	markCurrentTab()
	{
		const currentTab = this.list.querySelector('[aria-current][href^="/"]');
	
		const tabs = this.list.children;
		for (const tab of tabs) {
			if (tab.href === location.href) {
				tab.setAttribute('aria-current', 'page');
				tab.classList.add(...this.currentTabClasses);
			} else {
				tab.removeAttribute('aria-current');
				tab.classList.remove(...this.currentTabClasses);
			}
		}

		if (!this.list.querySelector('[aria-current]')) {
			currentTab.setAttribute('aria-current', 'page');
			currentTab.classList.add(...this.currentTabClasses);
		}
	}

	/**
	 * タブを補完します。
	 * @returns {Promise.<void>}
	 */
	async complete()
	{
		/**
		 * 各タブの共通の親要素。
		 * @member {HTMLElement}
		 */
		this.list = document.querySelector('#root > :not(header) nav');

		if (this.list.querySelector('[href*="type=ugoira"], [href*="/member_tag_all.php"]')) {
			// すでに補完済みなら
			this.markCurrentTab();
			return;
		}
		
		const tabs = this.list.children;
		
		if (!this.currentTabClasses) {
			const currentTab = this.list.querySelector('[aria-current="page"]');
			const noCurrentTabClassList = Array.from(Array.from(tabs).find(tab => tab !== currentTab).classList);
			/**
			 * カレントタブに設定されるクラス。
			 * @member {string[]}
			 */
			this.currentTabClasses
				= Array.from(currentTab.classList).filter(token => !noCurrentTabClassList.includes(token));
		}

		const pageType = location.pathname.startsWith('/tags/') ? 'search' : 'user';

		// 挿入するタブのテンプレートを作成
		const templateTab = tabs[pageType === 'search' ? 1 : 0].cloneNode(true);
		templateTab.removeAttribute('aria-current');
		templateTab.classList.remove(...this.currentTabClasses);
		const param = new URLSearchParams(templateTab.search);

		for (const { path, type, label, position } of TabCompleter.URLS_AND_LABLES[pageType]) {
			let tab;
			if (path === '/member_illust.php') {
				tab = document.querySelector('[href^="/member_illust.php?id="]:not([href*="type="])');
				if (tab) {
					tab.classList = templateTab.classList;
				}
			}

			if (!tab) {
				tab = templateTab.cloneNode(true);
			}

			switch (pageType) {
				case 'search':
					tab.pathname = tab.pathname.replace(/[^/]+$/, path);
					tab.firstElementChild.textContent = label;
					break;

				case 'user':
					tab.pathname = path;
					tab.text = label;
					break;
			}
			if (type) {
				param.set('type', type);
				tab.search = param;
			}
			if (position) {
				tabs[position].before(tab);
			} else {
				this.list.append(tab);
			}
		}

		this.markCurrentTab();
	}
}

document.addEventListener('DOMContentLoaded', function () {
	new TabCompleter();
}, { passive: true, once: true });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址