JavaのString.formatメソッドのJavaScript版を作成してみた

JavaのString.formatメソッドと同様の機能を持つ文字列フォーマット用ライブラリのJavaScript版を作成してみました。基本的にJavaと同じ動作になるようにしていますので、Javaに慣れていて他の書式を覚えるのが面倒な方にオススメです。

format4js - Project Hosting on Google Code

JavaScript用の文字列フォーマットライブラリが欲しくて探してみたのですが、良さそうなものが見当たらなかったため自作してみることに。一通りの変換が動くようになったのでGoogle Codeで公開してみました。

ちなみに、最初は簡単に出来ると思って気軽に作り始めたのですが、真面目に仕様を調べてみると今まで使ったことがないオプションが盛り沢山で予想外に苦労しました。普段は%dと%sくらいしか使わないのですが、実はString.formatって結構高機能なんですね…。

使い方

下記のような形でJavaScriptファイル(format4js.js)を読み込んでください。

<script src="http://format4js.googlecode.com/hg/format4js.js"></script>

jQueryがある場合はjQueryのプラグインとして動作します。

//jQueryプラグインとして利用する場合:
var str = $.format('5+5=%d', 10);

またjQueryなしで単体でも利用できます。

//単体で利用する場合:
var str = mdgw.format('5+5=%d', 10);

基本的にJavaと同じですのでサポートしている書式等はJavaのドキュメント等を御覧ください。

機能制限

実装上の都合で下記の機能制限があります。

  • Localeサポートがありません (Locale.USと仮定)
  • サポートしていない変換
    • %h, %H *1
    • %tN, %TN *2
    • %tZ, %TZ *3
    • %tc, %Tc *4

おまけ

Javascript用の拡張として、下記の書式を追加してみました。変換にJSON.stringifyメソッドを利用しているため、そのままではIE8等では動作しません。別途json2.jsを読み込んでおくか、デバッグ用に限定してご利用下さい。

  • %j JSONとして出力

JavaJavaScriptで同じ変換を行って結果の確認を行っていますが、条件によっては変換結果が違うなどの問題があるかもしれません。もし問題等がありましたらお知らせ頂ければ幸いです。

*1:JavaScriptにhashCodeメソッドが存在しないため

*2:JavaScriptのDateがnanosecondsに非対応のため

*3:別途Timezone DBが必要となるため

*4:別途Timezone DBが必要となるため

MathMLを含むページを表示可能にする軽量のJavaScriptプログラムを作成してみた

MathMLに非対応のブラウザでも、MathMLを含むページを表示可能にする軽量のJavaScriptプログラムを作成してみました。

MathMLは数式をXML形式で表現するため、数式を画像として用意する場合とは異なり、細かな修正やプログラムからの操作に便利です。数式を含んだページを作成する際にはぜひとも利用したいところなのですが、対応しているブラウザを利用する、もしくはプラグインを組み込まないと表示されないのがネックになります。ただ10年くらい前はMozillaくらいしか対応ブラウザがなかったのですが、最近はHTML5の一部に含まれることもあって、ネイティブに対応しているブラウザが増えてきています。

また最近はMathJaxというライブラリがあり、これを利用するとMathMLに非対応のブラウザでも数式を表示することができます。実行結果も非常に美しい表示になり、MathML対応ライブラリの決定版という感じです。

ということでMathJaxを使えば全て解決かと思いきや、微妙な問題もあります。

  • (初回の)表示が重い
  • (そのままでは)日本語が使えない
  • (どうやら)HTMLタグを混在させることができない
  • (時々)数式が表示されない場合がある

大半が微妙な問題なので、今後のバージョンアップで解決しそうですね。

ただMathJaxでは、数式の表示にWebFontを利用しており、初回起動時にローディングがあるためどうしても表示まで時間がかかります。またWebFontに非対応のブラウザでは、各文字ごとに別れたフォント画像を1枚づつロードするため、やはりこちらも結構な重さです。特にiPhoneで3G回線を利用する場合等には若干キビシイものがあります。

