/
favorite-tags.user.js
115 lines (104 loc) · 4.01 KB
/
favorite-tags.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// ==UserScript==
// @name Tissue | Favorite Tags
// @author Eai <eai@mizle.net>
// @license MIT
// @version 1.1.1
// @match https://shikorism.net/checkin
// @match https://shikorism.net/checkin/*/edit
// @icon https://shikorism.net/favicon.ico
// ==/UserScript==
/**
* 渡されたテキストをタグ欄に追加する
*/
const dispatchTag = (tag) => {
const input = document.querySelector("#tagInput");
// https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js#46012210
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value",
).set;
nativeInputValueSetter.call(input, tag);
const inputEvent = new Event("input", { bubbles: true });
const submitEvent = new KeyboardEvent("keydown", {
bubbles: true,
cancelable: true,
keyCode: 13,
});
input.dispatchEvent(inputEvent);
input.dispatchEvent(submitEvent);
};
const createTagHtml = (tagText) => {
const username = document.querySelector(`.dropdown-item`).href.match(/user\/(.+)/)[1];
return `<a
class="list-inline-item badge badge-primary tis-tag-input-item favorite-tags"
href="https://shikorism.net/search/checkin?q=user:${username}+tag:${tagText}"
>
${tagText}
</a>`;
};
const addTagEvent = (tagElement) => {
tagElement.addEventListener("click", (event) => {
event.preventDefault();
dispatchTag(event.target.textContent);
});
};
(async () => {
const favoriteTagsSet = await fetch(
`${document.querySelector(".dropdown-item[href$='/stats']").href}`,
)
.then((res) => res.text())
.then((html) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const tags = [
...doc.querySelectorAll(".tis-stat-table a.text-reset:not(.text-decoration-none)"),
].reduce((set, element) => set.add(element.innerText.trim()), new Set());
return tags;
});
const recentTagsSet = await fetch(
`${document.querySelector(".dropdown-item[href$='/okazu']").href}`,
)
.then((res) => res.text())
.then((html) => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
const tags = [...doc.querySelectorAll(`.mb-2 .badge-secondary`)]
// 五十音順でソート
.sort((a, b) => a.innerText.localeCompare(b.innerText), "ja")
.reduce((set, element) => set.add(element.innerText.trim()), new Set());
return tags;
});
const fragment = document.createDocumentFragment();
// TODO: refactor
const favoriteTags = document.createElement("div");
favoriteTags.insertAdjacentHTML(
"afterbegin",
`<small class="form-text text-muted">よく使うタグ: </small>`,
);
favoriteTagsSet.forEach((tagText) => {
const template = document.createElement("template");
template.innerHTML = createTagHtml(tagText);
const element = template.content.firstChild;
addTagEvent(element);
favoriteTags.appendChild(element);
});
fragment.appendChild(favoriteTags);
// TODO: refactor 2
const recentTags = document.createElement("div");
recentTags.insertAdjacentHTML(
"afterbegin",
`<small class="form-text text-muted">最近使ったタグ: </small>`,
);
recentTagsSet.forEach((tagText) => {
const template = document.createElement("template");
template.innerHTML = createTagHtml(tagText);
const element = template.content.firstChild;
addTagEvent(element);
recentTags.appendChild(element);
});
fragment.appendChild(recentTags);
// Fragmentを挿入
document
.querySelector(`label[for='tagInput']`)
.parentElement.insertBefore(fragment, document.querySelector(`.form-group .form-text`));
})();