generated at
関連ページリストにタグに応じて色やアイコンをつける
関連ページリストで、特定のタグやリンクがついているページにアイコンをつけるUserScript
scrapbox-syncで、タグによってページの公開状態を決めているが、関連ページの公開状態を調べるのにいちいち見て回る必要があり面倒だったので作ったyosider
アイコン版のほうが、該当するタグが複数種類ついている場合にすべて表示できるので便利
色版でも頑張ればできそうだが

面白そうtakker
例によってちょっと書き換えた
for文を減らした
デバッグしないで書き換えて放置というクソ迷惑なことしていますごめんなさい
ありがとうございますyosider
むしろそれを期待して置いてるところあるw

色版
script1.js
const tagColorPairs = { // for default-dark 'private': '#a95756', 'not-private': '#3c763d', } mark(); const observer = new MutationObserver(mark); //observer.observe(document.querySelector('div.related-page-list.clearfix ul.grid'), { childList: true }); addObserver(); function addObserver() { let elem = document.querySelector('div.related-page-list.clearfix ul.grid'); if (!elem) { window.setTimeout(addObserver, 500); return; } observer.observe(elem, { childList: true }); }
要素の読み込みがおわるまでMutationObserverのobserve開始を待つようにしたyosider

script1.js
const encode = s => s.replace(/[^A-Za-z0-9-.!~*();,:@&=+$]/g, match => { if (match===' ') return '_' else if (match==='"') return '\\"' // a[href='...']の中なので必要 else if (match==="'") return "\\'" else return encodeURIComponent(match); }); async function mark() { if (!scrapbox.Page.title) return; const {relatedPages: {links1hop}} = await (await fetch(`/api/pages/${scrapbox.Project.name}/${scrapbox.Page.title}`)).json(); links1hop.forEach(({linksLc, title}) => { const tag = Object.keys(tagColorPairs).find(tag => linksLc.includes(tag)); if (!tag) return; const header = document.querySelector(`.related-page-list .grid li.page-list-item a[href="/${scrapbox.Project.name}/${encode(title)}"] .header`);
title のエンコードを特殊な感じにしないといけないっぽい
fail encodeURIComponent(title)
: はそのまま
(半角スペース)は _ に置換
どう書けば良いのかわからない…誰か教えて下さいyosider
title.replace(/ /g, '_').replace(/\/g, encodeURIComponent('/')).... みたいに列挙するしかない?
title.toLowerCase().replace(/ /g, '_') でいけると思いますtakker
title titleLc に変換できます
ScrapBubble#608ceb131280f000005d3077で使っている方法です
js
const header = document.querySelector(`.related-page-list .grid li.page-list-item a[href="/${scrapbox.Project.name}/${title.toLowerCase().replace(/ /g, '_')}"] .header`);
LcはLowerCaseの略だったのかyosider
関連ページリストのカードを、hrefの値で取得しようとしているのですが、他にも / とか # とかencodeしないといけない文字があるようで、これだけではだめでしたyosider
とりあえず以下でいけた
js
function _encode(match, offset, string) { if (match===' ') return '_' else if (match==="'") return "\\'" // a[href='...']の中なので必要 else if (match==='"') return '\\"' // 一応 else return encodeURIComponent(match); } const encode = s => s.replace(/[^A-Za-z0-9-.!~*();,:@&=+$]/g, _encode);
script1.js
header.style.borderTop = `${tagColorPairs[tag]} solid 10px`; }); }

アイコン版
FontAwesomeアイコンを使う場合は、そのためのUserCSSも必要
style.css
.grid li { font-family: "Roboto", Helvetica, Arial, "Hiragino Sans", sans-serif, FontAwesome; } .grid i { font-style: normal; }
script2.js
const tagIconPairs = { // for default-dark 'private': `<i class="fa fa-lock" style="color:#cd5c5c; margin-right:0.3em;"></i>`, 'not-private': `<i class="fas fa-unlock" style="color:#6fac63; margin-right:0.2em;"></i>`, } mark(); const observer = new MutationObserver(mark); addObserver(); function addObserver() { let elem = document.querySelector('div.related-page-list.clearfix ul.grid'); if (!elem) { window.setTimeout(addObserver, 500); return; } observer.observe(elem, { childList: true }); } const encode = s => s.replace(/[^A-Za-z0-9-.!~*();,:@&=+$]/g, match => { if (match===' ') return '_' else if (match==='"') return '\\"' // a[href='...']の中なので必要 else if (match==="'") return "\\'" else return encodeURIComponent(match); }); async function mark() { if (!scrapbox.Page.title) return; const {relatedPages: {links1hop}} = await (await fetch(`/api/pages/${scrapbox.Project.name}/${scrapbox.Page.title}`)).json(); links1hop.forEach(({linksLc, title}) => { const html = Object.keys(tagIconPairs) .flatMap(tag => linksLc.includes(tag) ? [tagIconPairs[tag]] : []) .join(''); const titleElem = document.querySelector(`.related-page-list .grid li.page-list-item a[href="/${scrapbox.Project.name}/${encode(title)}"] .title`); //const temp = titleElem.textContent; //titleElem.textContent = ''; //titleElem.textContent = temp; titleElem.textContent = titleElem.textContent; titleElem.insertAdjacentHTML('afterbegin', html); }); }
Element.insertAdjacentHTML()だと要素が上書きされず増えていってしまう
observerが発火する度にアイコンがふえる!
知らんかったtakker
てことは一旦 textContent をまっさらにすればいいわけか (絶対Element.innerHTML使わないマン)
なるほど…?
なんか直感に反する動作だな…yosider
test という1hop linkに対して、 titleElem.textContent "test" という文字列だけだが、 titleElem.textContent = '' とするとアイコンと文字列両方が消える
titleElem.textContent = titleElem.textContent; だけでよさそう
なんだこの意味不明な行w
takker
違和感ありまくりだったのでいろいろ挟んでいたのですが、そんな事せずとも A=A で動くみたいですね
やっていることは titleElem.setTextContent(titleElem.getTextContent()) みたいな単なる関数呼び出しなので、まあ値が変化するのは当然といえば当然なのですが、いかんせん表記が A=A なので、違和感を拭えませんね……
何もわからない。。

MDN