Ghost Blog は MarkdownレンダリングにShowdownモジュールを利用しているが、何故かそれに含まれるtable拡張を利用していない。幾度か要望がでたり議論もあったりしたのだが、一向に進展しないためテーマレベルで代替策を講じることにしてみた。
jquery.ex-mark-table.js
このスクリプトはjQuery Extensionになっていて、ロードすると jQuery.markTable() メソッドが追加される。これはMarkdown形式のtable書式を含むオブジェクトを渡してやるとHTML-Tableにオンデマンドで整形する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | // // jquery.ex-mark-table.js // // $Id: mark-table.js 143 2015-05-08 09:31:06Z askn $ // ( function ($) { $.fn.markTable = function (config) { var defaults = { className: 'mark-table' } var options = $.extend(defaults, config); return this .each( function (i) { var title = $( this ).attr( "title" ); var $exported = $( "<table/>" ); var $body = $( "<tbody/>" ); var table = [], align = [], header = []; var imported = $( this ).html().split(/([^\n]*\n)/); while (imported.length) { var line = imported.shift(); if (line) { if (line.length == 0) continue ; var column = line.match(/([^\|]*\|)/g); if (column && column.length > 0) { column.shift(); table.push($.map(column, function (val, index) { return val.replace(/\|$/, "" ); })); } } } if (title != undefined) { $( "<caption/>" ).text(title).appendTo($exported); } if (table.length && table[0].length && !table[0][0].match(/(^\:-|-\:$)/)) { header = table.shift(); } if (table.length && table[0].length && table[0][0].match(/(^\:-|-\:$)/)) { align = $.map(table.shift(), function (val, index) { if (val.match(/^\:\-*\:$/)) { return "center" ; } else if (val.match(/^\-*\:$/)) { return "right" ; } else { return "left" ; } }); } if (header.length) { var $head = $( "<tr/>" ); $.each(header, function (key, val) { var $column = $( "<th/>" ).html(val); if (align[key]) { $column.addClass(align[key]); } $head.append($column); }); $( "<thead/>" ).append($head).appendTo($exported); } if (table.length) { while (table.length) { var $line = $( "<tr/>" ); $.each(table.shift(), function (key, val) { var $column = $( "<td/>" ).html(val); if (align[key]) { $column.addClass(align[key]); } $line.append($column); }); $line.appendTo($body); } $body.appendTo($exported); } $( this ).replaceWith($( "<div/>" ).addClass(options.className).append($exported)); }); }; })(jQuery); // End of Script |
これをさらに次のようにしてindex.js等から呼び出す。1
( function pageInit () { $( 'code[class^="language-"]' ).each( function () { var $ this = $( this ); var attr = $ this .attr( 'class' ).replace(/^language-?/, '' ); var match = attr.match(/title:(.*)/); if (match) { $ this .parent().attr( 'title' , match[1]); attr = attr.replace(/title:.*/, '' ); } if (attr != null && attr.match(/^table/)) { // マークダウンテーブル $ this .parent().html($ this .html()).markTable(); } }); })(); |
Markdownの方では、ネイティブにMarkdownテーブル書式を書くことが出来ないので、全体をコード書式で括るようにする。このとき三連バッククォート2の直後にtableというキーワードを付与する。更にtaitle:キーワードを足すとここから行末までをテーブルキャプションにすることが出来る。3
```table title:テーブルキャプション |foo |bar |baz | |:---|:----:|----:| |Left|center|right| ``` |
これを保存するとGhostは次のHTMLコードに変換する。
{gfm-js-extract-pre-1} |
付与したキーワードにlanguage-前置詞が付いたclass名として埋め込まれるのがミソで、ようはこれをjQueryで検出してクライアントサイドでpreをまるごとtableセットに書き換えてやる。あとはCSSで表示を整形4してやればよい。
foo | bar | baz |
---|---|---|
Left | center | right |
これの実際のHTMLはつぎのように展開されている。
< div class = "mark-table" > < table > < caption >テーブルキャプション</ caption > < thead > < tr > < th class = "left" >foo </ th > < th class = "center" >bar </ th > < th class = "right" >baz </ th > </ tr > </ thead > < tbody > < tr > < td class = "left" >Left</ td > < td class = "center" >center</ td > < td class = "right" >right</ td > </ tr > </ tbody > </ table > </ div > |
今後ネイティブにMarkdownテーブル記法が実装された場合は(本実装との互換性はともかく)三連バッククォートを除去するだけで対応できるはずだ。