はてなブックマークメタブ展開

  • // ==UserScript==
    // @name         はてなブックマークメタブ展開
    // @title        はてなブックマークメタブ展開
    // @namespace    https://let.hatelabo.jp/Lhankor_Mhy/let/jtaB0fa4gYAA
    // @version      0.1
    // @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(`[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 です。

History

  1. 2023/01/04 15:14:24 - 01/04
  2. 2022/12/28 15:21:54 - 2022-12-28
  3. 2022/12/28 14:33:58 - 2022-12-28
  4. 2022/12/28 14:33:40 - 2022-12-28
  5. 2022/12/27 18:35:10 - 2022-12-27
  6. 2022/12/27 18:28:52 - 2022-12-27
  7. 2022/12/27 18:26:23 - 2022-12-27
  8. 2022/12/27 18:23:32 - 2022-12-27