美しい数式の表示にはMathJaxは素晴らしいのですが、見た目は適当で良いのでもう少し表示が軽いものも欲しくなります。そこで、JavaScript+CSSによるMathML表示用のプログラムを作成してみました。ちなみに、MathJaxはプラグイン構造になっているため、MathJaxを拡張することでも目的を達成できる可能性はあるのですが、NIHな精神により車輪の再発明をしています。

ソースコード

とりあえず基本的な部分が動くようになったのでGoogle Codeで公開してみました。ちなみに画像は一切利用せず、JavaScript+CSSのみで実現しています。またJavaScriptによる加工もほとんど行わず、基本的にHTML+CSSによるレイアウトを利用するようにしています。

使い方

MathMLを含むページの内に下記のコードを追加します。

<link rel="stylesheet" href="https://display-mathml.googlecode.com/hg/display-mathml.css?r=stable" />
<script src="https://display-mathml.googlecode.com/hg/display-mathml.js?r=stable"></script>

これだけでMathMLの部分が数式っぽく表示されるはずです。

動作デモ

下記のページで実際に試してみることができます。

  • Examples - 正常に動作する数式の例です。
  • Live Demo - MathMLタグをWordの数式エディタ等からコピペして表示を確認できます。
  • Bookmarklet - MathMLを含むページで利用することでMathML部分を数式として表示するブックマークレットです。

色々と未実装・バグがいっぱいなのですが、恐らく中学・高校くらいで出てくる数式であれば概ね表示できると思います。

最近のMicrosoft Office付属の数式エディタは、数式をMathMLとしてコピーする機能がついています。数式エディタからコピーするだけで、簡単にMathMLを利用してページ上に数式を表示できるのでぜひお試しください。

Wicketで階層構造を扱う際にHTMLとJavaコードの構造を一致させる

Scala exercise 6: Tackling the Wicket hierarchy mismatch problemという記事に触発されて、同様の手法をJavaで再現してみました。

Wicketでネストされたコンポーネントを扱う場合、HTMLテンプレートに比べてJava側は見通しが悪くなりがちだと思います。元記事ではScalaを利用した解決方法が示されているわけですが、私が理解した範囲ではポイントは下記の2点のようです。

  1. コンポーネントの階層構造と同じになるようにソースコードをインデント(構造化)する。
  2. 追加対象を常にctxという同じ変数名にすることでHTMLの構造変化があってもどこにでもコピペ移動ができる。

実際にこの手法で書かれたソースコードを見るとなかなか悪くなさそうです。ただ残念ながらScalaは今ひとつ使い方が分からないので、元記事のScala exerciseという目的を全力で無視して、Javaで同様の手法を再現してみることにしました。

どのようにJavaで再現するかですが、まずコンポーネントの階層構造をソースコードに反映させる部分は、無名インナークラスを利用することにしました。コンポーネントの階層が増えるたびにインナークラスを作成することになります。また追加対象を常に同じ変数にする部分は、せっかく無名クラスをネストさせるので、単純にthisを利用することにしました。

このような方針で適当に作ってみたところ、下記のようなコードが出来上がりました。

public MyPage
    ...
    Form form = new Form("form");
    add(new With(form) {
        {
            TextField nameField = new TextField("email");
            add(nameField);
        }
    });
    ..

対応するHTMLテンプレートはこんな感じです。*1

<form id="wicket-form" action="...">
  <input id="wicket-email" type="text" />
</form>

作成したプログラムはWithというクラス1つのみです。Javaコードでは、無名インナークラスとしてWithから派生したクラスを作成しています。Withという名前のとおり、コンストラクタに渡されたコンテナが、インナークラス内でのthisとして使われることをイメージしています。また記述の簡略化のために、インスタンスの初期化ブロックを利用しています。

WithはMarkupContainerが持つ全てのpublic methodを実装したクラスです。それらのメソッドは全て、コンストラクタに渡されたコンテナへと処理が委譲されます。この構造により、インスタンスの初期化ブロック内でaddメソッドを呼ぶと、実際にはコンストラクタに渡されたコンテナ(この例の場合はform)へと追加されることになります。

