自作Markdown記法とプレビュー機能の作り方



Markdown記法とは

はてなブログやQiitaなど様々なところで使われている記法です。

Markdown(マークダウン)は、文書を記述するための軽量マークアップ言語のひとつである。本来はプレーンテキスト形式で手軽に書いた文書からHTMLを生成するために開発されたものである。 (wikipedia引用)

例えば、「*タイトル*」と書くと「タイトル」と表示されるような感じです。

今回は、Markdown記法で書かれた文章をhtmlに変換し、
プレビュー機能で文章を表示するところまでを紹介します。



利用するもの

・AngularJS
 プレビュー機能の作成に利用します。
 ※当記事ではAngularJSの知識をある程度持っていることを前提で進めていきます。
 AngularJS — Superheroic JavaScript MVW Framework

・propellerkit
 プレビュー機能の作成に利用します。
 propeller.in




MarkdownをHTMLに変換

今回考えたMarkdown記法は以下の通りです。

Markdown記法 表示結果
#titleタイトル#
タイトル
%red赤い文字% 赤い文字
*bold太い文字* 太い文字
_under下線_ 下線
※あくまで分かりやすさ優先で考えたのでhtmlで書くより面倒なものもあります^^;

さっそくMarkdownで入力された文章をhtmlに変換する処理を作成していきます。
ここでは仮処理として、ボタンが押されたらMarkdownをhtmlに変換するようにします。

ここからAngularJSを利用していきます。

htmlはこちら

<textarea type="text" cols="120" rows="20"ng-model="content"></textarea>
<button type="button" ng-click="convert();"> 変換 </button >


Javascriptはこちら

$scope.content = "";
function convert(){
 var convertContent = $scope.content;
 console.log(convertContent) //変換処理前
 /*変換処理*/
  //タイトル
   convertContent = convertContent.split('#title').join("<div style='font-size:20px;font-weight:bold;'>");
    convertContent = convertContent.split('#').join("</div>");
  //赤い文字
    convertContent = convertContent.split('%red').join("<span style='color:red;'>");
    convertContent = convertContent.split('%').join("</span>");
  //太文字
    convertContent = convertContent.split('*bold').join("<b>");
    convertContent = convertContent.split('*').join("</b>");
  //下線
    convertContent = convertContent.split('_under').join("<u>");
    convertContent = convertContent.split('_').join("</u>");
 
  console.log(convertContent) //変換処理後
}

※AngularJSの基本的な部分は省略しています。


単純なので簡単に説明します。

まず変換ボタンが押されると、「ng-click」にある「convert」関数が動作され、
変換処理がはじまります。

変換処理ではjavascriptの「split」メソッドと「join」メソッドを利用しています。

例えば、入力されたMarkdownの中に「#title」という文字列が含まれていた場合。
まずは「split」メソッドを使って、この文字列を除去します。
次に「join」メソッドを使って、除去した箇所に対応するhtmlを追加します。

このようにすることで、Markdownをhtmlの形に変換することができます。
(だいぶんゴリ押しですが...)

実際にコンソールを使って、変換処理前と変換処理後の文章を比べてみましょう。

例えば、以下のような文章をフォームに入力したとします。

#titleタイトルです#

%red赤い文字です%

*bold太い文字です*

_under下線付きです_


コンソール結果は以下の通りです。

【変換前】

#titleタイトルです#

%red赤い文字です%

*bold太い文字です*

_under下線付きです_

【変換後】

<div style='font-size:20px;font-weight:bold;margin-bottom:-10px;'>タイトルです</div>

<span style='color:red;'>赤い文字です</span>

<b>太い文字です</b>

<u>下線付きです</u>


Markdownの部分がhtmlに変換されていることがわかります。

以上で、Markdownをhtmlに変換する処理が完成しました。
次に、htmlに変換した文章をプレビューで表示する機能を作成していきましょう。



まずはプレビューを表示していきましょう。

上記で仮処理としていた「変換」ボタンを「プレビュー」ボタンに変更し
ボタンが押されたらプレビューのダイアログが表示される機能を作成してきます。

ここでは、propellerkitのModalを利用します。
propeller.in



それでは早速htmlを見ていきましょう。

<textarea type="text" cols="120" rows="20"ng-model="content"></textarea>
<button type="button" data-target="#preview-dialog" data-toggle="modal" ng-click="preview()"> プレビュー </button >
<!--プレビューダイアログ-->
<div tabindex="-1" class="modal fade" id="preview-dialog" aria-hidden="true">
        <div class="modal-dialog modal-lg" >
            <div class="modal-content">
                <div class="modal-header pmd-modal-bordered">
                    <button aria-hidden="true" data-dismiss="modal" class="close"type="button">×</button>
                    <h2 class="pmd-card-title-text">プレビュー</h2>
                </div>
                <p>ここに文章が表示されます</p>
            </div>
        </div>
