index.xhtml 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE html><html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>GenerateTOC</title>
  6. <meta name="author" content="SENOO, Ken" />
  7. <script>
  8. // <![CDATA[
  9. function GenerateTOC() {
  10. /**
  11. ## Policy
  12. 1. Get input HTML text
  13. 2. Parse HTML text
  14. * Get section>h1-h6
  15. * Insert `<nav><h?>Table of Contents</h?><ol><li></li></ol></nav>` before second section
  16. * Set value to YYYYMMDDThhmm in section>h1-h6 id attribute
  17. */
  18. // Get HTML document as DOM
  19. var value = document.getElementById("input").value;
  20. var view = document.getElementById("view");
  21. view.innerHTML = value;
  22. cleanup_element(view);
  23. // Generate TOC
  24. var root = document.getElementById("view");
  25. var first_heading = root.querySelectorAll("h1, h2, h3, h4, h5, h6")[1];
  26. first_heading = first_heading.tagName.toLowerCase();
  27. var toc = '<nav>\n';
  28. toc += "<" + first_heading + ">目次";
  29. toc += "</" + first_heading + ">";
  30. nav = create_toc(root.children);
  31. // Remove title headings
  32. nav = nav.replace(/^[\s\S]*?<ol>[\s\S]*?<ol>/, "<ol>");
  33. nav = nav.replace(/\n<\/li>$/, ""); // Delete trailing li
  34. nav = nav.replace(/<\/li>\n<\/ol>$/, "");
  35. toc += nav;
  36. toc += "\n</nav>\n";
  37. // Insert nav to source HTML
  38. var nav = document.createElement("nav");
  39. insert = root.querySelectorAll("section")[1];
  40. insert.insertAdjacentHTML("beforeBegin", toc);
  41. // Output
  42. document.getElementById("output").textContent = root.innerHTML;
  43. // Preview result
  44. document.getElementById("view").innerHTML = root.innerHTML;
  45. navigator.clipboard.writeText(root.innerHTML);
  46. }
  47. function create_toc(children){
  48. var toc = "";
  49. // Date prefix for YYYYMMDDThhmm format
  50. var date = new Date();
  51. var now = date.toISOString().replace(/-|T.*/g, "");
  52. now += "T" + ( "0"+date.getHours() ).slice(-2) + ( "0"+date.getMinutes() ).slice(-2);
  53. for (let node of children){
  54. tag_name = node.tagName.toLowerCase();
  55. if (tag_name.match(/h[1-6]/)){
  56. let id = now + "_" + node.textContent;
  57. id = id.replace(/\s/g, "-");
  58. // Remove for escape in id
  59. id = id.replace(/[~^`'"(){}\\|!&<>]/g, "");
  60. var nodeText = escape_html(node.textContent);
  61. toc += '\n<li><a href="#' + id + '">' + nodeText + '</a></li>';
  62. node.id = id;
  63. } else if (tag_name === "section"){
  64. toc += "\n<ol>";
  65. toc += create_toc(node.children);
  66. toc += "\n</ol>\n</li>";
  67. }
  68. }
  69. toc = toc.replace(/<\/ol>\n<\/li>\n<ol>\n/g, "");
  70. toc = toc.replace(/<\/li>\n<ol>/g, "\n<ol>");
  71. return toc;
  72. }
  73. // 改行除去処理でスキップする要素 (子孫含めスキップされる)
  74. const element_skip_list = {
  75. pre: true,
  76. };
  77. // 改行除去処理の対象要素 (子孫含め処理される)
  78. const element_target_flags = {
  79. a: true,
  80. em: true,
  81. strong: true,
  82. p: true,
  83. li: true,
  84. td: true,
  85. h1: true,
  86. h2: true,
  87. h3: true,
  88. h4: true,
  89. h5: true,
  90. h6: true,
  91. };
  92. // DOMツリーの各要素から不要な改行/空白を除去
  93. // in_target: 処理対象の要素に入るとtrueになる
  94. // queue: 要素を跨いだ処理の空白で使用する作業用配列
  95. function cleanup_element(element, in_target, queue) {
  96. in_target = in_target || false; // 未指定ならfalse
  97. queue = queue || []; // 未指定なら空配列
  98. // 処理対象要素から出た時にqueueを空にする
  99. if (!in_target) {
  100. queue.length = '';
  101. }
  102. // 子要素のリストを持たなければスキップ
  103. if (!element.childNodes) { return; }
  104. // 直下の子要素を処理
  105. let list = element.childNodes;
  106. for (let i = 0, length = list.length; i < length; i++) {
  107. let child = list[i];
  108. let nodename = child.nodeName.toLowerCase();
  109. // 特定の要素(主にpre)をスキップ
  110. if (element_skip_list[nodename] === true) { continue; }
  111. // 対象要素又はその子孫なら改行/空白を除去
  112. let next_in_target = (in_target || element_target_flags[nodename] === true);
  113. if (next_in_target) {
  114. cleanup_node(child, queue);
  115. }
  116. // 子孫を処理するため再帰呼び出し
  117. cleanup_element(child, next_in_target, queue);
  118. // 子孫要素の最後の「日本語文字」以降の改行/空白を除去
  119. if (!in_target && next_in_target) {
  120. cleanup_queue(queue);
  121. }
  122. }
  123. }
  124. // テキストノードから不要な改行/空白を除去
  125. function cleanup_node(node, queue) {
  126. // 正規表現のメモ:
  127. // (?:[^\s\d\w]| )←日本語 (記号含む)
  128. // [^\S ]←改行/空白 (\sから全角スペースを除いた物)
  129. // [^\S\n ]←空白 (\sから全角スペース/改行文字を除いた物)
  130. // [\[\]\(\)\{\}\<\>\|] ←括弧と「|」
  131. // テキストノード以外はスキップ
  132. if (node.nodeType !== document.TEXT_NODE) { return; }
  133. let text = node.data;
  134. // 前要素の末尾が日本語だった場合
  135. if (queue.length > 0) {
  136. // 改行/空白だけのテキストノードはqueueに追加 (次に日本語が出現したらまとめて除去)
  137. if (text === '' || /^[^\S ]*$/.test(text)) {
  138. queue.push(node);
  139. return;
  140. }
  141. // 日本語又は開き括弧以外の記号が出現したらtrue
  142. let jp_or_paren = /^[^\S ]*(?:[^\s\d\w\[\(\{\<\|]| )/.test(text);
  143. // 前の要素の末尾が閉じ括弧ならtrue
  144. let last_paren = (/[^\S ]*[\]\)\}\>\|][^\S ]*$/.test(queue[0].data));
  145. // 日本語又は開き括弧以外の記号が出現したらqueueを使用して最後の日本語より後の改行/空白をまとめて除外
  146. // 閉じ括弧の後に、。が来た場合も除外
  147. if (jp_or_paren && (!last_paren || /^[^\S ]*[、。]/.test(text))) {
  148. cleanup_queue(queue);
  149. text = text.replace(/^[^\S ]*/, "");
  150. } else {
  151. // 上記以外ならqueueを破棄
  152. queue.length = 0;
  153. }
  154. }
  155. // 日本語と閉じ括弧以外の記号+改行+後続の空白+開き括弧以外
  156. text = text.replace(/((?:[^\s\d\w\]\)\}\>\|]| ))\n[^\S ]*([^\[\(\{\<\|])/g, "$1$2");
  157. // 閉じ括弧以外+改行+後続の空白+日本語と開き括弧以外の記号
  158. text = text.replace(/([^\]\)\}\>\|])\n[^\S ]*((?:[^\s\d\w\[\(\{\<\|]| ))/g, "$1$2");
  159. // 括弧+改行+後続の空白+、。
  160. text = text.replace(/([\[\]\(\)\{\}\<\>\|])\n[^\S ]*([、。])/g, "$1$2");
  161. if (/(?:[^\s\d\w]| )[^\S ]*$/.test(text)) {
  162. // 末尾が日本語ならqueueに追加
  163. queue.push(node);
  164. } else {
  165. // 上記以外ならqueueを使用して最後の日本語より後の改行/空白を除去
  166. cleanup_queue(queue);
  167. }
  168. node.data = text;
  169. }
  170. // queueに溜まったテキストノードから末尾の改行/空白を削除
  171. function cleanup_queue(queue) {
  172. // 途中に改行文字を含むか?
  173. let has_break = false;
  174. for (let i = 1, length = queue.length; i < length; i++) { // 0番目は日本語が末尾なので1番目以降を調べる
  175. let text = queue[i].data;
  176. if (/\n/g.test(text)) {
  177. has_break = true;
  178. break;
  179. }
  180. }
  181. // 途中に改行を含まないなら削除しない
  182. if (!has_break) {
  183. queue.length = 0;
  184. return;
  185. }
  186. for (let i = 0, length = queue.length; i < length; i++) {
  187. let text = queue[i].data;
  188. text = text.replace(/[^\S ]*$/, "");
  189. queue[i].data = text;
  190. }
  191. queue.length = 0;
  192. }
  193. function escape_html(unsafeText) {
  194. if(typeof unsafeText !== 'string') {
  195. return unsafeText;
  196. }
  197. let escaped = document.createElement('div');
  198. escaped.textContent = unsafeText;
  199. return escaped.innerHTML;
  200. }
  201. // ]]>
  202. </script>
  203. <style>
  204. /** caption */
  205. caption::before {
  206. counter-increment: table;
  207. content: "Table " counter(table) " ";
  208. }
  209. figure>figcaption::before {
  210. counter-increment: figure;
  211. content: "Figure " counter(heading1) "." counter(figure) " ";
  212. }
  213. /* subfigureの開始時だけリセット */
  214. figure>figure:first-of-type {
  215. counter-reset: subfigure;
  216. }
  217. /* そのままやると,subfigureの番号が先に登場するのでfigureと番号が不一致 */
  218. /* だから,subfigure内ではfigureの番号を増加させ,抜けるときに減らす */
  219. figure>figure>figcaption::before {
  220. counter-increment: figure subfigure;
  221. counter-reset: subsubfigure;
  222. content: "Figure " counter(heading1) "." counter(figure) "(" counter(subfigure, lower-alpha) ") ";
  223. }
  224. /* 増やしたfigureの番号を減らして元に戻す */
  225. figure>figure figcaption::after {
  226. content: "";
  227. counter-increment: figure -1;
  228. }
  229. figure pre~figcaption::before,
  230. figure figcaption:first-child::before {
  231. counter-increment: listing;
  232. content: "List " counter(heading1) "." counter(listing) " ";
  233. }
  234. figure blockquote~figcaption::before {
  235. content: "—";
  236. }
  237. /* list */
  238. /* listが見出し設定の後にあると見出し番号がリセットされる? */
  239. /* ol { */
  240. /* counter-reset: enum; */
  241. /* list-style-type: none; */
  242. /* } */
  243. /* li::before { */
  244. /* counter-increment: enum; */
  245. /* content: counters(enum, ".") ". "; */
  246. /* } */
  247. ol {
  248. counter-reset: enum1;
  249. padding-left: 1em;
  250. }
  251. ol>li {
  252. list-style-type: none;
  253. padding-left: 0.5em;
  254. text-indent: -0.5em;
  255. }
  256. ol>li::before {
  257. counter-increment: enum1;
  258. content: counter(enum1) ". ";
  259. }
  260. ol>li>ol{
  261. counter-reset: enum2;
  262. padding-left: 1em;
  263. text-indent: -1em;
  264. }
  265. ol>li>ol>li::before {
  266. counter-increment: enum2;
  267. content: counter(enum1) "." counter(enum2) ". ";
  268. }
  269. ol>li>ol>li>ol{
  270. counter-reset: enum3;
  271. padding-left: 1em;
  272. text-indent: -1em;
  273. }
  274. ol>li>ol>li>ol>li::before {
  275. counter-increment: enum3;
  276. content: counter(enum1) "." counter(enum2) "." counter(enum3) ". ";
  277. }
  278. ol>li>ol>li>ol>li>ol{
  279. counter-reset: enum4;
  280. padding-left: 1em;
  281. text-indent: -1em;
  282. }
  283. ol>li>ol>li>ol>li>ol>li::before {
  284. counter-increment: enum4;
  285. content: counter(enum1) "." counter(enum2) "." counter(enum3) "." counter(enum4) ". ";
  286. }
  287. ol>li>ol>li>ol>li>ol>li>ol{
  288. counter-reset: enum5;
  289. padding-left: 1em;
  290. text-indent: -1em;
  291. }
  292. ol>li>ol>li>ol>li>ol>li>ol>li::before {
  293. counter-increment: enum5;
  294. content: counter(enum1) "." counter(enum2) "." counter(enum3) "." counter(enum4) "." counter(enum5) ". ";
  295. }
  296. ol>li>ol>li>ol>li>ol>li>ol>li>ol{
  297. counter-reset: enum6;
  298. padding-left: 1em;
  299. text-indent: -1em;
  300. }
  301. ol>li>ol>li>ol>li>ol>li>ol>li>ol>li::before {
  302. counter-increment: enum6;
  303. content: counter(enum1) "." counter(enum2) "." counter(enum3) "." counter(enum4) "." counter(enum5) "." counter(enum6) ". ";
  304. }
  305. /* title */
  306. body>h1:first-of-type {
  307. text-align: center;
  308. font-size: xx-large;
  309. }
  310. /* 節番号の設定 */
  311. body { counter-reset: heading1; }
  312. /** article要素の見出しでカウンターをリセット */
  313. /* article要素はそんなに入れ子にならないから最上位の見出しだけリセット */
  314. article>h1:first-of-type,
  315. article>h2:first-of-type,
  316. article>h3:first-of-type,
  317. article>h4:first-of-type,
  318. article>h5:first-of-type,
  319. article>h6:first-of-type {
  320. counter-reset: heading1;
  321. font-size: 2.17em;
  322. }
  323. section>h1:first-of-type,
  324. section>h2:first-of-type,
  325. section>h3:first-of-type,
  326. section>h4:first-of-type,
  327. section>h5:first-of-type,
  328. section>h6:first-of-type {
  329. counter-increment: heading1; counter-reset: heading2 figure table listing;
  330. font-size: 2.0em;
  331. }
  332. section>h1:first-of-type::before,
  333. section>h2:first-of-type::before,
  334. section>h3:first-of-type::before,
  335. section>h4:first-of-type::before,
  336. section>h5:first-of-type::before,
  337. section>h6:first-of-type::before {
  338. content: counter(heading1) " ";
  339. }
  340. section>section>h1:first-of-type,
  341. section>section>h2:first-of-type,
  342. section>section>h3:first-of-type,
  343. section>section>h4:first-of-type,
  344. section>section>h5:first-of-type,
  345. section>section>h6:first-of-type {
  346. counter-increment: heading2; counter-reset: heading3;
  347. font-size: 1.83em;
  348. }
  349. section>section>h1:first-of-type::before,
  350. section>section>h2:first-of-type::before,
  351. section>section>h3:first-of-type::before,
  352. section>section>h4:first-of-type::before,
  353. section>section>h5:first-of-type::before,
  354. section>section>h6:first-of-type::before {
  355. content: counter(heading1) "." counter(heading2) " ";
  356. }
  357. section>section>section>h1:first-of-type,
  358. section>section>section>h2:first-of-type,
  359. section>section>section>h3:first-of-type,
  360. section>section>section>h4:first-of-type,
  361. section>section>section>h5:first-of-type,
  362. section>section>section>h6:first-of-type {
  363. counter-increment: heading3; counter-reset: heading4;
  364. font-size: 1.67em;
  365. }
  366. section>section>section>h1:first-of-type::before,
  367. section>section>section>h2:first-of-type::before,
  368. section>section>section>h3:first-of-type::before,
  369. section>section>section>h4:first-of-type::before,
  370. section>section>section>h5:first-of-type::before,
  371. section>section>section>h6:first-of-type::before {
  372. content: counter(heading1) "." counter(heading2) "." counter(heading3) " ";
  373. }
  374. section>section>section>section>h1:first-of-type,
  375. section>section>section>section>h2:first-of-type,
  376. section>section>section>section>h3:first-of-type,
  377. section>section>section>section>h4:first-of-type,
  378. section>section>section>section>h5:first-of-type,
  379. section>section>section>section>h6:first-of-type {
  380. counter-increment: heading4; counter-reset: heading5;
  381. font-size: 1.50em;
  382. }
  383. section>section>section>section>h1:first-of-type::before,
  384. section>section>section>section>h2:first-of-type::before,
  385. section>section>section>section>h3:first-of-type::before,
  386. section>section>section>section>h4:first-of-type::before,
  387. section>section>section>section>h5:first-of-type::before,
  388. section>section>section>section>h6:first-of-type::before {
  389. content: counter(heading1) "." counter(heading2) "." counter(heading3) "."
  390. counter(heading4) " ";
  391. }
  392. section>section>section>section>section>h1:first-of-type,
  393. section>section>section>section>section>h2:first-of-type,
  394. section>section>section>section>section>h3:first-of-type,
  395. section>section>section>section>section>h4:first-of-type,
  396. section>section>section>section>section>h5:first-of-type,
  397. section>section>section>section>section>h6:first-of-type {
  398. counter-increment: heading5; counter-reset: heading6;
  399. font-size: 1.33em;
  400. }
  401. section>section>section>section>section>h1:first-of-type::before,
  402. section>section>section>section>section>h2:first-of-type::before,
  403. section>section>section>section>section>h3:first-of-type::before,
  404. section>section>section>section>section>h4:first-of-type::before,
  405. section>section>section>section>section>h5:first-of-type::before,
  406. section>section>section>section>section>h6:first-of-type::before {
  407. content: counter(heading1) "." counter(heading2) "." counter(heading3) "."
  408. counter(heading4) "." counter(heading5) " ";
  409. }
  410. /** heading6 だけカウンターが増えない? */
  411. section>section>section>section>section>section>h1:first-of-type,
  412. section>section>section>section>section>section>h2:first-of-type,
  413. section>section>section>section>section>section>h3:first-of-type,
  414. section>section>section>section>section>section>h4:first-of-type,
  415. section>section>section>section>section>section>h5:first-of-type,
  416. section>section>section>section>section>section>h6:first-of-type {
  417. /* 使っていなくてもheading7をリセットしないとheading6が増えないっぽい */
  418. counter-increment: heading6; counter-reset: heading7;
  419. font-size: 1.17em;
  420. }
  421. section>section>section>section>section>section>h1:first-of-type::before,
  422. section>section>section>section>section>section>h2:first-of-type::before,
  423. section>section>section>section>section>section>h3:first-of-type::before,
  424. section>section>section>section>section>section>h4:first-of-type::before,
  425. section>section>section>section>section>section>h5:first-of-type::before,
  426. section>section>section>section>section>section>h6:first-of-type::before {
  427. content: counter(heading1) "." counter(heading2) "." counter(heading3) "."
  428. counter(heading4) "." counter(heading5) "." counter(heading6) " ";
  429. }
  430. /** Table */
  431. table {
  432. border-collapse: collapse;
  433. border: none;
  434. /** centering **/
  435. margin-left: auto;
  436. margin-right: auto;
  437. margin-top: 0.5em;
  438. margin-bottom: 0.5em;
  439. }
  440. /** Table color **/
  441. tbody>tr:nth-of-type(even) {
  442. background: #ffffaa;
  443. }
  444. thead>tr>th {
  445. background: #ffcc99;
  446. text-align: center;
  447. font-weight: bold
  448. }
  449. /** Column margin **/
  450. td {
  451. padding-left: 0.5em;
  452. padding-right: 0.5em;
  453. }
  454. /** Table border **/
  455. td, th {
  456. border-style: none;
  457. }
  458. thead>tr:first-of-type {
  459. border-top-style: solid;
  460. border-top-width: medium;
  461. }
  462. thead>tr:last-of-type {
  463. border-bottom-style: solid;
  464. border-bottom-width: thin;
  465. }
  466. tbody>tr:first-of-type {
  467. border-top-style: solid;
  468. border-top-width: thin;
  469. }
  470. tbody>tr:last-of-type {
  471. border-bottom-style: solid;
  472. border-bottom-width: medium;
  473. }
  474. th, td {
  475. word-wrap: break-word;
  476. }
  477. /** Cross reference */
  478. [href^="#chap:"]::before {
  479. content: "第";
  480. }
  481. [href^="#chap:"]::after {
  482. content: "章";
  483. }
  484. [href^="#sec:"]::after, [href^="#sub"]::after {
  485. content: "節";
  486. }
  487. [href^="#par:"]::after {
  488. content: "段落";
  489. }
  490. [href^="#enu:"]::before {
  491. content: "項目";
  492. }
  493. [href^="#eq:"]::before {
  494. content: "式(";
  495. }
  496. [href^="#eq:"]::after {
  497. content: ")";
  498. }
  499. [href^="#fig:"]::before {
  500. content: "Figure ";
  501. }
  502. [href^="#tab:"]::before {
  503. content: "Table ";
  504. }
  505. [href^="#lis:"]::before {
  506. content: "List ";
  507. }
  508. /** Font */
  509. body {
  510. font-family: "TeX Gyre Heros", "FreeSans", "Migu 1P", "TakaoGothic", "VL Gothic", "Yu Gothic", "Meiryo UI", sans-serif;
  511. }
  512. p {
  513. text-indent: 1em;
  514. word-break: break-all;
  515. }
  516. h1, h2, h3, h4, h5, h6 {
  517. font-family: "TeX Gyre Heros", "Migu 1P", "TakaoGothic", "VL Gothic", "Yu Gothic", "Meiryo UI", sans-serif;
  518. font-weight: bold;
  519. /* orange */
  520. /* background-color: rgb(100%,80%,0%); */
  521. background-color: #e6e6ff;
  522. margin-top: 0.5em;
  523. margin-bottom: 0.5em;
  524. }
  525. em, strong {
  526. color: red;
  527. font-weight: bold;
  528. }
  529. strong {background-color: yellow;}
  530. /** PC */
  531. kbd, code, samp, var {
  532. font-family: "Migu 1M", "Inconsolata", "DejaVu Sans Mono", "Consolas", "TakaoGothic", "VL Gothic", "HGGothicM", "MS Gothic", monospace;
  533. border-width: thin;
  534. border-style: solid;
  535. border-color: #cccccc;
  536. border-radius: 5px;
  537. }
  538. kbd {
  539. background-color: #ffe6e6;
  540. }
  541. code {
  542. background-color: #e6ffe6;
  543. }
  544. samp {
  545. background-color: #e6e6e6;
  546. }
  547. var {
  548. background-color: #e6ffff;
  549. }
  550. pre {
  551. font-family: "Migu 1M", "Inconsolata", "DejaVu Sans Mono", "Consolas", "TakaoGothic", "VL Gothic", "HGGothicM", "MS Gothic", monospace;
  552. background-color: #ffffe6;
  553. overflow: auto;
  554. }
  555. /** Line number for source code */
  556. pre>code, pre>samp {
  557. display: block;
  558. border-style: none;
  559. position: relative;
  560. padding-left: 3em;
  561. overflow-x: auto;
  562. }
  563. pre>code::before, pre>samp::before {
  564. content: "0001000200030004000500060007000800090010001100120013001400150016001700180019002000210022002300240025002600270028002900300031003200330034003500360037003800390040004100420043004400450046004700480049005000510052005300540055005600570058005900600061006200630064006500660067006800690070007100720073007400750076007700780079008000810082008300840085008600870088008900900091009200930094009500960097009800990100";
  565. display: block;
  566. width: 2.4em;
  567. height: 100%;
  568. position: absolute;
  569. left: 0;
  570. white-space: pre-wrap;
  571. word-wrap: break-word;
  572. overflow-wrap: break-word;
  573. word-break: break-all;
  574. overflow-y: hidden;
  575. border-right: 1px solid;
  576. }
  577. /** Blockquote */
  578. blockquote {
  579. background-color: #eee;
  580. padding: 1.0em;
  581. padding-left: 3em;
  582. position: relative;
  583. }
  584. blockquote::before {
  585. content: "“";
  586. font-size: 600%;
  587. line-height: 1em;
  588. font-family: serif;
  589. color: #999;
  590. position: absolute;
  591. left: 0;
  592. top: 0;
  593. }
  594. blockquote p {
  595. margin: 0;
  596. }
  597. /** Description element */
  598. dt {
  599. font-weight: bold;
  600. font-family: sans-serif;
  601. padding-left: 0.5em;
  602. }
  603. dt, dd {
  604. line-height: 1.5em;
  605. word-wrap: break-word;
  606. }
  607. dt {
  608. float: left;
  609. width: 20%;
  610. }
  611. dd {
  612. margin-left: 22%;
  613. }
  614. dd::after {
  615. content: "";
  616. display: block;
  617. clear: both;
  618. }
  619. /** figure */
  620. figure {
  621. text-align: center;
  622. }
  623. figure blockquote~figcaption {
  624. text-align: right;
  625. }
  626. figure blockquote {
  627. text-align: left;
  628. }
  629. figure pre {
  630. text-align: left;
  631. }
  632. figure>dl {
  633. border-radius: 0.4em;
  634. border: solid thin rgb(10%,10%,10%);
  635. background-color: rgb(90%,90%,90%);
  636. text-align: left;
  637. }
  638. figure>dl>dt {
  639. display: block;
  640. width: 100%;
  641. border-top-left-radius: 0.2em;
  642. border-top-right-radius: 0.2em;
  643. color: white;
  644. background-color: rgb(30%,30%,30%);
  645. box-sizing: border-box;
  646. }
  647. figure>dl>dd {
  648. padding-left: 0.5em;
  649. margin: auto;
  650. }
  651. figure>dl>dd:nth-of-type(n+2) {
  652. border-top-style: dashed;
  653. border-top-width: thin;
  654. }
  655. .column-2 {
  656. width: 45%;
  657. display: inline-block;
  658. vertical-align: bottom;
  659. margin-left: 1em;
  660. margin-right: 1em;
  661. }
  662. .column-3 {
  663. width: 30%;
  664. display: inline-block;
  665. vertical-align: bottom;
  666. margin-left: 1em;
  667. margin-right: 1em;
  668. }
  669. .column-4 {
  670. width: 20%;
  671. display: inline-block;
  672. vertical-align: bottom;
  673. margin-left: 1em;
  674. margin-right: 1em;
  675. }
  676. /** Show update date */
  677. [data-updated-date]::after {
  678. color: gray;
  679. font-size: 0.5em;
  680. content: " Update: " attr(data-updated-date);
  681. }
  682. /** Page style */
  683. @page {
  684. margin: 2cm;
  685. }
  686. /** Print style */
  687. @media print {
  688. /* ShowerでPDF作成するときにChromiumでtheadが二重に表示される対策 */
  689. thead {
  690. display: table-row-group;
  691. }
  692. /* Show URL */
  693. a[href]::after {
  694. content: " (" attr(href) ")";
  695. }
  696. a[href^="#"]::after,
  697. a[href^="javascript"]::after {
  698. content: "";
  699. }
  700. }
  701. </style>
  702. </head>
  703. <body>
  704. <h1>GenerateTOC</h1>
  705. <main>
  706. <p>XHTML5の文章から<code>section</code>要素を使った<a href="https://www.w3.org/TR/html51/sections.html#headings-and-sections">明示的なセクション</a>見出し(<code>section&gt;h1-h6</code>)を抽出して目次を作成する。紹介記事:<a
  707. href="https://senooken.jp/product/20170506/">GenerateTOC: Generator for Table of Contents from HTML Headings</a>。</p>
  708. <nav>
  709. <h2>Table of Contents</h2>
  710. <ol>
  711. <li><a href="#Input/Output-Form">Input/Output Form</a></li>
  712. <li><a href="#Usage">Usage</a>
  713. <ol>
  714. <li><a href="#Brief">Brief</a></li>
  715. <li><a href="#Details">Details</a></li>
  716. </ol>
  717. </li>
  718. <li><a href="#Sample-Input/Output">Sample Input/Output</a></li>
  719. </ol>
  720. </nav>
  721. <p></p>
  722. <section>
  723. <h1 id="Input/Output-Form">Input/Output Form</h1>
  724. <form>
  725. <fieldset> <legend>Input HTML</legend> <textarea id="input"
  726. placeholder="Input here" style="width: 100%; height: 10em;"></textarea> <button
  727. type="button" onclick="GenerateTOC();">Go+Copy</button> </fieldset>
  728. </form>
  729. <form>
  730. <fieldset> <legend>Output HTML</legend> <textarea id="output"
  731. placeholder="Output here" style="width: 100%; height: 10em;"></textarea> </fieldset>
  732. </form>
  733. <form>
  734. <fieldset> <legend>Result View</legend> <output id="view"></output> </fieldset>
  735. </form>
  736. </section>
  737. <section>
  738. <h1 id="Usage">Usage</h1>
  739. <section>
  740. <h2 id="Brief">Brief</h2>
  741. <p> </p>
  742. <ol>
  743. <li>[Input HTML]に目次を生成したいHTMLコードを記入する。</li>
  744. <li>[Go+Copy]を押下する。</li>
  745. <li>[Output HTML]に目次とリンクの作成のために見出し要素(<code>h1-h6</code>)の<code>id</code>属性が記入されたHTMLコードが出力されてクリップボードにコピーされる。また、[Result View]に見出し追加後のHTMLコードの描画結果を表示する。</li>
  746. </ol>
  747. </section>
  748. <section>
  749. <h2 id="Details">Details</h2>
  750. <p>入力として,ルートに<code>section</code>要素を持つXHTMLを与える。 </p>
  751. <p>空要素に終端スラッシュがなかったり,XMLで認められない構文を使っているHTMLを使う場合は,事前に例えば以下のサイトでHTMLをXHTMLに変換しておく。 </p>
  752. <blockquote>
  753. <p><a href="http://www.csgnetwork.com/cvthtml2xhtml.html">HTML To XHTML Code Converter</a></p>
  754. </blockquote>
  755. <p>目次には,<code>section</code>要素を使った<strong><a href="https://www.w3.org/TR/html51/sections.html#headings-and-sections">明示的な見出し</a>のみを対象</strong>とする。<code>section</code>要素を使わずに,<code>h1-h6</code>要素を単独で使った<em>暗黙的な見出しは目次の対象外</em>となる。</p>
  756. <figure><figcaption>Sample of Target HTML</figcaption>
  757. <pre><code>&lt;section&gt;
  758. &lt;h1&gt;Heading 1&lt;/h1&gt;
  759. &lt;section&gt;
  760. &lt;h2&gt;Heading 2&lt;/h2&gt;
  761. &lt;/section&gt;
  762. &lt;/section&gt;
  763. </code></pre> </figure>
  764. <p></p>
  765. <figure> <figcaption>Sample of non-Target HTML</figcaption>
  766. <pre><code>&lt;h1&gt;Heading 1&lt;/h1&gt;<br />&lt;h2&gt;Heading 2&lt;/h2&gt; </code></pre>
  767. </figure><p>目次の生成は,2個目の<code>section</code>要素(<code>section&gt;section</code>)の直前に,見出しへのリンクが付けられた番号付きリスト(<code>ol&gt;li</code>要素)を含む<code>nav</code>要素を挿入することで行う。 </p><figure>
  768. <figcaption>Place of Table of Contents</figcaption>
  769. <pre><code>&lt;section&gt;
  770. &lt;h1&gt;Heading 1&lt;/h1&gt;
  771. &lt;!-- Here is the place table of contents is inserted by nav element. --&gt;
  772. &lt;section&gt;
  773. &lt;h2&gt;Heading 2&lt;/h2&gt;
  774. &lt;/section&gt;
  775. &lt;/section&gt;
  776. </code></pre>
  777. </figure><p></p><p>見出しへのリンクを作るために,<code>section&gt;h1-h6</code>要素の<code>id</code>属性に,実行日時(<code>YYYYMMDDThhmm</code>形式)とテキストの空白をハイフン<code>-</code>に置換した値を<strong>上書き</strong>で設定する。</p>
  778. <p>Example: <code>&lt;h1&gt;Heading 1&lt;/h1&gt;</code> -&gt; <code>&lt;h1 id="20170506T04:30_Heading-1"&gt;Heading 1&lt;/h1&gt;</code></p>
  779. <p>その際に,以下の文字はエスケープ処理が難しいので<code>id</code>属性からは削除する。</p>
  780. <pre><code>~^`'"(){}\\|!&amp;&lt;&gt;</code></pre><p>目次の見出しは最初の目次対象の見出しと同じ<code>h1-h6</code>要素を使い,テキストは<code>Table of Contents</code>となる。</p><p>[Go+Copy]ボタンを押下して何も反応がないときは,HTMLをうまく処理できていない可能性が高い。例えば,ルートの<code>section</code>要素の終了タグを記入し忘れるなど,[Input HTML]に記入したHTMLがXHTMLとして適切かどうか確認したほうがいい。</p><pre><code></code></pre></section></section><section>
  781. <h1 id="Sample-Input/Output">Sample Input/Output</h1>
  782. <p> </p>
  783. <ul>
  784. </ul>
  785. <table border="1" style="width: 100%;">
  786. <caption>Sample Input and Output</caption>
  787. <thead>
  788. <tr>
  789. <th><br />
  790. </th>
  791. <th>Input</th>
  792. <th>Output</th>
  793. </tr>
  794. </thead>
  795. <tbody>
  796. <tr>
  797. <td>Source</td>
  798. <td>
  799. <pre><code>&lt;section&gt;
  800. &lt;h1&gt;Title&lt;/h1&gt;<br />  &lt;section&gt;
  801. &lt;h2&gt;Heading 1~^`"'(){}\|!&amp;amp;&amp;lt;&amp;gt;[]=-+*/%;:.,?$#@_&lt;/h2&gt;
  802. &lt;section&gt;
  803. &lt;h3&gt;Heading 1-1&lt;/h3&gt;
  804. &lt;/section&gt;
  805. &lt;section&gt;
  806. &lt;h3&gt;Heading 1-2&lt;/h3&gt;
  807. &lt;/section&gt;
  808. &lt;/section&gt;
  809. &lt;section&gt;
  810. &lt;h2&gt;Heading 2&lt;/h2&gt;
  811. &lt;/section&gt;
  812. &lt;/section&gt;</code></pre>
  813. </td>
  814. <td>
  815. <pre><code>&lt;section xmlns="http://www.w3.org/1999/xhtml"&gt;
  816. &lt;h1 id="20170505T0118_Title"&gt;Title&lt;/h1&gt;
  817. &lt;nav&gt;
  818. &lt;h2&gt;Table of Contents&lt;/h2&gt;&lt;ol&gt;
  819. &lt;li&gt;&lt;a href="#20170505T0118_Heading-1[]=-+*/%;:.,?$#@_"&gt;Heading 1~^`"'(){}\|!&amp;amp;&amp;lt;&amp;gt;[]=-+*/%;:.,?$#@_&lt;/a&gt;
  820. &lt;ol&gt;
  821. &lt;li&gt;&lt;a href="#20170505T0118_Heading-1-1"&gt;Heading 1-1&lt;/a&gt;&lt;/li&gt;
  822. &lt;li&gt;&lt;a href="#20170505T0118_Heading-1-2"&gt;Heading 1-2&lt;/a&gt;&lt;/li&gt;
  823. &lt;/ol&gt;
  824. &lt;/li&gt;
  825. &lt;li&gt;&lt;a href="#20170505T0118_Heading-2"&gt;Heading 2&lt;/a&gt;&lt;/li&gt;
  826. &lt;/ol&gt;
  827. &lt;/nav&gt;
  828. &lt;section&gt;
  829. &lt;h2 id="20170505T0118_Heading-1[]=-+*/%;:.,?$#@_"&gt;Heading 1~^`"'(){}\|!&amp;amp;&amp;lt;&amp;gt;[]=-+*/%;:.,?$#@_&lt;/h2&gt;
  830. &lt;section&gt;
  831. &lt;h3 id="20170505T0118_Heading-1-1"&gt;Heading 1-1&lt;/h3&gt;
  832. &lt;/section&gt;
  833. &lt;section&gt;
  834. &lt;h3 id="20170505T0118_Heading-1-2"&gt;Heading 1-2&lt;/h3&gt;
  835. &lt;/section&gt;
  836. &lt;/section&gt;
  837. &lt;section&gt;
  838. &lt;h2 id="20170505T0118_Heading-2"&gt;Heading 2&lt;/h2&gt;
  839. &lt;/section&gt;
  840. &lt;/section&gt;</code></pre>
  841. </td>
  842. </tr>
  843. <tr>
  844. <td>View</td>
  845. <td>
  846. <section>
  847. <h1>Title</h1>
  848. <section>
  849. <h2>Heading 1~^`"'(){}\|!&amp;&lt;&gt;[]=-+*/%;:.,?$#@_</h2>
  850. <section>
  851. <h3>Heading 1-1</h3>
  852. </section>
  853. <section>
  854. <h3>Heading 1-2</h3>
  855. </section>
  856. </section>
  857. <section>
  858. <h2>Heading 2</h2>
  859. </section>
  860. </section>
  861. </td>
  862. <td>
  863. <section xmlns="http://www.w3.org/1999/xhtml">
  864. <h1 id="20170505T0118_Title">Title</h1>
  865. <nav>
  866. <h2>Table of Contents</h2>
  867. <ol><li><a href="#20170505T0118_Heading-[]=-+*/%;:.,?$#@_">Heading 1~^`"'(){}\|!&amp;&lt;&gt;[]=-+*/%;:.,?$#@_</a>
  868. <ol><li><a href="#20170505T0118_Heading-1-1">Heading 1-1</a></li><li><a
  869. href="#20170505T0118_Heading-1-2">Heading 1-2</a></li></ol>
  870. </li><li><a href="#20170505T0118_Heading-2">Heading 2</a></li></ol>
  871. </nav>
  872. <section>
  873. <h2 id="20170505T0118_Heading-[]=-+*/%;:.,?$#@_">Heading 1~^`"'(){}\|!&amp;&lt;&gt;[]=-+*/%;:.,?$#@_</h2>
  874. <section>
  875. <h3 id="20170505T0118_Heading-1-1">Heading 1-1</h3>
  876. </section>
  877. <section>
  878. <h3 id="20170505T0118_Heading-1-2">Heading 1-2</h3>
  879. </section>
  880. </section>
  881. <section>
  882. <h2 id="20170505T0118_Heading-2">Heading 2</h2>
  883. </section>
  884. </section>
  885. </td>
  886. </tr>
  887. </tbody>
  888. </table>
  889. <p></p></section>
  890. </main>
  891. <footer>
  892. <p> <small>Author: SENOO, Ken (<a href="https://twitter.com/senopen">@senopen</a>). License: CC0. Created date: <time>2017-05-06.</time></small></p>
  893. </footer>
  894. </body>
  895. </html>