またWithはIBehaviorを実装しているため、addで追加することができます。IBehaviourは親コンテナに追加される際に、bindメソッドが呼ばれます。そこでbindメソッド内で、コンストラクタに渡されたコンテナがaddされていないようであれば、Withの親(この例の場合はWebPage)にaddしています。これにより、Withクラスの作成とコンテナの追加を1行で記述することが可能になっています。ちなみにWith自体は他に何の処理も行わないので追加しても無害(なはず)です。

ダウンロード

1ファイルだけの簡単なコードなのですが、一応Google Codeで公開しています。

 http://code.google.com/p/wicket-nest/

またJARファイルも下記からダウンロードできます。

 http://jenkins.madogiwa.org/job/wicket-nest/

Maven2

JARをダウンロードする以外に、Maven2リポジトリもあります。

もしMaven2で利用する場合は、pom.xmlに下記のリポジトリを追加して下さい。

<repositories>
  <repository>
    <id>maven.madogiwa.org</id>
    <name>madogiwa.org Repository</name>
    <url>http://maven.madogiwa.org/maven2</url>
  </repository>
</repositories>

また同様にpom.xmlに下記の依存設定を追加して下さい。

<dependency>
    <groupId>org.madogiwa</groupId>
    <artifactId>wicket-nest</artifactId>
    <version>1.0</version>
</dependency>

