初めに
ちょっとしたメモをとるのに便利なマークダウンは世界中で使われていて、自分もよく使ってます。
ただ、最近マークダウンで書いた文字に色をつけたいな〜となりいろいろ調べてました(HTMLをべた書きしたり・・・)
自分でマークダウンの変換を作ったほうが早い!?と迷走したりもしましたが、最終的にJavaScriptのライブラリたちを改造するのに落ち着きました。
MarkdownのJavaScriptライブラリ達
軽く調べたところ、showdown.js、markdown-js、marked.jsの3種類がよくヒットして、これらは、下記の記事で詳しく比較されてました。
JavaScript - Markdownパーサーのshowdown.js、markdown-js、markedを簡単比較
marked.jsに選んだのはvue.jsに簡単なサンプルがあったのでvue.jsの勉強も兼ねて選んでみました。
marked.jsの使い方
使い方は簡単でmarked.jsを読み込み下記の様に実行すればconsoleにパースされたHTMLが出て来る。(公式より)
console.log(marked('I am using __markdown__.'));
パースされたHTMLが変えるためそれをそのまま要素に入れてあげればいい。(公式より)
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Marked in the browser</title>
<script src="lib/marked.js"></script>
</head>
<body>
<div id="content"></div>
<script>
document.getElementById('content').innerHTML =
marked('# Marked in browser\n\nRendered by **marked**.');
</script>
</body>
</html>
ただ、これだと一般的なマークダウンエディタのように、左側で編集、右側にビューとできないのでキー入力され、離れた時にパースを実行させてみる。(CSSはvue.jsより)
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Sample</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.min.js"></script>
<style type="text/css">
html,body,#editor {
margin: 0;
height: 100%;
font-family: 'Helvetica Neue', Arial, sans-serif;
color: #333;
}
textarea,#editor div {
display: inline-block;
width: 49%;
height: 100%;
vertical-align: top;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0 20px;
}
textarea {
border: none;
border-right: 1px solid #ccc;
resize: none;
outline: none;
background-color: #f6f6f6;
font-size: 14px;
font-family: 'Monaco', courier, monospace;
padding: 20px;
}
code {
color: #f66;
}
</style>
</head>
<body>
<div id="editor">
<textarea id="input"># hoge</textarea>
<div id="result"></div>
</div>
<script>
document.getElementById('input').onkeyup = function(e) {
document.getElementById('result').innerHTML =
marked(document.getElementById('input').value);
};
</script>
</body>
</html>
上記サンプルはいい感じに動く。
vue.jsのサンプルは下記の通り。
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Vue-Sample</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.28/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.min.js"></script>
<style type="text/css">
html,body,#editor {
margin: 0;
height: 100%;
font-family: 'Helvetica Neue', Arial, sans-serif;
color: #333;
}
textarea,#editor div {
display: inline-block;
width: 49%;
height: 100%;
vertical-align: top;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 0 20px;
}
textarea {
border: none;
border-right: 1px solid #ccc;
resize: none;
outline: none;
background-color: #f6f6f6;
font-size: 14px;
font-family: 'Monaco', courier, monospace;
padding: 20px;
}
code {
color: #f66;
}
</style>
</head>
<body>
<div id="editor">
<textarea v-model="input" debounce="300"></textarea>
<div v-html="input | marked"></div>
</div>
<script>
new Vue({
el: '#editor',
data: {
input: '# hello'
},
filters: {
marked: marked
}
})
</script>
</body>
</html>
うーん。JavaScriptがスッキリしている。
オリジナルの記法の実装
では、marked.jsを弄ってオリジナルの記法を実装していきたいと思います。
marked.jsはrenderをoverrideすることができます。
公式のoverrideのサンプルは下記の通り。
var marked = require('marked');
var renderer = new marked.Renderer();
renderer.heading = function (text, level) {
var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
return '<h' + level + '><a name="' +
escapedText +
'" class="anchor" href="#' +
escapedText +
'"><span class="header-link"></span></a>' +
text + '</h' + level + '>';
},
console.log(marked('# heading+', { renderer: renderer }));
重要なのはvar renderer = new marked.Renderer();
でRnederオブジェクトを作成し、renderer.heading
でh1,h2要素などのレンダーを上書きをしている点です。
function(text,level)
の引数は
## fuga
とマークダウンを書いた場合、text
にfuga
、level
に2
が入ります。
marked.jsは読み込んだテキストをパースして、Renderオブジェクトの各関数によってHTMLに変換をしています。
marked.jsの764行目あたりに各マークダウンのHTMLの変換処理が書かれています。
strong
などインライン要素はわかりやすいです。
Renderer.prototype.strong = function(text) {
return '<strong>' + text + '</strong>';
};
本当はオリジナルの記法を追加したかったですが、marked.jsがrenderのoverrideを提供しているのでこれを利用します。
書いたコードがこちらです。
renderer.em = function(text) {
var indexNumber = text.indexOf('/');
if (indexNumber !== -1 && text.substr(indexNumber - 1, 1) !== "\\") {
return '<span style="color:' + text.substr(0, indexNumber) + ';">' + text.substr(indexNumber + 1) + '</span>';
}
return '<em>' + text.replace('\\/', '/') + '</em>';
};
今回はem要素を利用しました。
ここから試せます。
使い方は下記のように色を最初に書き、「/」で区切りテキストを記述することで色を指定できます。
また、「/」を表示したい場合のためにエスケープを実装しています。エスケープは「\」を「/」の前に入れます。
*red/hoge*
*red\/hoge*
これで、なんとか実装ができました。
最後に
marked.jsはコードのハイライトもできたりサーバーサイドでも使えるため、いろいろできる幅が広がりそうです。
いいマークダウンライフを!