- // ==UserScript==
- // @name pixiv 検索オプションを追加
- // @name:ja pixiv 検索オプションを追加
- // @name:en pixiv Search Options
- // @description This is a search option add-on script for pixiv. Enables you to select search mode by radio buttons (legacy feature).
- // @description:ja 検索オプションを、以前のようにラジオボタンで選択出来るようにする。
- // @namespace http://loda.jp/script/
- // @version 6.0.1
- // @match https://www.pixiv.net/*
- // @exclude https://www.pixiv.net/member_illust.php?*mode=manga*
- // @exclude https://www.pixiv.net/apps.php*
- // @require https://gitcdn.xyz/cdn/greasemonkey/gm4-polyfill/a834d46afcc7d6f6297829876423f58bb14a0d97/gm4-polyfill.js
- // @resource dialog-polyfill.css https://bowercdn.net/c/dialog-polyfill-0.4.10/dialog-polyfill.css
- // @require https://bowercdn.net/c/dialog-polyfill-0.4.10/dialog-polyfill.js
- // @require https://greasyfork.org/scripts/17895/code/polyfill.js?version=625392
- // @require https://greasyfork.org/scripts/19616/code/utilities.js?version=752462
- // @require https://greasyfork.org/scripts/17896/code/start-script.js?version=112958
- // @license MPL-2.0
- // @contributionURL https://www.amazon.co.jp/registry/wishlist/E7PJ5C3K7AM2
- // @compatible Edge 非推奨 / Deprecated
- // @compatible Firefox
- // @compatible Opera
- // @compatible Chrome
- // @grant GM.getValue
- // @grant GM_getValue
- // @grant GM.setValue
- // @grant GM_setValue
- // @grant GM.registerMenuCommand
- // @grant GM_registerMenuCommand
- // @grant GM.getResourceUrl
- // @grant GM_getResourceURL
- // @noframes
- // @run-at document-start
- // @icon data:image/png;base64,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
- // @author 100の人
- // @homepageURL https://greasyfork.org/scripts/265
- // ==/UserScript==
-
- // 当スクリプトはpixivが作成、配布しているアプリケーションではありません。
- // <https://www.pixiv.net/terms/?page=brand>
-
- 'use strict';
-
- // L10N
- Gettext.setLocalizedTexts({
- /*eslint-disable quote-props, max-len */
- 'en': {
- '完全一致': 'Exact match',
- '部分一致': 'Partial match',
- 'タイトル・キャプション': 'Title/Description',
- '小説': 'Novels',
- 'タグ': 'Tags',
- 'キーワード': 'Keyword',
- '本文': 'Content',
- 'ユーザー': 'User',
- 'グループ': 'Groups',
- 'すべて': 'All',
- '検索': 'Search',
- '小説検索': 'Search novel',
- 'ユーザー検索': 'Search user',
- 'グループ検索': 'Search group',
- 'pixiv 検索オプションを追加': 'pixiv Search Options',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': 'If you set this, the page header becomes higher.',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': 'If you have opened pixiv pages in other tabs, this setting will reflect after the tab refresh.',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': 'In search result pages of illusts and mangas, maintains current search mode',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': 'If you unset this, selects “Partial match” radio button by default.',
- },
- 'fr': {
- '完全一致': 'Concordance parfaite',
- '部分一致': 'Concordance partielle',
- 'タイトル・キャプション': 'Titre, Légende',
- '小説': 'Roman',
- 'タグ': 'Mots-clés',
- 'キーワード': 'Mots-clés',
- '本文': 'Contenu',
- 'ユーザー': 'Utilisateur',
- 'グループ': 'Groups',
- 'すべて': 'Tout',
- '検索': 'Rechercher',
- '小説検索': 'Rechercher un roman',
- 'ユーザー検索': '',
- 'グループ検索': 'Rechercher un groupe',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- 'ko': {
- '完全一致': '완전 일치',
- '部分一致': '부분 일치',
- 'タイトル・キャプション': '제목・캡션',
- '小説': '소설',
- 'タグ': '태그',
- 'キーワード': '키워드',
- '本文': '본문',
- 'ユーザー': '유저',
- 'グループ': '그룹',
- 'すべて': '전체',
- '検索': '검색',
- '小説検索': '소설 검색',
- 'ユーザー検索': '유저 검색',
- 'グループ検索': '그룹 검색',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- 'ru': {
- '完全一致': 'Полное совпадение',
- '部分一致': 'Частичное совпадение',
- 'タイトル・キャプション': 'Заголовок',
- '小説': 'Рассказы',
- 'タグ': 'Метка',
- 'キーワード': 'Ключевые слова',
- '本文': 'Текст',
- 'ユーザー': 'Пользователь',
- 'グループ': 'Группа',
- 'すべて': 'Все',
- '検索': 'Поиск',
- '小説検索': 'Искать рассказ',
- 'ユーザー検索': 'Искать пользователя',
- 'グループ検索': 'Искать группу',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- 'th': {
- '完全一致': '',
- '部分一致': '',
- 'タイトル・キャプション': 'ชื่อและคำบรรยาย',
- '小説': 'นิยาย',
- 'タグ': 'แท็ก',
- 'キーワード': 'คีย์เวิร์ด',
- '本文': '',
- 'ユーザー': 'ผู้ใช้',
- 'グループ': '',
- 'すべて': 'ทั้งหมด',
- '検索': 'ค้นหา',
- '小説検索': '',
- 'ユーザー検索': '',
- 'グループ検索': '',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- 'zh': {
- '完全一致': '完全相同',
- '部分一致': '部分相同',
- 'タイトル・キャプション': '题目/简述',
- '小説': '小说',
- 'タグ': '标签',
- 'キーワード': '关键词',
- '本文': '内容',
- 'ユーザー': '用户',
- 'グループ': '群组',
- 'すべて': '全部',
- '検索': '搜索',
- '小説検索': '搜索小说',
- 'ユーザー検索': '搜索用户',
- 'グループ検索': '搜索群组',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- 'zh-tw': {
- '完全一致': '完全相同',
- '部分一致': '部分相同',
- 'タイトル・キャプション': '題目/簡述',
- '小説': '小說',
- 'タグ': '標籤',
- 'キーワード': '關鍵詞',
- '本文': '內容',
- 'ユーザー': '用戶',
- 'グループ': '群組',
- 'すべて': '全部',
- '検索': '搜索',
- '小説検索': '搜索小說',
- 'ユーザー検索': '搜索用戶',
- 'グループ検索': '搜索群組',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- 'es': {
- '完全一致': 'Coincidencia exacta',
- '部分一致': 'Coincidencia parcial',
- 'タイトル・キャプション': 'Título/Descripción',
- '小説': 'Novelas',
- 'タグ': 'Etiquetas',
- 'キーワード': 'Palabra clave',
- '本文': 'Mensaje',
- 'ユーザー': 'Usuarios',
- 'グループ': 'Grupo',
- 'すべて': 'Todos',
- '検索': 'Buscar',
- '小説検索': 'Buscar novela',
- 'ユーザー検索': 'Buscar usuario',
- 'グループ検索': 'Buscar grupo',
- 'pixiv 検索オプションを追加': '',
- 'この設定をオンにすると、ヘッダの高さが大きくなります。': '',
- '別のタブでpixivを開いていた場合、この設定の変更はそのタブの再読み込み後に反映されます。': '',
- 'イラスト・マンガの検索結果ページで、検索モードを維持': '',
- 'この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。': '',
- },
- /*eslint-enable quote-props, max-len */
- });
-
- /**
- * スクリプトの起動。
- */
- class PixivSearchOptions
- {
- constructor()
- {
- this.id = Math.random();
-
- this.observeURLChanged();
-
- startScript(
- () => {
- if (document.body.classList.contains('not-logged-in')) {
- return;
- }
-
- /**
- * 検索窓。
- * @type {HTMLInputElement}
- * @access protected
- */
- this.word = document.querySelector(
- '#root form input[type="text"], #js-mount-point-header form input[type="text"]' // 後者は仕様変更前
- );
-
- this.main();
- },
- parent => parent.matches('#root, #js-mount-point-header div'), // 後者は仕様変更前
- target => target.matches('#root > div:first-child')
- || /* 仕様変更前 */ target.firstElementChild && target.firstElementChild.localName === 'form',
- () => document.querySelector(
- '#root form input[type="text"], #js-mount-point-header form input[type="text"]' // 後者は仕様変更前
- ),
- {},
- 60 * 1000
- );
- }
-
- observeURLChanged()
- {
- addEventListener('message', this);
-
- GreasemonkeyUtils.executeOnUnsafeContext(function (id) {
- History.prototype.pushState = new Proxy(History.prototype.pushState, {
- apply(target, thisArg, argumentsList)
- {
- postMessage({ id, url: argumentsList[2] }, location.origin);
- return Reflect.apply(target, thisArg, argumentsList);
- },
- });
- }, [this.id]);
- }
-
- /**
- * @param {Event}
- */
- handleEvent(event)
- {
- switch (event.type) {
- case 'message': {
- if (event.origin !== location.origin || typeof event.data !== 'object' || event.data === null
- || event.data.id !== this.id || !event.data.url) {
- break;
- }
- this.reflectSearchMode(new URL(event.data.url, location));
- break;
- }
- case 'submit': {
- const form = event.target;
- if (this.word.value === '') {
- break;
- }
-
- const searchMode = form.querySelector('[name="s_mode"]:checked');
- const subOption = form.querySelector('[name="s_sub_mode"]:checked');
-
- if (searchMode.value === 's_tag' || searchMode.value === 's_novel' && subOption.value === 's_tag') {
- // 検索窓のデフォルトの検索モードなら
- break;
- }
-
- event.stopPropagation();
-
- if (searchMode.dataset.formAction) {
- form.action = searchMode.dataset.formAction;
- }
- this.word.name = searchMode.dataset.wordName || 'word';
- if (searchMode.dataset.modeName) {
- searchMode.name = searchMode.dataset.modeName;
- }
-
- const novel = searchMode.value === 's_novel';
- if (subOption) {
- // 副検索モードが存在すれば
- searchMode.value = subOption.value;
- subOption.removeAttribute('name');
- }
-
- if (['s_usr', 's_group'].includes(searchMode.value)) {
- // ユーザー、グループの検索なら
- if (form.action !== location.origin + location.pathname) {
- // サービス間をまたぐ場合、不要なパラメーターは送信しない
- for (const hiddenParam of form.querySelectorAll('[type="hidden"]')) {
- hiddenParam.disabled = true;
- }
- }
- break;
- }
-
- const result = /^\/tags\/[^/]+\/([a-z]+)$/.exec(location.pathname);
- const currentMode = result ? result[1] : '';
- if (!currentMode || currentMode !== 'novels' && novel || currentMode === 'novels' && !novel
- || decodeURIComponent(/^\/tags\/([^/]+)/u.exec(location.pathname)[1]) !== this.word.value) {
- // 検索結果以外のページ、サービス間をまたぐ場合、
- // またはキーワードが現在のページと異なる場合
- form.action = `/tags/${encodeURIComponent(this.word.value)}/${novel ? 'novels' : 'artworks'}`;
- this.word.removeAttribute('name');
- if (searchMode.value === 's_tag_full') {
- // タグ完全一致なら、s_modeを削除
- searchMode.removeAttribute('name');
- }
- break;
- }
-
- // イラストからイラスト、小説から小説の場合
- event.preventDefault();
-
- // 検索オプションボタンをクリック
- document.querySelector('[d^="M0 1C0 0.447754"]').closest('button').click();
- new MutationObserver((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.querySelectorAll('[class$="-dummyInput"]')[novel ? 0 : 1];
- // プルダウンメニューを開く
- 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[(novel
- ? ['s_tag_only', 's_tag_full', 's_tc', 's_tag']
- : ['s_tag', 's_tag_full', 's_tc']).indexOf(searchMode.value)].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();
- }, 100);
- }).observe(select.parentElement, { childList: true });
- }).observe(dialog, { subtree: true, childList: true });
- }).observe(document.body, { childList: true });
- break;
- }
- case 'change': {
- // 副検索モードの選択
- const subOptions = event.target.parentElement.parentElement;
- if (event.target.name === 's_mode') {
- // 検索モードの選択なら
- if (subOptions.classList.contains('sub-options')) {
- // 副検索モードが存在すれば
- subOptions.querySelector('[value="s_tag"], [value="keyword_all"]').checked = true;
- } else {
- const subOption = event.currentTarget.querySelector('[name="s_sub_mode"]:checked');
- if (subOption) {
- subOption.checked = false;
- }
- }
- this.word.placeholder = event.target.dataset.placeholder;
- } else if (event.target.name === 's_sub_mode') {
- // 副検索モードの選択なら
- const subOption = subOptions.firstElementChild.firstElementChild;
- subOption.checked = true;
- this.word.placeholder = subOption.dataset.placeholder;
- }
- break;
- }
- case 'dblclick':
- // ラベルをダブルクリックで検索
- if (event.target.matches('label, label *')) {
- event.currentTarget.dispatchEvent(new Event('submit'));
- }
- break;
- }
- }
-
- main()
- {
- // 言語の設定
- Gettext.setLocale(document.documentElement.lang);
-
- /**
- * 検索オプションを設定するラジオボタンのHTML文字列。
- * @type {string}
- */
- const optionsHTML = h`
- <label>
- <input data-placeholder="${_('検索')}" value="s_tag_full" name="s_mode" type="radio" />
- ${_('完全一致')}
- </label>
- <label>
- <input data-placeholder="${_('検索')}" value="s_tag" name="s_mode" type="radio" />
- ${_('部分一致')}
- </label>
- <label>
- <input data-placeholder="${_('検索')}" value="s_tc" name="s_mode" type="radio" />
- ${_('タイトル・キャプション')}
- </label>
- <div id="s_novel" class="sub-options" title="${_('小説')}">
- <label>
- <input data-placeholder="${_('小説検索')}" value="s_novel" name="s_mode" type="radio" />
- <img alt="${_('小説')}" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAARBAMAAADNtor0AAAAD1BMVEVldC2Spb6susrCzdr9//waY6iPAAAAAXRSTlMAQObYZgAAAEFJREFUCNdjYBAUZIABERdDBNNZAM6ECQsCmY6CQCDAIAJiugABJtPZ2NiEfFFDIROYqIuzMkzUxcXZCMIUhAEBACaMGgfSfm2+AAAAAElFTkSuQmCC" />
- </label>
- <label>
- <input value="s_tag_full" name="s_sub_mode" type="radio" />
- ${_('タグ')}
- </label>
- <label>
- <input value="s_tag" name="s_sub_mode" type="radio" />
- ${_('キーワード')}
- </label>
- <label>
- <input value="s_tc" name="s_sub_mode" type="radio" />
- ${_('本文')}
- </label>
- </div>
- <label title="${_('ユーザー')}">
- <input data-placeholder="${_('ユーザー検索')}" data-word-name="nick"
- data-form-action="https://www.pixiv.net/search_user.php" value="s_usr" name="s_mode" type="radio" />
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048" width="16px" height="16px">
- <title>${_('ユーザー')}</title>
- <path fill="#809DB8" d="M848,348 l98,-20 q28,0 54,22 q38,32 48,44 q22,26 34,26 q16,0 26,-4 q14,-6 52,2 q32,6 48,24 q18,20 23,42 t5,50 q0,32 2,48 q-4,24 -2,38 q6,36 6,62 t-6,44 t12,28 t38,10 h22 q28,0 40,46 q8,30 8,60 q0,14 -2,30 l-6,46 q-2,16 -6,22 q-6,10 -10,16 t-14,14 l-18,12 l-24,16 q-18,10 -30,18 q-22,62 -50,102 q-34,48 -58,70 l-22,22 q-10,96 -10,118 v6 q0,8 2,16 q0,8 4,22 q4,18 12,28 l20,28 q10,14 30,28 q8,6 46,26 q26,14 62,24 q46,12 82,18 q88,12 138,28 q60,18 102,44 t62,40 t50,46 q28,30 30,32 q20,22 40,98 q20,74 20,140 v66 h-1768 v-72 q0,-42 14,-126 q16,-92 34,-106 q8,-6 32,-34 t46,-48 t62,-46 q36,-24 100,-44 q66,-22 140,-32 q60,-8 102,-22 q40,-12 74,-32 t50,-34 q26,-24 34,-36 q10,-16 16,-30 q12,-18 10,-20 q-4,-6 -2,-6 v-132 q-4,-2 -12,-8 q-8,-4 -28,-22 q-14,-10 -40,-36 q-18,-18 -38,-50 t-32,-64 l-36,-18 q-20,-10 -24,-14 q-4,-2 -12,-18 q-8,-14 -8,-16 q0,-4 -2,-18 t-4,-22 q0,-4 2,-10 t5,-26 t3,-38 q0,-20 8,-38 q10,-32 12,-36 q8,-14 18,-24 t22,-10 h14 q8,-2 18,-12 q12,-12 8,-30 q2,-64 2,-134 q0,-22 8,-50 q10,-38 18,-58 q12,-32 38,-56 t60,-32 l52,-12 h-2 q2,0 6,-2 q10,-2 12,-2 v-6 v-4 q-2,-4 0,-6 q0,-2 8,-4 t22,-2 z" />
- </svg>
- </label>
- <div id="s_group" class="sub-options" title="${_('グループ')}">
- <label>
- <input data-placeholder="${_('グループ検索')}" data-mode-name="mode"
- data-form-action="https://www.pixiv.net/group/search_group.php"
- value="s_group" name="s_mode" type="radio" />
- <img alt="${_('グループ')}" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAADFBMVEWLpryswdDV3ubr8POkfP3KAAAAT0lEQVQI12NYBQQMYGLd7/e/GFYtX/6LYfX27fsYVqU/i2JYwX4hjGEJk4MYw0Y2hSiGLUwNcgzrVr9aBxLjY1jCDOJGP93FsPrf/ldgUwDsuCQfQnDv1AAAAABJRU5ErkJggg==" />
- </label>
- <label>
- <input value="keyword_all" name="s_sub_mode" type="radio" />
- ${_('すべて')}
- </label>
- <label>
- <input value="tag_full" name="s_sub_mode" type="radio" />
- ${_('完全一致')}
- </label>
- <label>
- <input value="tag" name="s_sub_mode" type="radio" />
- ${_('部分一致')}
- </label>
- <label>
- <input value="keyword" name="s_sub_mode" type="radio" />
- ${_('タイトル・キャプション')}
- </label>
- </div>`;
-
- const form = this.word.form;
- form.id = 'suggest-container';
-
- this.style({
- gridElementSelector: Array.from(form.parentElement.parentElement.classList).map(cls => '.' + cls).join(''),
- });
-
- // 検索オプションを設定するラジオボタンの追加
- form.insertAdjacentHTML('beforeend', optionsHTML);
-
- form.parentElement.addEventListener('submit', this, true);
- form.addEventListener('change', this);
- form.addEventListener('dblclick', this);
-
- // 現在の検索モードを設定
- this.reflectSearchMode(location);
- }
-
- /**
- * 現在のページの検索モードをもとに、検索窓の検索モードを設定します。
- * @param {string} url
- * @returns {void}
- */
- reflectSearchMode(url)
- {
- const { pathname, searchParams } = new URL(url);
-
- let mode, subMode;
-
- const form = this.word.form;
-
- if (pathname.startsWith('/tags/')) {
- mode = searchParams.get('s_mode');
- if (pathname.endsWith('novels')) {
- // 小説の検索結果
- subMode = mode;
- mode = 's_novel';
- } else {
- // イラストの検索結果
- const currentMode = mode || 's_tag_full';
- mode = 's_tag';
-
- GM.getValue('check-match-full-by-default-on-full-match-result', false).then(function (value) {
- if (value) {
- form.querySelector(`[value="${currentMode}"]`).click();
- }
- });
- }
- } else if (pathname.startsWith('/novel/')) {
- mode = 's_novel';
- } else if (pathname === '/search_user.php') {
- mode = 's_usr';
- } else if (pathname.startsWith('/group/')) {
- mode = 's_group';
- } else {
- mode = 's_tag';
- }
-
- form.querySelector(!subMode ? `[value="${mode}"]` : `#${mode} [value="${subMode}"]`).click();
- }
-
- /**
- * スタイルシートを設定します。
- * @access protected
- */
- style({ gridElementSelector })
- {
- document.head.insertAdjacentHTML('beforeend', `<style>
- ${gridElementSelector} {
- grid-template-columns: 1fr minmax(auto, 970px) 1fr;
- }
-
- #suggest-container {
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- /*------------------------------------
- 検索オプション
- */
- #suggest-container > div:first-of-type {
- padding-right: 1em;
- }
- #suggest-container label {
- padding: 0 0.7em;
- display: flex;
- align-items: center;
- white-space: nowrap;
- }
- #suggest-container label input {
- margin-right: 0.3em;
- width: initial;
- }
-
- /*------------------------------------
- 副検索モード
- */
- #suggest-container .sub-options label:not(:first-of-type) {
- display: none;
- position: absolute;
- z-index: 1;
- width: 13em;
- height: 2em;
- border: solid 1px #D6DEE5;
- border-top-style: none;
- border-bottom-style: none;
- background: #FFFFFF;
- }
- #suggest-container .sub-options label:nth-of-type(2) {
- border-top-style: solid;
- border-radius: 5px 5px 0 0;
- }
- #suggest-container .sub-options label:nth-of-type(3) {
- margin-top: 2em;
- }
- #suggest-container .sub-options label:nth-of-type(4) {
- margin-top: 4em;
- }
- #suggest-container .sub-options label:nth-of-type(5) {
- margin-top: 6em;
- }
- #suggest-container .sub-options label:last-of-type {
- border-bottom-style: solid;
- border-radius: 0 0 5px 5px;
- }
- #suggest-container .sub-options:hover label {
- display: flex;
- }
- #suggest-container .sub-options:hover::after {
- display: block;
- }
- </style>`);
- }
- }
-
- const pixivSearchOptions = new PixivSearchOptions();
-
- /**
- * 設定を管理します。
- */
- class PixivSearchOptionsSettings
- {
- constructor()
- {
- startScript(
- async () => {
- GM.registerMenuCommand(_('pixiv 検索オプションを追加'), () => this.showDialog());
- },
- parent => parent === document.documentElement,
- target => target === document.body,
- () => document.body
- );
- }
-
- /**
- * 設定ダイアログを表示します。
- */
- async showDialog()
- {
- if (!this.dialog) {
- this.initialize();
- }
-
- const form = document.forms['pixiv-search-options-settings'];
- form['check-match-full-by-default-on-full-match-result'].checked
- = await GM.getValue('check-match-full-by-default-on-full-match-result', false);
-
- this.dialog.showModal();
- }
-
- /**
- * @param {Event} event
- */
- handleEvent(event)
- {
- switch (event.type) {
- case 'change':
- GM.setValue(event.target.name, event.target.checked);
- break;
-
- case 'click':
- this.dialog.close();
- break;
- }
- }
-
- /**
- * 設定ダイアログを構築します。
- * @access private
- */
- initialize()
- {
- document.head.insertAdjacentHTML('beforeend', `<style>
- /*====================================
- pixiv 検索オプションを表示 設定ダイアログ
- */
- #pixiv-search-options-settings {
- border-radius: 0.5em;
- border-color: #69AFCA;
- border-width: 0.5em;
- }
-
- #pixiv-search-options-settings h1 {
- font-size: 1.5em;
- font-weight: bold;
- margin-bottom: 0.5em;
- text-align: center;
- }
-
- #pixiv-search-options-settings form {
- color: #333333;
- }
-
- #pixiv-search-options-settings form ul li {
- margin: 0.2em 0;
- }
-
- #pixiv-search-options-settings form label {
- display: flex;
- align-items: center;
- }
-
- #pixiv-search-options-settings form small {
- margin-left: 1.3em;
- }
-
- #pixiv-search-options-settings form [name="close"] {
- border: none;
- position: absolute;
- border-radius: 50%;
- position: absolute;
- top: -1em;
- right: -1em;
- width: 2.5em;
- height: 2.5em;
- background:
- black url("https://source.pixiv.net/www/images/common/icon_modal_close.png") no-repeat 50% 50%;
- cursor: pointer;
- overflow: hidden;
- white-space: nowrap;
- text-indent: 200%;
- }
- </style>`);
-
- document.body.insertAdjacentHTML('afterbegin', h`<dialog id="pixiv-search-options-settings">
- <h1>${_('pixiv 検索オプションを追加')}</h1>
- <form name="pixiv-search-options-settings">
- <ul>
- <li>
- <label>
- <input type="checkbox" name="check-match-full-by-default-on-full-match-result" />
- ${_('イラスト・マンガの検索結果ページで、検索モードを維持')}
- </label>
- <p><small class="settingColor">${_('この設定をオフにすると、最初は「部分一致」にチェックが入った状態になります。')}</small></p>
- </li>
- </ul>
- <button type="button" name="close">${_('閉じる')}</button>
- </form>
- </dialog>`);
-
- this.dialog = document.getElementById('pixiv-search-options-settings');
- this.polyfillDialogElements([this.dialog]);
-
- const form = document.forms['pixiv-search-options-settings'];
- form.addEventListener('change', this);
- form.close.addEventListener('click', this);
- }
-
- /**
- * Microsoft Edge、およびFirefox向けに、dialog要素のpolyfillを行います。
- * @access private
- * @param {HTMLDialogElement[]} dialogs
- * @returns {Promise.<void>}
- */
- async polyfillDialogElements(dialogs)
- {
- document.head.insertAdjacentHTML(
- 'beforeend',
- h`<link rel="stylesheet" href="${await GM.getResourceUrl('dialog-polyfill.css')}" />`
- );
-
- for (const dialog of dialogs) {
- /*globals dialogPolyfill */
- dialogPolyfill.registerDialog(dialog);
- }
- }
- }
-
- new PixivSearchOptionsSettings();