// ==UserScript==
// @name Qiita Sushi
// @namespace https://greasyfork.org/users/432749
// @description QiitaのLGTMアイコンをSushiアイコンに変更する
// @author fukuchan
// @match https://qiita.com/*
// @version 0.0.1.20200325234213
// @downloadURL none
// ==/UserScript==
/*
* OpenSushi
* (c) remin
*/
const viewBox = '0 0 64 64';
const innerHTML = '
Sushi';
// SVG要素の中身をSushiに変更するメソッド
const replaceSvg = svg => {
svg.setAttribute('viewBox', viewBox);
svg.innerHTML = innerHTML;
};
// SVG要素やテキストのLGTMを発見し、全てSushiに変更するメソッド
const sushinize = node => {
// LGTMのSVG要素をそれぞれSushiに変更し、要素の変更を監視する
const images = node.querySelectorAll('*[class*="like"] svg:not([data-sushinized]), .ItemLink__status svg:not([data-sushinized]), .ms-ItemList_counts svg:not([data-sushinized]), svg[class*="Lgtm"]:not([data-sushinized])');
images.forEach(image => {
// Sushiに変更する
replaceSvg(image);
// 属性の変更を監視
new MutationObserver((records, observer) => {
// 属性変更時に発動するメソッドの中で属性を変更し無限再帰に陥るので一旦observeを停止する
observer.disconnect();
// Sushiに再変更する・Sushiを1回転させる
replaceSvg(records[0].target);
records[0].target.classList.add("sushi-go-round");
// observeを再開
observer.observe(records[0].target, {attributes: true});
}).observe(image, {attributes: true});
// 何重にも監視するのを防止するため、data-sushinized属性を追加しておく
image.dataset.sushinized = "1";
});
// 文字列のLGTMをSushiに置換する
const texts = node.querySelectorAll('.userPopularItems_likeUnit, .notification_actionWrapper span.bold:last-of-type, li[role="presentation"] a[href*="like"], .ms-ItemHeader_likedCount, .op-CounterItem_name, *[class*="Label"], .msg-Item_body, *[class*="UserAnalyzeResult__TagStatsTitle"], a.st-Dropdown_item[href*="lgtm"], a.st-Dropdown_item[href*="like"]');
texts.forEach(text => {
text.childNodes.forEach(child => {
if (child.nodeType === Node.TEXT_NODE) {
child.textContent = child.textContent.replace("LGTM", "Sushi");
}
});
});
};
// 現在読み込まれているdocument中のLGTMを全てSushiに置換
sushinize(document);
// コメントなど遅延読み込みされる動的な要素中のLGTMを読み込まれると同時に全てSushiに置換
const dynamicNodes = document.querySelectorAll('#comments, *[class*="List_view"], div[data-hyperapp-app="Milestones"], div[id*="Snackbar"], div[class*="UserMain__Content"], div[class*="UserAnalyzeResult"]');
dynamicNodes.forEach(node => {
new MutationObserver((records, observer) => {
observer.disconnect();
sushinize(node);
observer.observe(node, {childList: true});
}).observe(node, {childList: true});
});
// 回転SushiのCSSアニメーションを定義
const style = document.createElement('style');
style.innerText = '.liked svg.sushi-go-round, svg.sushi-go-round[color="#fff"] { animation: sushi-go-round 1s ease; } @keyframes sushi-go-round { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }';
document.head.append(style);