リンクにつながっているページを芋づる式に探して出力するbookmarklet

browserが応答なしになるので非推奨です
計算がめちゃくちゃ重い
2 hopまでなら大した計算量ではないんですけどね……
3 hopはやりすぎでした……
大量のリンクがページに書き込まれる
用途
一気にリンクを書き出して、その中から必要なものを順次取り出す
全部を手動で貼り付けようとすると時間かかるし面倒
2020-10-22 07:24:59 実装を変える
search/titles
を用いる
ページ数が爆発的に増えても問題ない
仕様
発動すると、ページに含まれているリンクにつながっているページをすべて引き出して新しいページに書き出す
逆リンク・順リンクは問わず、つながっているページををすべて列挙する
深さを指定可能
既定では3 hop先まで取得するようにした
実装
bookmarklet.jsjavascript:(()=>{let s=document.createElement("script");s.src='https://scrapbox.io/api/code/takker/リンクにつながっているページを芋づる式に探して出力するbookmarklet/bookmark.js';document.body.appendChild(s);})()
bookmark.jsjavascript:(async() => {
if(document.domain !== 'scrapbox.io') return;
// 最初に全ページのリンクデータを取得する
const getAllLinkData = async () => {
const pages = [];
let followingId = null;
do {
const param = followingId === null ? '' : `?followingId=${followingId}`;
const res = await fetch(
`https://scrapbox.io/api/pages/${scrapbox.Project.name}/search/titles${param}`
);
followingId = res.headers.get('X-Following-Id');
pages.push(...(await res.json()));
} while (followingId);
return pages;
};
// 指定したページデータとページタイトル、リンク先ページと被リンク先ページの両方を集めたページタイトルのリストを返す
const get1hopLinks = (pages, title) => {
const links = pages.find(page => page.title === title)?.links ?? [];
const linkeds = pages.filter(page => page.links.some(link => link === title))
.map(page => page.title);
return [...new Set([...links, ...linkeds])];
}
console.log('loading page data...');
const linkData = await getAllLinkData();
console.log('Got all page data.');
let targetLink = [scrapbox.Page.title];
let foundLinks = [];
// リンクを再帰的に検索する
for (const i of [1,2,3]) {
const links = [...new Set(targetLink.flatMap(title => get1hopLinks(linkData,title)))];
console.log(`Got ${links.length} ${i}-hop links.`);
foundLinks = new Set([...links,...foundLinks]);
targetLink = links;
}
console.log(`Found ${foundLinks.size} links.`);
// 空リンクを除外する
console.log('Removing empty links...');
const existedTitles = new Set(scrapbox.Project.pages
.filter(page => page.exists && !page.title.includes('🆖PIRACY'))
.map(page => page.title));
const results = [...foundLinks]
.filter((title, i) => {
if (existedTitles.has(title)) {
console.log(`[${i}]Found '${title}'`);
return true;
}
return false;
});
console.log(`Output ${results.length} links`);
const lines = results.map(link => `[${link}]`);
const e = a => encodeURIComponent(a);
window.open(`https://scrapbox.io/${scrapbox.Project.name}/temporary_page_${new Date()}?body=${e(lines.join('\n'))}`);
})();