*1:今回の内容には不要ですが、この例では[http://d.hatena.ne.jp/mdgw/20101106/p1:title=前回作成したライブラリ wicket-altmark]を利用しています。

Wicketのコンポーネント追加先指定にHTMLのID属性を利用可能にするライブラリを作成してみた

Wicketコンポーネントを追加する場所を指定する場合、通常は該当するタグにwicket:idという属性を付与する必要があるのですが、代わりにHTMLのID(class)属性を利用可能とするライブラリを作成してみました。

Wicketコンポーネントを追加する場所を指定する場合、該当するタグにwicket:idという属性を付けます。XHTMLを利用している場合はこの方式でも良いのですが、XHTML亡き今HTML5が主流になってくると、wicket:という独自namespaceの属性は浮いた感じになってしまいます。またCSSjQueryを頻繁に使い出すと、idやclass属性を付与することが多くなるため、wicket:idを改めて付けるのも面倒になります。

このライブラリを利用すると、wicket:idを付与することなくWicketを利用することが可能となります。等の専用タグを利用しない場合は完全に普通のHTMLになりますので、Pure HTMLにこだわりがある人にはオススメです。ちなみにライブラリの動作確認はWicket 1.4.13で行っています。

使い方

利用前の下準備としては、ライブラリのJARファイルを追加した後、WicketのApplicationクラスで下記のようにMarkupParserFactoryを上書きするだけです。

protected void init() {
    ...
    getMarkupSettings().setMarkupParserFactory(new AlternativeMarkupParserFactory());
    ...
}

実際に利用する際は、HTML内のコンポーネントを追加したい場所に対して、wicket:idの代わりに wicket- で始まるIDを付けてあげます。

<!-- html template -->
<span id="wicket-username"></span>

HTMLにIDを付与することで、wicket:idを用いた場合と同様に、その場所にコンポーネントを追加することが可能です。ただし、wicket:idを用いた場合との違いとして、Java側でIDを指定する場合は wicket- を除いた部分を指定する必要があります(設定によりprefix込みにも変更可能)。

// add Label component (remove "wicket-" from id)
add(new Label("username", "Hoge Hoge"));

基本的な使い方は以上です。

class属性の利用

ID属性以外にclass属性も利用可能です。DataViewなどの繰り返し出力を伴うコンポーネントにID属性を使うと、下記のように重複したIDが出力されてしまいます。

<!-- Do not use id attribute in repeater -->
<span id="wicket-username"></span>
<span id="wicket-username"></span>
<span id="wicket-username"></span>
...

そこで、このような場合はclass属性を利用します。class属性への指定方法はID属性と同じです。

<!-- html template (in repeater) -->
<span class="wicket-username"></span>

またコンポーネントを追加する際の指定方法もまったく同じです。

add(new Label("username", "Hoge Hoge"));

カスタマイズ

IDに関する動作はAlternativeMarkupParserFactoryの引数でカスタマイズが可能です。第1引数はIDに付けるプレフィックス(デフォルトは wicket-)を指定します。第2引数はプレフィックスを除去するかどうかを指定します。デフォルトはtrue(プレフィックスを除去)です。

protected void init() {
    ...
    // プレフィックスを wi- とする。またプレフィックスは残したままとする。
    getMarkupSettings().setMarkupParserFactory(new AlternativeMarkupParserFactory("wi-", false));
    ...
}

...

<span id="wi-username"></span>

...

// 第2引数をfalseとした場合は wi- 付きで指定する。
add(new Label("wi-username", "Hoge Hoge"));

ダウンロード

ソースコードは下記で公開しています。

 http://code.google.com/p/wicket-altmark/

またJARファイルは下記からダウンロードできます。

 http://hudson.madogiwa.org/job/wicket-altmark/

Maven2

JARをダウンロードする以外に、Maven2リポジトリもあります。
もしMaven2で利用する場合は、pom.xmlに下記のリポジトリを追加して下さい。

<repositories>
  <repository>
    <id>maven.madogiwa.org</id>
    <name>madogiwa.org Repository</name>
    <url>http://maven.madogiwa.org/maven2</url>
  </repository>
</repositories>

また同様にpom.xmlに下記の依存設定を追加して下さい。

<dependency>
    <groupId>org.madogiwa</groupId>
    <artifactId>wicket-altmark</artifactId>
    <version>0.2</version>
</dependency>

とりあえず試してみた限りでは動いているのですが、真面目な用途には利用していないため問題があるかもしれません。また実装が手抜きなので、XHTMLでnamespaceを変更してると動かない場合があるはずです。その他、何かありましたらコメント欄などでお知らせ頂ければ幸いです。

追記

どうやって実現しているのかはWicket勉強会の資料をご覧下さい。

おまけ - Wicket 1.5の場合

ソースコードを見た限りですが、1.5ではユーザがMarkup操作用のフィルターを追加することが考慮されているみたいです。実際には試していないのですが、こんな感じに利用することで1.5でも動きそうです。

protected void init() {
    ...
    getMarkupSettings().setMarkupFactory(new MarkupFactory() {
        public MarkupParser newMarkupParser(final MarkupResourceStream resource) {
            MarkupParser parser = super.newMarkupParser(resource);
            parser.add(new org.madogiwa.wicket.altmark.HtmlTagIdentifier());
            return parser;
        }
    });
    ...
}

iPad(のSafari)でフリック入力を実現してみた

前回作成したWebブラウザ上で動作する日本語ソフトウェアキーボードですが、結局フリック入力も出来るようにしてみました。今回も前回と同じくiPhoneiPadでも動作します。

まずはiPadでの画面

f:id:mdgw:20101016035416p:image:w368

そしてこちらはiPhoneでの画面

f:id:mdgw:20101016035417p:image:w240

実物を試してみたい方はこちらからどうぞ。

Japanese Virtual Keyboard (flick mode)

Bookmarkletを登録することで任意のページで利用可能となります。

色々と実用性がないままですが、とりあえず当初目的であったiPadでフリック入力が実現できました。iPadでフリック入力を使ってみた感想ですが、iPhone等で慣れた人なら普通に便利に使える気がします。特に手で持って使う際には、重くて手が疲れるというのを気にしなければ、あまり違和感を感じませんでした。*1

反対にiPadを置いて使う場合には、誤操作が多くなり、少し操作し難く感じました。この点については、現状でもiPhoneよりは大きいのですが、とりあえずiPhoneと同じ程度の大きさに変更したのですが、もう少しパッドを大きくすると操作性が向上するような気がしています。

iPadでフリック入力というのは半信半疑だったのですが、調整と実装がキチンとされれば便利に使える可能性が高いと思います。ということで、いつの日にかiOSでフリック入力が実装されることを期待して待ちましょう…。

*1:後は実装がショボイことに起因する問題も置いといてですが…