// ==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 });