追記('15/01/02):pandoc側で修正が入ったので(Issue #1842)、minifyされてないファイルでもそのうち動くようになるはずです。
好きなJavascriptの可視化パッケージをRの世界にサクッと持ち込む(htmlwidgets) - Technically, technophobic.で、「サクッと持ち込む」と言いつつサクッと持ち込めなかった理由についての話です。
地味です。あんまり面白くないです。
問題
つくったパッケージで書いたグラフが、RStudio上だとちゃんと表示されるのに、Rmarkdownだとうまく表示されない、という謎の現象が起こっていました。詳しくはhtmlwidgets
のIssueを見てください。
原因
調べたところ、どうも問題はRmarkdownでJavascriptをData URIに埋め込むときに起こっていることが分かりました。Data URIでの埋め込みについては後述するのでとりあえず省略します。
ブラウザの開発者ツールでエラーが起こってる行を見ると、文字化けしています。
RStudio上で見たときに同じ行がどうなっているかというと、、なんとUTF-8の文字が使われています。
全く知らなかったんですが、JavascriptってUTF-8の文字をふつうに使えるんですね。
D3.jsの言い分
これにつまづく人はしばしばいるらしく、D3.jsではIssueが立ってました。
d3.v3.js does not work if document encoding is not UTF8 · Issue #1195 · mbostock/d3 · GitHub
ここで提案されている解決策は3つです。
- ドキュメントの先頭あたりに
<meta charset="utf-8">
と書いてcharsetを宣言する src
タグの中で<script charset="utf-8" src="d3.js"></script>
という感じでcharsetを指定する- minifyされるとASCII文字になるので
d3.min.js
を使う
しかし、knitされたファイルを見ると、
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="generator" content="pandoc" />
ということで、ばっちり解決策1を使ってるはずです。なのになんで文字化けするんでしょう?
d3.min.jsを使うととりあえず動く。
そのあと解決策2を試してもむりで、もうだめかも、と絶望感を感じながら解決策3を試すと、なんと、動きました!!!
めでたしめでたし!
...なんですが、なんかもやもやするのでもう少し調べてみます。
Data URI scheme
さっき説明を端折りましたが、Data URI schemeとは何なんでしょう? これは、画像とかJavascript/CSSファイルをbase64でエンコードしてページに埋め込める仕組みです。
具体的には、RStudio上ではこんな感じになっているところが、
<script src="lib/d3-3.5.2/d3.js"></script>
RmarkdownをknitしたHTMLファイルだと、以下のようになっています。
<script src="data:application/x-javascript,%28function%28%29%20%7B%0A%20%20%22use%20...
これは、rmarkdown
が内部的に使っているpandoc
の--standalone
オプションを利用しています。
Data URI schemeの仕様
Data URI schemeはRFC2397によると以下のようなフォーマットです。
data:[<mediatype>][;base64],<data>
dataurl := "data:" [ mediatype ] [ ";base64" ] "," data mediatype := [ type "/" subtype ] *( ";" parameter ) data := *urlchar parameter := attribute "=" value
で、このparameter
にcharset
を指定できるんですが、気になる記述があります。
If
<mediatype>
is omitted, it defaults to text/plain;charset=US-ASCII. As a shorthand, "text/plain" can be omitted but the charset parameter supplied.
え、デフォルトがUS-ASCII
? この21世紀にそんな暴挙ありですか??
この部分が、MIMEタイプだけ指定してcharsetを指定していない場合に言及しているのかよく分からないんですが、現実、ブラウザはそういう感じ(他の場所でcharsetを指定してあっても、Data URIに書いてあるやつはASCIIとみなす)の挙動をしています。うーん。
じゃあcharset指定すればよくない?
そう思ってpandoc
のソースまで見たんですが、ここにはたしてcharsetが入れれる余地があるのかよく分かりません。。
思い余ってpandocにIssueまで立ててしまいましたが、単なる勘違いかもしれません。
結論
よく分かんないけど、とりあえずminifyされたファイル使っとけばおkなんでしょ?という流れに私が立てたIssueはなってます。若干もやっとしますが、bowerでダウンロードされるファイルにはたいがい*.min.js
があるので、まあそれで問題ないはずです。
まさかRを使っていて、ブラウザの開発者ツールを開いてRFCを読み出す日が来るとは思ってませんでした。つらい。。
次回予告
上のような話になってるんですが、Rのパッケージビルド時にGulpでconcat+uglifyしてminifyされたJSとCSSを自動生成する方法を無駄に模索中です。たぶん使う人いないと思いますけど、、ここまで来たらまとめようかなと思ったり思わなかったりしています。