generated at
GTDを噛み砕くをScrapbox書籍に変換する
GTDを噛み砕くScrapbox書籍にして読みたくなってきた
2022-02-12 20:40:48 作った

著作権
無断転載禁止となっている
まあこの辺りはstaさんに聞けばいいだろう

既存の考察
リンクを文字置換でつけるのもありか/sta/GTDを噛み砕くのScrapbook#6006345a79d3a90000f74f4e

DOMのparse
本文は全て div.uk-width-medium-3-4 に入っている
見出し
h1 から h4 まである
それぞれIDが振られている
振られていないものはない
#脚注 は無視する
階層構造になっていない点に注意
各節のformat
文章の塊ごとに p で区切られている
この塊を一つの段落としよう
ul li
箇条書きにする
ネストもある
code
インラインコード記法にする
a
外部リンク記法にする
href と中身が同じなら、生のURLをそのまま入れる
strong
[* ] を使う
blockquote p
一行の引用にする
table
thead tbody がある
captionはない
table で統一しておくか
innerText.split("\n") したやつの行頭にindentを入れればいい感じになる
a[id]
脚注へのリンク
「脚注1」というページに書き出す
リンクは ([脚注1]) の形で挿入する
脚注の中身は li[id] で取得できる

適当にscriptを書いてみる
型チェックは面倒なのでしない
どうせ書き捨てコードだし
何回も使うわけでもない
2022-02-12 20:21:49 完成
階層構造型リンクしか貼っていない
キーワードを自動でリンクにしてみるか?
script.js
(() => { main(); function main() { if (!location.href.startsWith("https://stakiran.github.io/gtd_kamikudaku")) return; const body = document.querySelector("div.uk-width-medium-3-4"); const sections = makeHierarchy(body); const pages = []; for (const section of sections) { if (section[0].id === "脚注") continue; const pages_ = [...toPages(section)]; if (pages_.length === 0) continue; pages.push(pages_.pop()); pages.push(...pages_); } const footnotes = Array.from(body.querySelectorAll("li[id] p")) .map((p, i) => ({ title: `脚注${i + 1}`, lines: [`脚注${i + 1}`, p.firstChild.textContent.trim()], })); const index = { title: "目次", lines: [ "目次", ...pages.map(({ id, level }) => `${" ".repeat(level - 1)}[${id}]`), ], }; const json = { pages: [index, ...pages.map(({id,lines}) => ({ title: id, lines: [id, ...lines] })), ...footnotes], }; // download dataを作成 const blob = new Blob([JSON.stringify(json)], {type: 'application/json'}); // download linkを生成 const url = URL.createObjectURL(blob); window.open(url); } function* toPages(section) { const data = section[0] const lines = []; for (const node of section.slice(1)) { if (Array.isArray(node)) { const pages = [...toPages(node)]; if (pages.length === 0) continue; const link = pages[pages.length - 1].id; lines.push(`[${link}]`, ""); // 先に親ページを返す yield pages.pop(); for (const page of pages) { yield page; } continue; } switch (node.nodeName) { case "P": lines.push(...convertP(node).split("\n"), ""); break; case "TABLE": lines.push("table:table", ...node.innerText.split("\n").map((text) => ` ${text}`), ""); break; case "UL": lines.push(...convertUl(node), ""); break; case "PRE": lines.push("code:txt", ...node.innerText.split("\n").map((text) => ` ${text}`), ""); break; case "FIGURE": { const img = node.querySelector("img"); const src = img.src.trim(); const text = img.alt.trim(); lines.push(text, ` [${src}]`, ""); break; } default: lines.push(...node.innerText.split("\n"), ""); break; } } yield { ...data, lines }; } function convertP(p) { return Array.from(p.childNodes).map((node) => { switch (node.nodeName) { case "STRONG": return node.textContent.split("\n").map((text) => `[* ${text}]`).join("\n"); case "CODE": return node.textContent.split("\n").map((text) => `\`${text}\``).join("\n"); case "A": { if (node.id) { const n = node.textContent.trim(); return `([脚注${n}])`; } const url = node.href.trim(); const text = node.textContent.trim(); return url === text ? ` ${url} ` : `[${url} ${text}]`; } case "UL": return convertUl(node).join("\n"); default: return node.textContent; } }).join(""); } function convertUl(ul) { const list = Array.from(ul.children).filter((li) => li.tagName === "LI"); return list.flatMap( (li) => convertP(li).split("\n").map((text, i) => i === 0 ? ` ${text}` : ` ${text}`) ); } function makeHierarchy(body) { let node = body.querySelector("h1"); const sections = []; while (node) { const [section, next] = crawl(node); node = next; sections.push(section); } return sections; } function crawl(node, split = 1) { if (!node) return [[], undefined]; const section = [{id: node.id, text: node.textContent, level: split}]; let child = node.nextElementSibling; while (child && child.tagName !== `H${split}`) { if (parseInt(child.tagName.slice(1)) > split) { const result = crawl(child, split + 1); section.push(result[0]); child = result[1]; continue; } if (parseInt(child.tagName.slice(1)) <= split) break; section.push(child); child = child.nextElementSibling; } return [section, child]; } })();

#2022-02-12 18:37:25