Skip to content

Instantly share code, notes, and snippets.

@whiteball
Last active December 17, 2023 03:24
Show Gist options
  • Save whiteball/5a936e89a40b07b625be27e534b8874c to your computer and use it in GitHub Desktop.
Save whiteball/5a936e89a40b07b625be27e534b8874c to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name AIのべりすと 作品リストに絞り込みのためのタグリストを追加
// @namespace https://ai-novelist-share.geo.jp/
// @version 0.1.2
// @description 作品タイトルに含まれる"[...]"の部分をタグと見なして、作品リストページにタグ一覧を追加します。タグ名をクリックすると、そのタグを検索欄にセットして作品を絞り込み表示します。
// @author しらたま
// @match https://ai-novel.com/works.php
// @icon https://www.google.com/s2/favicons?sz=64&domain=ai-novel.com
// @updateURL https://gist.github.com/whiteball/5a936e89a40b07b625be27e534b8874c/raw/ai_novelist_tag_list_for_works.user.js
// @downloadURL https://gist.github.com/whiteball/5a936e89a40b07b625be27e534b8874c/raw/ai_novelist_tag_list_for_works.user.js
// @supportURL https://gist.github.com/whiteball/5a936e89a40b07b625be27e534b8874c
// @grant none
// ==/UserScript==
/*
更新履歴
2023/12/17 0.1.2
リモート表示の時にグリッドリスト以外だとタグ一覧が表示されない不具合を修正。
表示を切り替えたときにソート順が逆転する不具合を修正。
表示を切り替えてもソート順が辞書昇順に戻らないように変更。
2023/12/01 0.1.1
タグメイトタグ名の間をクリックした時にnullと表示されるのを修正。
検索クリアボタンを追加。
*/
(function() {
'use strict';
const works_find = document.getElementById('works_find')
if (!works_find) {
console.log('a')
return
}
document.head.insertAdjacentHTML('beforeend',`<style>
#mod-title-tag-list {
margin-top: 10px;
margin-left: auto;
margin-right: auto;
width: 80vw;
font-size: 80%;
}
#mod-title-tag-list p {
font-size: 95%;
padding: 0 10px;
margin-top: 0.25rem;
margin-bottom: 0;
}
#mod-title-tag-list p.reset {
font-size: 95%;
padding: 0 20px;
margin-top: 0.25rem;
margin-bottom: 0.5rem;
}
#mod-title-tag-list ul {
list-style: none;
padding: 0 20px;
margin-top: 0.25rem;
margin-bottom: 0;
}
#mod-title-tag-list li {
display: inline;
padding-right: 10px;
}
</style>`)
works_find.insertAdjacentHTML('beforebegin', `<div id="mod-title-tag-list">
<details><summary>タグリスト開閉</summary>
<p><a href="javascript:;" id="mod-tag-list-sort-dic">辞書順</a> / <a href="javascript:;" id="mod-tag-list-sort-count">件数順</a></p>
<ul id="mod-title-tag-list-inner"></ul>
<p class="reset"><a href="javascript:;" id="mod-tag-list-search-reset">検索クリア</a></p>
</details>
</div>`)
const maxId = Number(localStorage.max_works_id)
const reg = new RegExp(/\[([^\]]+)\]/g)
const tags = new Map()
// 一覧の作成
const buildList = function (type) {
tags.clear()
// tags.set('タグ無し', 0)
if (type === 'remote') {
let titleList = [...document.querySelectorAll('#works_remote .works_title')].map(e => e.innerText)
if (titleList.length === 0) {
titleList = [...document.querySelectorAll('#works_remote input.btn-square')].map(e => e.value)
}
for (const title of titleList) {
if (title) {
const matches = [...title.matchAll(reg)]
if (matches.length !== 0) {
for (const tag of matches) {
if (tags.has(tag[1])) {
tags.set(tag[1], tags.get(tag[1]) + 1)
} else {
tags.set(tag[1], 1)
}
}
} else {
// tags.set('タグ無し', tags.get('タグ無し') + 1)
}
}
}
} else {
for (let id = 0; id <= maxId; id++) {
const key = `works${id}`
const item = localStorage.getItem(key)
if (item !== null) {
const itemList = item.split('<|endofsection|>')
const title = itemList[6]
const matches = [...title.matchAll(reg)]
if (matches.length !== 0) {
for (const tag of matches) {
if (tags.has(tag[1])) {
tags.set(tag[1], tags.get(tag[1]) + 1)
} else {
tags.set(tag[1], 1)
}
}
} else {
// tags.set('タグ無し', tags.get('タグ無し') + 1)
}
}
}
}
let buffer = ''
for (const item of tags) {
buffer += `<li data-key="${item[0]}" data-count="${item[1]}"><a href="javascript:;">${item[0]}(${item[1]})</a></li>`
}
const ul = document.getElementById('mod-title-tag-list-inner')
if (ul) {
ul.innerHTML = buffer
for (const element of document.querySelectorAll('#mod-title-tag-list ul li a')) {
element.addEventListener('click', function (event) {
const works_find_text = document.getElementById('works_find_text')
if (works_find_text) {
works_find_text.value = '[' + event.target.parentElement.getAttribute('data-key') + ']'
works_find_text.dispatchEvent(new Event('input'))
}
})
}
}
}
// ソート処理
const sortEvent = function (key, asNum = false, reverse = false) {
const list = [...document.querySelectorAll('#mod-title-tag-list ul li')]
if (list.length === 0) {
return
}
const ul = list[0].parentElement
if (key === '') {
key = ul.getAttribute('data-sort')
if (!key) {
key = 'data-key'
}
const order = ul.getAttribute('data-order')
if (order === 'desc') {
reverse = true
} else {
reverse = false
}
} else if (ul.getAttribute('data-sort') && ul.getAttribute('data-order') === (reverse ? 'desc' : 'asc')) {
reverse = ! reverse
}
const mapped = list.map((v, i) => {
return { i, value: asNum ? Number(v.getAttribute(key)) : v.getAttribute(key) }
})
mapped.sort((a, b) => {
if (a.value > b.value) {
return reverse ? -1 : 1;
}
if (a.value < b.value) {
return reverse ? 1 : -1;
}
return 0;
});
const result = mapped.map((v) => list[v.i]);
for (const e of result) {
ul.append(e)
}
ul.setAttribute('data-sort', key)
ul.setAttribute('data-order', reverse ? 'desc' : 'asc')
}
const linkDic = document.getElementById('mod-tag-list-sort-dic')
if (linkDic) {
linkDic.addEventListener('click', function () {
sortEvent('data-key')
})
}
const linkCount = document.getElementById('mod-tag-list-sort-count')
if (linkCount) {
linkCount.addEventListener('click', function () {
sortEvent('data-count', true, true)
})
}
const linkReset = document.getElementById('mod-tag-list-search-reset')
if (linkReset) {
linkReset.addEventListener('click', function () {
const works_find_text = document.getElementById('works_find_text')
if (works_find_text) {
works_find_text.value = ''
works_find_text.dispatchEvent(new Event('input'))
}
})
}
// ローカルリモート切り替えにフック
const refreshlist_remote_orig = window.refreshlist_remote
window.refreshlist_remote = function (iterpos = 0, force_refresh = false) {
refreshlist_remote_orig(iterpos, force_refresh)
buildList('remote')
sortEvent('')
}
const refreshlist_orig = window.refreshlist
window.refreshlist = function () {
refreshlist_orig()
buildList('local')
sortEvent('')
}
// 初期表示
buildList('local')
sortEvent('data-key')
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment