はてなブックマークメタブ展開
by
Lhankor_Mhy
03/28 [2024/03/28 12:21:28]
コメントメタブと2階ブクマを展開します。説明→https://realtor-readyabooks.hatenablog.com/entry/2022/12/28/153626
@@ -2,7 +2,7 @@
// @name はてなブックマークメタブ展開
// @title はてなブックマークメタブ展開
// @namespace https://let.hatelabo.jp/Lhankor_Mhy/let/jtaB0fa4gYAA
-// @version 0.1
+// @version 0.11
// @description コメントメタブと2階ブクマを展開します。説明→https://realtor-readyabooks.hatenablog.com/entry/2022/12/28/153626
// @author Lhankor_Mhy
// @match https://b.hatena.ne.jp/entry/*
@@ -54,7 +54,7 @@
bookmarks.forEach(bookmark => {
const targetUserBookmark = targetElements.find(element => element.dataset?.userName === bookmark.user)
if (!targetUserBookmark) return undefined
- const targetElement = [targetUserBookmark, ...targetUserBookmark.querySelectorAll(`[data-user-name="${bookmark.user}"]`)].pop()
+ const targetElement = [targetUserBookmark, ...targetUserBookmark.querySelectorAll(`.entry-comment-contents[data-user-name="${bookmark.user}"]`)].pop()
addBookmark(targetElement, bookmark, eid)
})
})
// ==UserScript==
// @name はてなブックマークメタブ展開
// @title はてなブックマークメタブ展開
// @namespace https://let.hatelabo.jp/Lhankor_Mhy/let/jtaB0fa4gYAA
// @version 0.11
// @description コメントメタブと2階ブクマを展開します。説明→https://realtor-readyabooks.hatenablog.com/entry/2022/12/28/153626
// @author Lhankor_Mhy
// @match https://b.hatena.ne.jp/entry/*
// @icon https://b.hatena.ne.jp/favicon.ico
// @license CC0
// @noframes
// @grant none
// @javascript_url
// ==/UserScript==
(async () => {
const APIURL = 'https://b.hatena.ne.jp/entry/jsonlite/?url='
// ブクマ展開(x-template再利用)
const addBookmark = function (targetElement, bookmark, eid) {
targetElement.insertAdjacentHTML(
'beforeend',
document.getElementById('autoloader-bookmark-item').textContent
.replaceAll('{{user_name}}', bookmark.user)
.replaceAll('{{bookmarked_url}}', bookmark.url)
.replaceAll('{{user_page_path}}', `/${bookmark.user}/`)
.replaceAll('{{ sort }}', `recent`)
.replaceAll('{{profile_image_url}}', `https://cdn.profile-image.st-hatena.com/users/${bookmark.user}/profile.png`)
.replaceAll('{{ #is_public }}is-hidden{{ /is_public }}', `is-hidden`)
.replaceAll('{{{comment_expanded}}}', bookmark.comment)
.replaceAll('{{{tags}}}', `<li>${bookmark.tags.join('<li>')}`)
.replaceAll('{{created}}', bookmark.timestamp)
.replaceAll('{{comment_page_path}}', `/entry/${eid}/comment/${bookmark.user}`)
.replaceAll('{{#should_nofollow}}nofollow{{/should_nofollow}}', 'nofollow')
.replaceAll('{{#enable_button}} is-enabled{{/enable_button}}', 'is-enabled')
)
}
// 2階ブクマAPIコール
const targetURLs = [location.href], upstairsBookmarksList = []
for (const targetURL of targetURLs) {
const { bookmarks, entry_url, eid } = (await (await fetch(`${APIURL}${encodeURIComponent(targetURL)}`)).json()) ?? { entry_url: null }
if (!entry_url) continue
targetURLs.push(entry_url)
upstairsBookmarksList.push([bookmarks, eid])
}
// 2階ブクマをHTMLに展開
function addUpstairsBookmark(targetElements) {
upstairsBookmarksList.forEach(([bookmarks, eid]) => {
bookmarks.forEach(bookmark => {
const targetUserBookmark = targetElements.find(element => element.dataset?.userName === bookmark.user)
if (!targetUserBookmark) return undefined
const targetElement = [targetUserBookmark, ...targetUserBookmark.querySelectorAll(`.entry-comment-contents[data-user-name="${bookmark.user}"]`)].pop()
addBookmark(targetElement, bookmark, eid)
})
})
}
// メタブをHTMLに展開
function addMetaBookmark(targetElements) {
targetElements.forEach(async element => {
const targetURL = element.querySelector('[data-gtm-label="entry-recent-permalink"]').href
const { bookmarks, entry_url, eid } = (await (await fetch(`${APIURL}${encodeURIComponent(targetURL)}`)).json()) ?? { entry_url: null }
if (!entry_url) return undefined
bookmarks.forEach(
bookmark => {
addBookmark(element, bookmark, eid)
}
)
})
}
// 初回呼び出し
addUpstairsBookmark(Array.from(document.querySelectorAll(`.js-bookmarks-recent [data-user-name]`)))
addMetaBookmark(Array.from(document.querySelectorAll(`.js-bookmarks-recent [data-user-name]`)))
// ブクマ遅延読み込み監視
const targetNode = document.querySelector('.js-bookmarks-recent');
const upstairsBookmarkMutationConfig = { childList: true };
const upstairsBookmarkMutationCallback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
addUpstairsBookmark(Array.from(mutation.addedNodes).filter(node => node.dataset?.userName))
}
}
};
const upstairsBookmarkMutationObserver = new MutationObserver(upstairsBookmarkMutationCallback);
upstairsBookmarkMutationObserver.observe(targetNode, upstairsBookmarkMutationConfig);
const metaBookmarkMutationConfig = { childList: true, subtree: true };
const metaBookmarkMutationCallback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
addMetaBookmark(Array.from(mutation.addedNodes).filter(node => node.dataset?.userName))
}
}
};
const metaBookmarkMutationObserver = new MutationObserver(metaBookmarkMutationCallback);
metaBookmarkMutationObserver.observe(targetNode, metaBookmarkMutationConfig);
})()
- Permalink
- このページへの個別リンクです。
- RAW
- 書かれたコードへの直接のリンクです。
- Packed
- 文字列が圧縮された書かれたコードへのリンクです。
- Userscript
- Greasemonkey 等で利用する場合の .user.js へのリンクです。
- Loader
- @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
- Metadata
- コード中にコメントで @xxx と書かれたメタデータの JSON です。