Greasy Fork is available in English.

Append Tag Searching Tub

Adds “Keyword”, “Tags”, “My List”, “Images” and “Live” search tabs to all of the Niconico search boxes.

  1. // ==UserScript==
  2. // @name Append Tag Searching Tub
  3. // @name:ja niconico タグ検索タブを追加
  4. // @description Adds “Keyword”, “Tags”, “My List”, “Images” and “Live” search tabs to all of the Niconico search boxes.
  5. // @description:ja 『ニコニコ』各サービスの検索窓について、「キーワード」「タグ」「マイリスト」「静画」「生放送」検索タブが5つとも含まれるように補完します。
  6. // @namespace http://loda.jp/script/
  7. // @version 5.3.0
  8. // @match https://www.nicovideo.jp/
  9. // @match https://www.nicovideo.jp/?*
  10. // @match https://www.nicovideo.jp/#*
  11. // @match https://www.nicovideo.jp/tag/*
  12. // @match https://www.nicovideo.jp/related_tag/*
  13. // @match https://www.nicovideo.jp/mylist*
  14. // @match https://www.nicovideo.jp/search/*
  15. // @match https://seiga.nicovideo.jp/*
  16. // @match https://live.nicovideo.jp/*
  17. // @match https://com.nicovideo.jp/*
  18. // @match *://blog.nicovideo.jp/en_info/*
  19. // @match *://tw.blog.nicovideo.jp/*
  20. // @require https://gitcdn.xyz/cdn/greasemonkey/gm4-polyfill/a834d46afcc7d6f6297829876423f58bb14a0d97/gm4-polyfill.js
  21. // @require https://greasyfork.org/scripts/19616/code/utilities.js?version=868689
  22. // @license MPL-2.0
  23. // @contributionURL https://www.amazon.co.jp/registry/wishlist/E7PJ5C3K7AM2
  24. // @compatible Edge
  25. // @compatible Firefox 推奨 / Recommended
  26. // @compatible Opera
  27. // @compatible Chrome
  28. // @grant GM.setValue
  29. // @grant GM_setValue
  30. // @grant GM.getValue
  31. // @grant GM_getValue
  32. // @grant GM.deleteValue
  33. // @grant GM_deleteValue
  34. // @grant GM.xmlHttpRequest
  35. // @grant GM_xmlhttpRequest
  36. // @connect www.nicovideo.jp
  37. // @run-at document-start
  38. // @icon https://nicovideo.cdn.nimg.jp/uni/images/favicon/144.png
  39. // @author 100の人
  40. // @homepageURL https://greasyfork.org/scripts/268
  41. // ==/UserScript==
  42.  
  43. 'use strict';
  44.  
  45. // L10N
  46. Gettext.setLocalizedTexts({
  47. /*eslint-disable quote-props, max-len */
  48. 'en': {
  49. 'キーワード': 'Keyword',
  50. '動画をキーワードで検索': 'Search Video by Keyword',
  51. 'タグ': 'Tags',
  52. '動画をタグで検索': 'Search Video by Tag',
  53. 'マイリスト': 'My List',
  54. 'マイリストを検索': 'Search My List',
  55. '静画': 'Images',
  56. '静画を検索': 'Search Images',
  57. '生放送': 'Live',
  58. '番組を探す': 'Search Live Program',
  59. 'マンガ': 'Comics',
  60. },
  61. 'zh': {
  62. 'キーワード': '關鍵字',
  63. '動画をキーワードで検索': '',
  64. 'タグ': '標籤',
  65. '動画をタグで検索': '',
  66. 'マイリスト': '我的清單',
  67. 'マイリストを検索': '搜尋我的清單',
  68. '静画': '靜畫',
  69. '静画を検索': '搜尋靜畫',
  70. '生放送': '生放送',
  71. '番組を探す': '搜尋節目',
  72. 'マンガ': '漫畫',
  73. },
  74. /*eslint-enable quote-props, max-len */
  75. });
  76.  
  77.  
  78.  
  79. /**
  80. * 追加したタブバーから新しいタブで検索結果を開いたとき、選択中のタブを元に戻す遅延時間 (ミリ秒)。
  81. * @constant {number}
  82. */
  83. const CURRENT_TAB_RESTORATION_DELAY = 1000;
  84.  
  85. /**
  86. * 表示しているページの種類。
  87. * @type {string}
  88. */
  89. let pageType;
  90.  
  91. // ページの種類を取得
  92. switch (location.host) {
  93. case 'www.nicovideo.jp':
  94. if (location.pathname === '/') {
  95. // 総合トップページ
  96. pageType = 'top';
  97. } else if (location.pathname.startsWith('/search/')) {
  98. // 動画キーワード検索ページ
  99. pageType = 'videoSearch';
  100. } else if (location.pathname.startsWith('/mylist_search')) {
  101. // マイリスト検索ページ
  102. pageType = 'mylist';
  103. } else if (/^\/(?:(?:tag|related_tag)\/|(?:mylist|recent|newarrival|openlist|video_catalog)(?:\/|$))/
  104. .test(location.pathname)) {
  105. // 動画タグ検索ページと公開マイリスト等
  106. pageType = 'tag';
  107. } else if (location.pathname.startsWith('/user/')) {
  108. // ユーザーページ
  109. pageType = 'user';
  110. }
  111. break;
  112. case 'seiga.nicovideo.jp':
  113. pageType = location.pathname.startsWith('/search/')
  114. // 静画検索ページ
  115. ? 'imageSearch'
  116. // 静画ページ
  117. : 'image';
  118. break;
  119. case 'live.nicovideo.jp':
  120. pageType = location.pathname.startsWith('/search')
  121. // 生放送検索ページ
  122. ? 'liveSearch'
  123. // 生放送ページ
  124. : 'live';
  125. break;
  126. case 'blog.nicovideo.jp':
  127. // 英語版ニコニコインフォ
  128. pageType = 'info_en';
  129. break;
  130. case 'tw.blog.nicovideo.jp':
  131. // 台湾版ニコニコインフォ
  132. pageType = 'info_tw';
  133. break;
  134. }
  135.  
  136. waitTarget(() => document.documentElement).then(function () {
  137. Gettext.setLocale(document.documentElement.lang);
  138. });
  139.  
  140. if (pageType.startsWith('info_')) {
  141. // 英語版、または台湾版のニコニコインフォなら
  142. waitTarget(() => document.getElementById('siteHeaderLeftMenu')).then(function () {
  143. // 生放送へのリンクを取得
  144. const itemLive = document.querySelector('#siteHeader [href*="://live.nicovideo.jp/"]').parentElement;
  145. // 生放送リンクの複製
  146. const item = itemLive.cloneNode(true);
  147. // リンク文字を変更
  148. item.getElementsByTagName('span')[0].textContent = _('静画');
  149. // アドレスを変更
  150. item.getElementsByTagName('a')[0].href = 'https://seiga.nicovideo.jp/';
  151. // ヘッダに静画へのリンクを追加
  152. itemLive.before(item);
  153. });
  154. } else {
  155. // ページの種類別に、実行する関数を切り替える。
  156. switch (pageType) {
  157. case 'videoSearch': // 動画キーワード
  158. case 'mylist': // マイリスト
  159. waitTarget(() => document.getElementById('search_united_form')).then(addTagSearchTabAboveSearchBox);
  160. break;
  161.  
  162. case 'top':
  163. // トップページ
  164. addTagSearchButtonToTopPage();
  165. break;
  166.  
  167. case 'imageSearch':
  168. // 静画キーワード
  169. waitTarget(() => document.getElementById('usearch_form_input')).then(addTagSearchTabAboveSearchBox);
  170. break;
  171.  
  172. case 'image':
  173. // 静画
  174. waitTarget(() => document.getElementById('search_button')).then(careteTabsBarToSearchBox);
  175. break;
  176.  
  177. case 'liveSearch': {
  178. // 生放送キーワード
  179. const forms = document.getElementsByClassName('search-form');
  180. waitTarget(() => forms[0]).then(addTagSearchTabAboveSearchBox);
  181. break;
  182. }
  183. case 'live': {
  184. // 生放送
  185. const words = document.getElementsByClassName('search_word');
  186. waitTarget(() => words[0]).then(careteTabsBarToSearchBox);
  187. break;
  188. }
  189.  
  190. case 'tag':
  191. if (document.doctype.publicId) {
  192. // 公開マイリスト等
  193. waitTarget(() => document.getElementById('target_m')).then(addOtherServiceTabsAboveSearchBox);
  194. } else {
  195. // 動画タグ
  196. const mylists = document.getElementsByClassName('optMylist');
  197. waitTarget(() => mylists[0]).then(addOtherServiceTabsAboveSearchBox);
  198. }
  199. break;
  200.  
  201. case 'user': {
  202. // ユーザー
  203. const outers = document.getElementsByClassName('optionOuter');
  204. waitTarget(() => outers[0]).then(addImageLinkToUserPageMenu);
  205. break;
  206. }
  207. }
  208. }
  209.  
  210.  
  211.  
  212. /**
  213. * 各サービスのキーワード検索ページの検索窓に、動画の「タグ」検索タブを追加する。
  214. */
  215. function addTagSearchTabAboveSearchBox()
  216. {
  217. // マイリスト検索タブの取得
  218. const mylistTab = document.querySelector('.tab_table td:nth-of-type(2), #search_frm_a a:nth-of-type(2), .search_tab_list li:nth-of-type(2), .seachFormA a:nth-of-type(2), li:nth-of-type(2).search-tab-item');
  219.  
  220. // マイリスト検索タブの複製
  221. const tagTab = mylistTab.cloneNode(true);
  222.  
  223. // タブ名を変更
  224. const anchor = tagTab.tagName.toLowerCase() === 'a' ? tagTab : tagTab.getElementsByTagName('a')[0];
  225. let tabNameNode = anchor.getElementsByTagName('div');
  226. tabNameNode = (tabNameNode.length > 0 ? tabNameNode[0].firstChild : anchor.firstChild);
  227. tabNameNode.data = _('タグ') + (pageType === 'liveSearch' ? '' : ' ( ');
  228.  
  229. // クラス名を変更・動画件数をリセット
  230. const searchCount = tagTab.querySelector('strong, span');
  231. switch (pageType) {
  232. case 'videoSearch':
  233. searchCount.classList.remove('more');
  234. break;
  235. case 'mylist':
  236. searchCount.style.removeProperty('color');
  237. break;
  238. case 'imageSearch':
  239. searchCount.classList.remove('search_value_em');
  240. searchCount.classList.add('search_value');
  241. break;
  242. }
  243. searchCount.textContent = '-';
  244.  
  245. if (searchCount.id) {
  246. // 生放送
  247. searchCount.id = 'search_count_tag';
  248. }
  249.  
  250. // 検索語句を取得
  251. const searchWordsPattern = /(?:\/(?:search|tag|mylist_search)\/|[?&]keyword=)([^?&#]+)/g;
  252. const result = location.href.match(searchWordsPattern);
  253. const searchWords
  254. = result ? searchWordsPattern.exec(result[pageType === 'liveSearch' ? result.length - 1 : 0])[1] : '';
  255.  
  256. // タグが付いた動画件数を取得・表示
  257. if (searchWords && location.host !== 'www.live.nicovideo.jp') {
  258. GM.xmlHttpRequest({
  259. method: 'GET',
  260. url: 'https://www.nicovideo.jp/tag/' + searchWords,
  261. onload: function (response) {
  262. const responseDocument = new DOMParser().parseFromString(response.responseText, 'text/html');
  263. const total = responseDocument.querySelector('.tagCaption .dataValue .num').textContent;
  264.  
  265. const trimmedThousandsSep = total.replace(/,/g, '');
  266. if (trimmedThousandsSep >= 100) {
  267. // 動画件数が100件を超えていれば
  268. switch (pageType) {
  269. case 'videoSearch':
  270. searchCount.classList.add('more');
  271. break;
  272. case 'mylist':
  273. searchCount.style.color = '#CC0000';
  274. break;
  275. case 'imageSearch':
  276. searchCount.classList.remove('search_value');
  277. searchCount.classList.add('search_value_em');
  278. break;
  279. case 'liveSearch':
  280. searchCount.classList.add('strong');
  281. break;
  282. }
  283. }
  284.  
  285. switch (pageType) {
  286. case 'mylist':
  287. searchCount.textContent = ' ' + total + ' ';
  288. break;
  289. case 'videoSearch':
  290. case 'imageSearch':
  291. searchCount.textContent = total;
  292. break;
  293. case 'liveSearch':
  294. searchCount.textContent = trimmedThousandsSep;
  295. break;
  296. }
  297. },
  298. });
  299. }
  300.  
  301. // 非アクティブタブを取得
  302. const inactiveTab = document.querySelector('.tab_0, .tab1, .search_tab_list a:not(.active), .search-tab-anchor');
  303.  
  304. // クラス名を変更
  305. anchor.className = inactiveTab.className;
  306.  
  307. // アドレスを変更
  308. anchor.href = 'https://www.nicovideo.jp/tag/' + searchWords + inactiveTab.search;
  309.  
  310. // タグ検索タブを追加
  311. mylistTab.parentNode.insertBefore(tagTab, mylistTab);
  312. if (pageType === 'liveSearch') {
  313. mylistTab.parentNode.insertBefore(new Text(' '), mylistTab);
  314. } else if (inactiveTab.classList.contains('tab1')) {
  315. // GINZAバージョン
  316. mylistTab.parentNode.insertBefore(tagTab.previousSibling.cloneNode(true), mylistTab);
  317. }
  318. }
  319.  
  320.  
  321.  
  322. /**
  323. * ニコニコ動画の上部に表示されている検索窓に、「静画」「生放送」を検索するタブを追加する。
  324. */
  325. function addOtherServiceTabsAboveSearchBox()
  326. {
  327. // スタイルの設定
  328. document.head.insertAdjacentHTML('beforeend', `<style>
  329. :root {
  330. --max-search-box-width: 268px;
  331. }
  332. #PAGEHEADER > div {
  333. display: flex;
  334. }
  335. #head_search {
  336. max-width: var(--max-search-box-width);
  337. flex-grow: 1;
  338. }
  339. #search_input {
  340. width: 100%;
  341. display: flex;
  342. }
  343. #search_input .typeText {
  344. flex-grow: 1;
  345. }
  346. #head_ads {
  347. margin-right: -26px;
  348. }
  349. #search_input #bar_search {
  350. box-sizing: border-box;
  351. width: 100% !important;
  352. }
  353. /*====================================
  354. GINZAバージョン
  355. */
  356. .siteHeader > .inner {
  357. display: flex;
  358. }
  359. .videoSearch {
  360. max-width: var(--max-search-box-width);
  361. flex-grow: 1;
  362. padding-left: 4px;
  363. padding-right: 4px;
  364. }
  365. .videoSearchOption {
  366. display: flex;
  367. white-space: nowrap;
  368. }
  369. .videoSearch form {
  370. display: flex;
  371. }
  372. .videoSearch form .inputText {
  373. flex-grow: 1;
  374. }
  375. /*------------------------------------
  376. ×ボタン
  377. */
  378. .clear-button-inner-tag {
  379. left: initial;
  380. right: 3px;
  381. }
  382. </style>`);
  383.  
  384. // タブリストの取得
  385. const mylistTab = document.querySelector('#target_t, .optMylist');
  386.  
  387. // タブの複製・追加
  388. mylistTab.parentElement.append(...[
  389. {
  390. type: 'image',
  391. title: _('静画を検索'),
  392. url: 'https://seiga.nicovideo.jp/search',
  393. text: _('静画'),
  394. },
  395. {
  396. type: 'live',
  397. title: _('番組を探す'),
  398. url: 'https://live.nicovideo.jp/search',
  399. text: _('生放送'),
  400. },
  401. ].map(function (option) {
  402. const tab = mylistTab.cloneNode(true);
  403. if (mylistTab.classList.contains('optMylist')) {
  404. // GINZAバージョン
  405. tab.classList.remove('optMylist');
  406. tab.classList.add('opt' + option.type[0].toUpperCase() + option.type.slice(1));
  407. tab.dataset.type = option.type;
  408. tab.getElementsByTagName('a')[0].textContent = option.text;
  409. } else {
  410. // 公開マイリスト等
  411. tab.id = 'target_' + option.type[0];
  412. tab.title = option.title;
  413. tab.setAttribute('onclick', tab.getAttribute('onclick').replace(/'.+?'/, '\'' + option.url + '\''));
  414. tab.textContent = option.text;
  415. }
  416. return tab;
  417. }));
  418.  
  419. GreasemonkeyUtils.executeOnUnsafeContext(/* global Nico */ function () {
  420. eval('Nico.Navigation.HeaderSearch.Controller.search = '
  421. + Nico.Navigation.HeaderSearch.Controller.search.toString().replace(/(switch.+?{[^}]+)/, `$1;
  422. break;
  423. case "image":
  424. d = "https://seiga.nicovideo.jp/search/" + e;
  425. break;
  426. case "live":
  427. d = "https://live.nicovideo.jp/search/" + e;
  428. break;
  429. `));
  430. });
  431. }
  432.  
  433.  
  434.  
  435. /**
  436. * 静画・生放送の上部に表示されている検索窓に、「動画キーワード」「動画タグ」「マイリスト」「静画」「生放送」を検索するタブバーを設置する。
  437. */
  438. function careteTabsBarToSearchBox()
  439. {
  440. // スタイルの設定
  441. document.head.insertAdjacentHTML('beforeend', `<style>
  442. #sg_search_box {
  443. /* 静画 */
  444. margin-top: 0.2em;
  445. }
  446. #live_header div.score_search { /* 生放送マイページ向けに詳細度を大きくしている */
  447. /* 生放送 */
  448. top: initial;
  449. }
  450. /*------------------------------------
  451. タブバー
  452. */
  453. [action$="search"] > ul {
  454. display: flex;
  455. /* 生放送 */
  456. font-size: 12px;
  457. }
  458. /* 静画 */
  459. #head_search_form > ul {
  460. margin-left: 1.3em;
  461. /* マンガ・電子書籍 */
  462. line-height: 1.4em;
  463. }
  464. #head_search_form > ul:hover ~ .search_form_text {
  465. border-color: #999;
  466. }
  467. /*------------------------------------
  468. タブ
  469. */
  470. [action$="search"] > ul > li {
  471. margin-left: 0.2em;
  472. white-space: nowrap;
  473. }
  474. [action$="search"] > ul > li > a {
  475. background: lightgrey;
  476. padding: 0.2em 0.3em 0.1em;
  477. color: inherit;
  478. /* 生放送 */
  479. text-decoration: none;
  480. }
  481. #head_search_form > ul > li > a:hover {
  482. /* 静画 */
  483. text-decoration: none;
  484. }
  485. /*------------------------------------
  486. 選択中のタブ
  487. */
  488. [action$="search"] > ul > li.current > a {
  489. color: white;
  490. background: dimgray;
  491. }
  492. </style>`);
  493.  
  494. /**
  495. * 静画検索のtargetパラメータの値。
  496. * @type {string}
  497. */
  498. let imageSearchParamValue = 'illust';
  499.  
  500. const form = document.querySelector('[action$="search"]');
  501. const textField = form[pageType === 'image' ? 'q' : 'keyword'];
  502.  
  503. if (pageType === 'image') {
  504. // 静画の場合
  505. const pathnameParts = document.querySelector('#logo > h1 > a').pathname.split('/');
  506. switch (pathnameParts[1]) {
  507. case 'manga':
  508. imageSearchParamValue = 'manga';
  509. break;
  510. case 'book':
  511. imageSearchParamValue = pathnameParts[2] === 'r18' ? 'book_r18' : 'book';
  512. break;
  513. }
  514. }
  515.  
  516. form.insertAdjacentHTML('afterbegin', `<ul>
  517. <li>
  518. <a href="https://www.nicovideo.jp/search/" title="${h(_('動画をキーワードで検索'))}">${h(_('キーワード'))}</a>
  519. </li>
  520. <li>
  521. <a href="https://www.nicovideo.jp/tag/" title="${h(_('動画をタグで検索'))}">${h(_('タグ'))}</a>
  522. </li>
  523. <li>
  524. <a href="https://www.nicovideo.jp/mylist_search/" title="${h(_('マイリストを検索'))}">${h(_('マイリスト'))}</a>
  525. </li>
  526. <li${pageType === 'image' ? ' class="current"' : ''}>
  527. <a href="https://seiga.nicovideo.jp/search/?target=${imageSearchParamValue}"
  528. title="${h(textField.defaultValue)}">${h(_('静画'))}</a>
  529. </li>
  530. <li${pageType === 'live' ? ' class="current"' : ''}>
  531. <a href="https://live.nicovideo.jp/search/" title="' + h(_('番組を探す')) + '">${h(_('生放送'))}</a>
  532. </li>
  533. </ul>`);
  534.  
  535. const defaultCurrentTabAnchor = form.querySelector('.current a');
  536.  
  537. document.addEventListener('click', function (event) {
  538. if (event.button !== 2 && event.target.matches('[action$="search"] > ul > li > a')) {
  539. // タブが副ボタン以外でクリックされたとき
  540. let searchWord = textField.value.trim();
  541. if (pageType === 'image' && textField.value === textField.defaultValue) {
  542. // 静画の場合、検索窓の値が既定値と一致していれば空欄とみなす
  543. searchWord = '';
  544. }
  545. if (searchWord) {
  546. // 検索語句が入力されていれば
  547. switchTab(event.target);
  548. event.target.pathname = event.target.pathname.replace(/[^/]*$/, encodeURIComponent(searchWord));
  549. setTimeout(function () {
  550. // リンク先を新しいタブで開いたとき
  551. switchTab(defaultCurrentTabAnchor);
  552. }, CURRENT_TAB_RESTORATION_DELAY);
  553. } else {
  554. // 検索語句が未入力なら
  555. event.preventDefault();
  556. if (event.button === 0) {
  557. // 主ボタンでクリックされていれば
  558. switchTab(event.target);
  559. }
  560. }
  561. }
  562. });
  563.  
  564. // TabSubmitをインストールしているとマウスボタンを取得できず、中クリック時にも同じタブで検索してしまうため分割
  565. form.addEventListener('click', function (event) {
  566. if (event.target.type === (pageType === 'image' ? 'image' : 'submit')) {
  567. // 送信ボタンをクリックしたとき
  568. const searchWord = textField.value !== textField.defaultValue && textField.value.trim();
  569. if (searchWord) {
  570. event.stopPropagation();
  571. event.preventDefault();
  572. const anchor = form.querySelector('.current a');
  573. anchor.pathname = anchor.pathname.replace(/[^/]*$/, encodeURIComponent(searchWord));
  574. location.assign(anchor.href);
  575. }
  576. }
  577. }, true);
  578.  
  579. addEventListener('pageshow', function (event) {
  580. if (event.persisted) {
  581. // 履歴にキャッシュされたページを再表示したとき
  582. switchTab(defaultCurrentTabAnchor);
  583. }
  584. });
  585.  
  586. /**
  587. * 選択しているタブを切り替える。
  588. * @param {HTMLAnchorElement} target - 切り替え先のタブのリンク。
  589. */
  590. function switchTab(target) {
  591. form.getElementsByClassName('current')[0].classList.remove('current');
  592. target.parentElement.classList.add('current');
  593. if (pageType === 'image') {
  594. // 静画
  595. if (textField.defaultValue === textField.value) {
  596. // 検索語句が未入力なら
  597. textField.defaultValue = textField.value = target.title;
  598. } else {
  599. // 検索語句が入力されていれば
  600. textField.defaultValue = target.title;
  601. }
  602. } else {
  603. // 生放送
  604. textField.placeholder = target.title;
  605. }
  606. }
  607. }
  608.  
  609.  
  610.  
  611. /**
  612. * 総合トップページの検索窓に、動画「タブ」「マイリスト」検索ボタンを追加する。
  613. */
  614. function addTagSearchButtonToTopPage()
  615. {
  616. // スタイルの設定
  617. document.head.insertAdjacentHTML('beforeend', `<style>
  618. .CrossSearch {
  619. display: flex;
  620. margin-right: 1em;
  621. }
  622. .CrossSearch-services {
  623. display: flex;
  624. }
  625. .CrossSearch-service {
  626. width: unset;
  627. padding: 0 0.5em;
  628. white-space: nowrap;
  629. }
  630. .CrossSearch-form {
  631. width: unset;
  632. }
  633. </style>`);
  634.  
  635. // 静画検索ボタンの取得
  636. const refItem = document.querySelector('.CrossSearch-service[data-service="seiga"]');
  637.  
  638. const tagItem = refItem.cloneNode(true);
  639. tagItem.textContent = _('タグ');
  640. tagItem.dataset.service = 'tag';
  641. tagItem.dataset.baseUrl = 'https://www.nicovideo.jp/tag/';
  642. refItem.before(tagItem);
  643.  
  644. const mylist = refItem.cloneNode(true);
  645. mylist.textContent = _('マイリスト');
  646. mylist.dataset.service = 'mylist';
  647. mylist.dataset.baseUrl = 'https://www.nicovideo.jp/mylist_search/';
  648. refItem.before(mylist);
  649. }
  650.  
  651.  
  652.  
  653. /**
  654. * ユーザーページ左側のメニューに、静画へのリンクを追加する。
  655. */
  656. function addImageLinkToUserPageMenu()
  657. {
  658. // スタイルの設定
  659. document.head.insertAdjacentHTML('beforeend', `<style>
  660. .sidebar ul li.imageTab a span {
  661. width: 22px;
  662. height: 20px;
  663. background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAUCAQAAAAjdQW7AAAA/ElEQVQoz9WRzU3EMBCFv3EibRN2CyDtLiJJIRSSQlIIhSQgscDWYDeBhGc4ZJf87YErzwdr5M9vnmbEmCvz2FudMQSG12a3eBWbgcdOKgt4wwBJxM/mJnzorNKAX3Y6y7wqL+iz1uZ1ATocS5UjmmtbeQqSXGT1nX2Xa/WKzQ5IcsNbs3LOWDW5yu/t4umJdYxjr0EnJElUNIjXxEal1mPbMVPBqYG7aOC/2K1gl9FZUuXQ3/dKRsNDt3G2xf7M6zW/F8+t0V1l5KlIbFKXJRldZ0OSG97bDVwMBPF5WgWSJPJybrfTEGPffft8mYhQQPpoC25JjL/L8f/gH1eMbYCeUydgAAAAAElFTkSuQmCC");
  664. }
  665. </style>`);
  666.  
  667. const nextItem = document.getElementsByClassName('stampTab')[0];
  668.  
  669. const item = nextItem.cloneNode(true);
  670. const classList = item.classList;
  671. classList.remove('stampTab', 'active');
  672. classList.add('imageTab');
  673. const anchor = item.getElementsByTagName('a')[0];
  674. anchor.href = 'https://seiga.nicovideo.jp/user/illust/' + /[0-9]+/.exec(anchor.pathname)[0];
  675. anchor.lastChild.data = _('静画');
  676.  
  677. nextItem.prepend(item);
  678. }