作品一覧ページに「あらすじ」というリンクが追加されました。
クリックすると、あらすじが表示されます。
画面に収まらない時は、こんな感じで下にページャーが表示されます。
nehan.jsのリポジトリにpluginsというディレクトリを追加しました。
最初に追加したプラグインは以下の5つです。
先日のエントリでも紹介しましたが、font-awesomeのアイコンを短く書けるプラグイン。
<fa name="user"> <fa name="star">
emailを指定すると、gravatorのアイコンを出してくれるプラグイン。
<gravator email="sample@example.com" size="64">
いったん内容を表示しないで、クリックしたらポップアップで教えるチップを表示するプラグイン
<tip title="クリックしたら出るよ">クリックしたら表示したい内容だよ</tip>
外部のwidgetを貼付けたい時など、場合に酔っては内容をnehan.jsに組版させず、そのまま貼付けたいときがあります。
それをnehan.jsで指定する為の特別な属性が「pasted」なのですが、それをdivを使わずに短く書けるようにするタグです。
<!-- こんな風に書くのはメンドイ --> <div style="width:100px;height:100px" pasted> そのまま貼付けたい内容 </div> <!-- この方が短い --> <pasted size="100x100"> そのまま貼付けたい内容 </pasted>
脚本のように、発言者と発言をわけて書くのに使えるタグです。
画像を指定することも、発言者の名前を文字列で指定することも出来ます。
両方指定された時は画像が優先されます。
<speak name="太郎">僕は太郎です。</speak> <speak src="/path/to/jigo.png" size="64">いいえ、僕が次郎です。</speak>
表示すると例えばこんな感じのレイアウトに出来ます。
nehan.jsの読み込みの後に、使いたいプラグインのソースを読み込むだけです。
<script type="text/javascript" src="/path/to/nehan.js"></script> <!-- nehan.font-awesome.js を使う --> <script type="text/javascript" src="/path/to/nehan.font-awesome.js"></script>
ただしプラグインによっては、それ以外に読み込むものが必要な場合もあります。
その辺は、各プラグインのREADMEを参照してください。
追加したプラグインは、それぞれがnehan.jsの異なる機能を使って実現しています。
プラグインを作る際の参考にでもなれば、と思います。
nehan.jsでは、組版エンジンを扱う入り口として、主にPageStreamというものを提供しています。
ただしこれは柔軟に扱える反面、はじめての利用者にとっては直感的じゃなかったかもしれませんでした。
そこで、nehan.jsのversion5.0.3からは、より扱いやすい抽象化としてPagedElementというものが利用できるようになりました。
Nehan.createPagedElement
で作成できるのですが、イメージとしてはdocument.createElement
に近いものです。
PagedElementはDOM elementを内包しますが、通常のDOMと違って内部に複数ページを持っていて、それらを切り替えられる点が異なります。
Nehan.createPagedElement
で作成します。
var paged_element = Nehan.createPagedElement();
続いて、ページのサイズをsetStyle
でセットします。
setStyle
の第1引数は、セレクターです。
"body"がページのルートスタイルになります。
paged_element.setStyle("body", { "flow":"tb-rl", // 縦書きモード。横書きなら"lr-tb"にする。 "width":640, "height":480, "font-size":16 // fontSize(camel case)とは書けない。 });
cssのプロパティとして、camel caseが使えないことに注意してください(つまりfont-size
をfontSize
と設定することはできない)。
なおその他の要素のスタイルも、同じようにセットできます。
paged_element.setStyle(".my-header h1", { "font-size":"3em" });
ページの内容はsetContent
でセットします。
setContent
した直後に組版が始まるので、上述のsetStyle
を先に済ませておく必要があることに注意してください。
paged_element.setContent("<h1>hello, nehan.js</h1>");
paged_elementによって組版されるDOMは、getElement
で取得できます。
var element = paged_element.getElement(); // 画面上のどこかにセット document.getElementById("result").appendChild(element);
次ページの描画はsetNextPage
で、前ページの描画はsetPrevPage
です。
// 次ページボタンをクリック document.getElementById("next-button").onclick = function(){ paged_element.setNextPage(); }; // 前ページボタンをクリック document.getElementById("prev-button").onclick = function(){ paged_element.setPrevPage(); };
ページを組版している最中の状況(パーセントみたいなもの)が欲しい場合は、setContent
の第2引数にコールバックを指定します。
onProgress
とonComplete
が有効です。
paged_element.setContent("適当な内容", { onProgress : function(tree){ console.log("%d page(%d percent) is done", tree.pageNo, tree.percent); }, onComplete : function(time){ console.log("finish! %f time", time); } });
ページ数やアウトラインは、上記のonComplete
のタイミングで確定します。
なので次のように取得すると良いでしょう。
paged_element.setContent("<h1>適当な内容</h1>", { onComplete : function(time){ console.log("page_count = %d", paged_element.getPageCount()); console.log("outline element = %o", paged_element.engine.createOutlineElement()); } });
createOutlineElement
については、paged_element.engine
という内部エンジンを経由して呼んでいる点に注意してください。
またcreateOutlineElement
は、引数にコールバックオブジェクトを与えることができます。
それによって、アウトラインをクリックした時の挙動なども設定できるのですが、詳しくはnehan.jsのsection-tree-converterを参照してください。
jekyll-nehanというものを作りました( デモを見る)。
Jekyllという静的サイトジェネレータのテーマです。
このテーマを使うと、nehan.jsを使った縦書き横書きのページ送りエントリが簡単に投稿できます。
見た目はこんな感じになります。
スマホ等から見ると、こんな感じに。
投稿記事のヘッダにbook_type
を指定するだけです。
--- layout: post title: your awesome book date: 2014-06-24 book_type: vert ---
book_type
には、vert
(縦書き)とhori
(横書き)が指定できます。
j
: 次ページk
: 前ページleft
: 次ページ(vertの時), 前ページ(horiの時)right
: 前ページ(vertの時), 次ページ(horiの時)reactive programmingについての動画(語り手がハイテンション)を見つけました。
http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote-Duality
まだ前半しかみてないのですが、途中getterとsetterを「共変(covarience)」と「反変(contravarience)」という概念を使って説明している箇所があって、あんまりピンと来なかったので、少し落ち着いて考えてみました。
というわけで以下は備忘録です。
動画の人は「コーラはソーダのサブタイプ」って説明してました。
ソーダ型はコーラ型を含むので、ソーダ型で記述できた式は、ソーダの部分をコーラに置き換えても動作するはずです。
で、こういう状況を
ソーダ型 >= コーラ型
なんて書くことにします。
例えば型をもとに新しい型を作る型構築ルールRがあったとします。
で、これをソーダ型に適用したR(ソーダ型)と、コーラ型に適用したR(コーラ型)が、
R(ソーダ型)>= R(コーラ型)
という風に、元の関係を維持しているとき(つまりR(ソーダ型)の部分を、R(コーラ型)で置き換えることができるような状況になっているとき)、Rを「共変」と言います。
逆にR(コーラ型)の部分が、R(ソーダ型)で置き換え可能な状況になっている場合は「反変」と言います。
で、動画(の前半)で言ってることは、元のサブタイピングの関係に対して、getterの型構築子は共変だけど、setterの型構築子は反変になるから気をつけてね、ってことだと思います。
例えば、NumのサブタイプをIntとします。
で、Numは他にもFloatとかもサブタイプに含むものとします。
Num >= Int Num >= Float
続いて、getterを作る型構築ルールを次のように定義します。
getter(A) = () -> A
つまり、getter(A)は型Aから「何かしたらAが返る」という関数型を生み出すものとします。これをNum/Intに当てはめると
となります。
さて、プログラムの中に、getter(Num)が求められている処理があったとして、それをgetter(Int)に置き換えることはできるでしょうか。
これは問題ありません。
なぜならgetter(Num)を処理できる式は、getter(Num)の返り値となるNum型を処理できるのであり、そこがInt型を返すgetter(Int)に置き換わったとしても、問題なく処理できるはずだからです。
つまり getter(Num)な部分は、getter(Int)で置き換え可能なので、
getter(Num) >= getter(Int)
となります。
これは元のサブタイピングの関係(Num >= Int)と方向が一緒なので「それぞれの型を返す関数を作る」getter型構築子は共変(covarient)である、と言えます。
次にsetter型をつくる型構築ルールを次のように定義します。
setter(A) = A -> ()
型Aを受け取って何かをする、という型です。
つまり
このとき、setter(Num)が求められている箇所を、setter(Int)に置き換えることは出来るでしょうか。
これは出来ません。
なぜなら、Int型だけじゃなくFloat型も受け取るであろうsetter(Num)を前提に書かれた箇所を、Int型しか扱えないsetter(Int)で置き換えることはできないからです。
しかし、その逆はできるのです。
つまり、setter(Int)を受け取る部分を、setter(Num)に置き換えることは出来ます。
なぜなら、setter(Int)が受け取る型のすべて(といってもIntだけですが)を、setter(Num)は受理できるからです。
よって、型変換後の両者の対応関係は、
setter(Int) >= setter(Num)
となり、これは元のサブタイピングの関係性(Num >= Int)と方向が逆なので、「それぞれの型を受け取って何かする関数を作る」というsetter型構築子は反変(contravarient)である、と言えます。
(こんな感じの理解でいいのかな?)
nehan.jsのversion5から、セレクターのエレメント名の部分が正規表現で記述できるようになっています。
例えば、ヘッダー要素なら全てに適用したいスタイルがある時は、こんな風に書けます。
// すべてのh1〜h6 Nehan.setStyle("/h[1-6]/", { "font-weight":"bold" }); // .header以下のh1〜h6 Nehan.setStyle(".header /h[1-6]/", { "color":"#FF0000" });
h1
〜h6
のそれぞれにスタイルを書くよりは楽ですよね。
htmlタグにはヘッダータグのように、名前の中に意味を内包するタグもあるので、こうやって名前を正規表現でグルーピングできるのは便利だと思います。
特に自分専用のカスタムタグを作るときなんかは、重宝するかもしれません。
正規表現で指定できるのは、エレメント名の部分だけです。
次のように、クラス名に該当する部分を正規表現にすることは出来ません。
// こういう指定は出来ない。 Nehan.setStyle("div/¥.(?:b|strong)/", { "font-weight":"bold" }); // こういう指定なら出来る。 Nehan.setStyle("/(?:b|strong)/", { "font-weight":"bold" });
前回発表したアイコンの埋め込み特殊タグですが、こういうタグをnehan.jsでどうやって定義するのかを紹介したいと思います。
おさらいですが、先月に発表したfa
タグはこんな風に宣言します。
ハートのアイコンは、<fa name="heart">
img
タグとかhr
タグみたいに、閉じタグがありませんよね。
実はこういう閉じタグが不要なタグを定義する場合、事前にnehan.jsのパーサーに対し、その旨を教える必要があるのです。
じゃないと、通常のタグと同様に「閉じタグがあるもの」としてパースしてしまい、結果としてfa
タグに続く全てのコンテンツがfa
タグの「中身」として登録されてしまいます。
なので、こんな風に指定して、事前にfa
タグが閉じタグのないタグであることをnehan.jsに教えます。
Nehan.addSingleTagByName("fa");
後は、通常のスタイル設定と一緒で、このタグの振る舞いを定義します。
Nehan.setStyle("fa", { display:"inline", width:"1em", height:"1em", onload:function(ctx){ var markup = ctx.getMarkup(); var icon_name = markup.getAttr("name"); markup.setContent("<i class='fa fa-" + icon_name + "'></i>"); markup.setAttr("pasted", true); } });
やっていることは、
i
タグを使った書式)に書き換えるというものです(縦書き文庫では、実はもう少し色々とやっていますが、ここでは簡単の為に最低限の処理だけ書きました)。
最後のpasted属性に付いては補足が必要です。
実はこの属性、まだ公式のドキュメントに書いていないのです。
なぜかというと、運用の仕方とか命名について「いいのかな」ってモヤモヤしているからです。
もしかしたら、そのうち仕様とか命名とか変わるかもしれません。ご注意ください。
Font Awesomeのアイコンを簡単に埋め込めるタグfa
を用意しました。
name
属性にアイコン名を指定します。閉じタグは不要です。
例えばこんな感じで書くと…
ユーザー:<fa name="user"> スター:<fa name="star"> ハート:<fa name="heart"> ハート(赤):<fa name="heart" style="color:red"> ローディング:<fa name="spin spinner"> 回転する歯車:<fa name="spin cog">
こんな感じに表示されます。
詳しくはマークアップヘルプのアイコンフォントを入れるを参照してください。