</div>


ダイアログの表示方法は、上記リンクのpropellerkit公式ページを見ていただいた方が早いかと思います。

実際に「プレビュー」ボタンが押されると、このようなダイアログが表示されます。
f:id:haruka-i1997:20170828145804p:plain


それでは、いよいよhtmlに変換した文章をプレビューで表示していきたいと思います。

htmlは以下の通りです。
1箇所のみ変更しています。

<div tabindex="-1" class="modal fade" id="preview-dialog" aria-hidden="true">
        <div class="modal-dialog modal-lg" >
            <div class="modal-content">
                <div class="modal-header pmd-modal-bordered">
                    <button aria-hidden="true" data-dismiss="modal" class="close"type="button">×</button>
                    <h2 class="pmd-card-title-text">プレビュー</h2>
                </div>
                <p ng-bind-html="previewContent"></p>
            </div>
        </div>
</div>


Javascriptは以下の通りです。
ここも変更点は1箇所のみです。

$scope.content = "";
$scope.previewContent = "";
function preview(){
 var convertContent = $scope.content;
 /*変換処理*/
  //タイトル
   convertContent = convertContent.split('#title').join("<div style='font-size:20px;font-weight:bold;'>");
    convertContent = convertContent.split('#').join("</div>");
  //赤い文字
    convertContent = convertContent.split('%red').join("<span style='color:red;'>");
    convertContent = convertContent.split('%').join("</span>");
  //太文字
    convertContent = convertContent.split('*bold').join("<b>");
    convertContent = convertContent.split('*').join("</b>");
  //下線
    convertContent = convertContent.split('_under').join("<u>");
    convertContent = convertContent.split('_').join("</u>");

   $scope.previewContent= $sce.trustAsHtml(convertContent); 
}


htmlの変更点は

<p ng-bind-html="previewContent"></p>

のみです。
ここでjavascript側で変換したhtmlを反映します。

Javascriptは、最後の

$scope.previewContent= $sce.trustAsHtml(convertContent); 

を追加したのみです。

htmlで「ng-bind-html」を利用する際には、反映したいhtmlを
「$sce.trustAsHtml」を利用してエスケープしてあげる必要があります。
(しなければエラーが出てしまいます)

プレビューの表示結果は以下の通りです。
f:id:haruka-i1997:20170828152459p:plain

無事に表示されました。

しかし、改行や空白がきちんとされていない点が気になります。
ですので、javascriptで以下の処理を追加してみます。

$scope.content = "";
$scope.previewContent = "";
function preview(){
 var convertContent = $scope.content;

 /*改行・空白処理*/
    convertContent = convertContent.replace(/\r?\n/g, "<br>");
    convertContent = convertContent.replace(/\s+$/g,"");

 /*変換処理*/
  //タイトル
   convertContent = convertContent.split('#title').join("<div style='font-size:20px;font-weight:bold;'>");
    convertContent = convertContent.split('#').join("</div>");
  //赤い文字
    convertContent = convertContent.split('%red').join("<span style='color:red;'>");
    convertContent = convertContent.split('%').join("</span>");
  //太文字
    convertContent = convertContent.split('*bold').join("<b>");
    convertContent = convertContent.split('*').join("</b>");
  //下線
    convertContent = convertContent.split('_under').join("<u>");
    convertContent = convertContent.split('_').join("</u>");
    console.log(convertContent)

   $scope.previewContent= $sce.trustAsHtml(convertContent); 
}

改行・空白処理を追加しました。
ここではJavascriptの「replace」メソッドを利用しています。

まず1つめ

convertContent = convertContent.replace(/\r?\n/g, "<br>");

では、改行文字「/\r?\n/g」があれば改行コードに変換しています。

2つめの

convertContent = convertContent.replace(/\s+$/g,"");

では、空白文字「/\s+$/g」があれば空白に変換しています。


コンソールの表示結果は以下の通りです。

<div style='font-size:20px;font-weight:bold;margin-bottom:-10px;'>タイトルです</div><br><br><span style='color:red;'>赤い文字です</span><br><br><b>太い文字です</b><br><br><u>下線付きです</u>

改行している箇所にはしっかりと「br」タグが挿入されていることがわかります。


プレビューでの表示結果は以下の通りです。
f:id:haruka-i1997:20170828154409p:plain

先ほどとは違い、きちんと改行されていることがわかります。

以上でプレビュー機能が完成しました。



おわりに

今回は主にjavascriptのメソッドを使用し、Markdownからhtmlへの変換処理を作成しました。
一度作成してしまえば、少し面倒なhtmlも自作のMarkdown記法でスイスイ書くことができると思います。
ぜひお試しください。