2012/12/02

「関西Javaエンジニアの会スペシャル! JavaOne2012」に行ってきた

2012/12/1に、 「関西Javaエンジニアの会スペシャル! JavaOne2012」(http://connpass.com/event/1261/) に参加してきました。 関西Javaエンジニアの会のイベントに参加させていただくのは、今回が初めてですすが、 他の勉強会で登壇されている方など、ちらほら見かけました。

勉強会の趣旨はSan Fransiscoで開催された JavaOne 2012報告会ということ、 現地の様子の展開と、その中で行われた発表内容のフィードバック、発表技術の 簡単なレクチャーといったものです。

以下、特に自分が興味惹かれたものを簡単に書いてみます。

「StrutsとTomcatをやめようキャンペーン実施中です」

寺田さん(@yoshioterada)の、「Java EE プラットフォームにおける HTML5対応」にて 表題の発言がありました。

やべぇ、俺撲滅される側の人間だわ・・・。このセッションに限らず、登壇者の話や参加者の反応について、 技術と知識と意識の落差を感じることが少なくなかった。 浅学を恥じるばかり。

JavaEE 7でアップデートされる JSF 2.2では、ビューをxhtmlというものでかけて、 htmlとしてブラウザでそのまま表示できる形でモデルとのBindをバリバリかけるよ。 デザイナさんが作ったhtmlを頑張ってJSPに治すなんてナンセンスで無駄が多いでしょ、 xhtmlをデザイナとプログラマが両方かける形でやれてハッピーになるよ、 という話。 ↓みたいな感じで、htmlとしての解釈は可能な形で埋めると。


Lambdaは書きやすいだけでなく、早くなる

さくらばさん(@skrb)による「Lambdaへの道」セッションにて行われた、 Java SE8に盛り込まれる予定のLambdaの解説。

仕事でAjaxバリバリでビューを書くことがあり、Javascriptのmap関数などで使う無名関数の強力さに触れたあと、 Javaで同じことをやろうとして、匿名クラスをモリモリ記述することの面倒くささに 打ちひしがれていたので、javaのlambdaには興味津々でした。

このセッションで、シンタックスとしてはかなり簡易にかけるということがわかったが、 それ以上に、この無名関数部分を小さい粒度で書くことでCPUのマルチコアを有効に活用して 並列処理として実行されて、処理が早くなるというのが面白そう。 どうかけば、どれだけ早くなるのか? 学習コストを越えてどれほどのメリットが生まれるのか、というのが人の多い仕事で 採用するかの鍵になるかな。 検証資料を探さないと。


新Javascriptエンジン Nashorn(ナズホン?)

寺田さんの「JavaOne 2012 のアップデート」から。

Rhinoに代わるJavascriptエンジン。 Invoke dynamicsを使うことでより高速に処理できるとのこと。

Invoke dyanamicというのは、Java仮想マシンにて動的型付け言語がサポート(...?) というように聞いたが、このあたり全く理解が追いついておらず。

これまで、私は入力バリデーションを、画面の入力チェックをJavascriptで、サーバでの検証をJavaで、 としていたけど、これがJavascriptで一本化できるだろうか、とか妄想するなどした。


NetBeansによるHTML5サポート

寺田さんの「JavaOne 2012 のアップデート」から。 NetBeans(IDE)での編集を、専用プラグインを入れたChromeに動的に反映させられる機能があり、 それがHTML5に対応するとのこと。 恥ずかしながら、NetBeansを使ったことはないので、これは試してみねば、と思っている。

※ もちろん NecoBeansも知らなかった。




以下は、さらにメモからの細かい抜粋。

  • 一時期勉強会といったらMBAの展示会みたいだったけど、タブレット + キーボードの人が結構いる
  • Project SumatraでJavaからGPGPUがかけるようになる?学習コストは安くないとみるが、一部のボトルネック処理に対するアプローチにはなりうる?
  • Jigsawは開発が遅れたため、Java SE9に順延するが、他の機能はこれを待たずに計画通りアップデートに載せていくとのこと。出来てるやつはリリースしてサイクルを早めていくという方向で進めていくということか。
  • Websocketの処理が書きやすそう。Ajax - JSONIC RestServletで書いている部分の一部置き換えに使えるだろうか。DBのアップデートをpushするという応用は特に使えるシチュエーションを妄想できる。
  • (fxmlの話を受けて)デザイナさんとの協業って今まであまりやってこなかったけど、インタフェースを使いやすくするところで質を上げたいし、タブレットスマフォだとUIへ配る意識の差は使用感が大きく変わりそう。 もっと積極的に考えないといけない。自分でもある程度UI/UXの知見がいるし、協力してやっていく場合の作業フローも考えないといけない。
  • 会の最後に寺田さんご提供のプレゼント争奪ジャンケンで敗れた。JavaOne/Dukeグッズでドヤりたかった。

最後に

Javaと長く付き合うことになりそうな立場なので、 この先Javaにどんなものが出てくるかというのを人の口から聞いてみようということで参加しました。

その意味で大いに収穫ありです。 それに、外に出て他の人がやっているものを見聞きする刺激が、 忙しいなかでもオフで何かやろうという気持ちを起こしてくれることもあります。 普段書くJavaだけに、そういうところのモチベーションを維持する点でも有意義でした。

登壇者のみなさま、コーディネータのみなさま、参加者のみなさま、ありがとうございました。

2012/09/09

PATHの通っているディレクトリのファイルを全て出力するシェルスクリプト

環境

  • OS: FreeBSD 9.0 STABLE
find で type fするとか色々あるが、手っ取り早くこれで。
PATHのコロン区切り展開が sed なあたりが特に苦しまぎれだが、 スマートな解決はあるだろうか。

2012/08/21

JavaFX2を試しつつ gistを試す

Java FX2 を動かしてみたというのはもののついでで、 gistをBloggerに貼れるかどうか試したかったがためにできた記事。

環境

  • OS: Ubuntu 12.04 (64bit)
  • JDK: 1.7.0_06 64bit

Applicationクラス

アプリケーション全体の起動ポイントかつ各種制御。

FXMLファイル

画面の構成、デザイン。CSSも適用できるらしい。Flashで言うところのmxmlか。

コントローラクラス

FXMLの制御。Flashで言うところのmxmlに対するActionScriptファイルか。

その他

  • ビルド・起動時には、${JAVA_HOME}/jre/lib/rt.ja にパスを通す
  • FXMLを相対パスで指定する場合のベースディレクトリはJavaの起動ディレクトリ。リソースファイルとかと同じ。
  • "Control"って名前のクラスにしてしまっているけど、Oracleの名づけ的にはControllerですよ。
今までは、BloggerにGoogleのprettyprint.jsを入れて、preタグの特定クラスにマークアップさせていたが、 gistの方が手っ取り早い。 資産としてBloggerに依存しないし、訂正が楽なのもよい。ので、しばらくはgistを使っていってみる。

2012/07/08

jQuery MobileでGoogleMaps使う際の覚書(地図API動的ロードと地図サイズ最大化)

ライブラリ類のバージョン

  • jQuery 1.7.2
  • jQuery Mobile 1.1.0
  • Google Maps API V3 (3.8)

覚書1: Google Maps APIの動的ロード

htmlのどこいらかで、jsapiを読み込んでおく。
<div id="scriptArea">
  <script type="text/javascript" src="http://www.google.com/jsapi"></script>
</div>
地図を読み込む局面で、jspai経由でgoogle Maps API読み込みを行う。
// Maps API読み込み状態を管理するdeferredオブジェクトを作っておく。
_maps_loaded: $.Deferred()
// 地図の読み込み終了直後に行う初期化処理を登録しておく。
this._maps_loaded.done(this.initialize_map);

// 読み込み開始、完了時にDeferredを完了状態にする。
google.load('maps', '3', {
 other_params: 'sensor=false',
 callback: function(){
     that._maps_loaded.resolve();
}});

覚書2: 地図を画面いっぱいに広げる

jQuery Mobileだと地図用のdivのwidth,heightを100%にしても広がらない模様。
$('#map_canvas').css('width', $(document).width());
$('#map_canvas').css('height', $(document).height());

// ↓は実機では未検証。
// 画面回転についていくなら、orientationchangeメソッドについていけばよい(はず)。
$(window).on('orientationchange',function(e){
});




下はテスト書きした全文。適当なオブジェクトに作り始めたけど、クラス化するか、jQuery MobileのWidget化するか、かな。
var store_map_page = {
    _maps_loaded: $.Deferred(), 
    _map_options: {},
    geos: {},
        
    map_obj: {},
    
    map_info_window: {},

    _lazy_loaded: function(){
     this.geos = {
         station: new google.maps.LatLng(34.810538,135.494923),
         yamada: new google.maps.LatLng(34.812141,135.494709),
         oasis: new google.maps.LatLng(34.811155,135.494473)
     };
     var map_options = {
         zoom: 17,
         center: this.geos['station'],
         mapTypeId: google.maps.MapTypeId.ROADMAP
     };
     this.map_obj = new google.maps.Map(document.getElementById('store_map_canvas'), map_options);
     this.map_info_window = new google.maps.InfoWindow(this.map_obj);
     _map_options = map_options;
    },

    _initialize: function (){
     var that = this;
     this._maps_loaded.done(this.map_resize, this._lazy_loaded);
     google.load('maps', '3', {
      other_params: 'sensor=false',
      callback: function(){
          that._maps_loaded.resolve();
     }});
    },

    map_resize: function(){
     $('#store_map_canvas').css('width', $(document).width());
     $('#store_map_canvas').css('height', $(document).height());
    },

    orientation_changed: function(e){
     this.map_resize();
     if(e.orientation =='portrait'){
     } else {
     
     }
    }
};

$('body').on('pageshow', function(){
   store_map_page._initialize.apply(store_map_page);
      });

$(window).on('orientationchange',function(e){
   store_map_page.orientation_changed.call(store_map_page, e);
});

$(window).resize(store_map_page.map_resize);

2012/04/04

JavaScriptオブジェクト⇔JSON文字列の変換処理時間

JavaScriptオブジェクトをJSON文字列にシリアライズ、 逆にJSON文字列からJSONオブジェクトにデシリアライズするのにかかる時間の計測結果。 オブジェクトのネストはなく、型はStringのみで、かつ一回の集計のみと、 まともな計測データの体はなしていない。 Webサービスを作るときに、やり取りするJSONのサイズにかかる ブラウザの計算量負荷を考えるにあたって、大雑把にスケールをつかんでみようかという目的で計測。

環境

  • OS: Windows7 64bit
  • CPU: Core i5 650 (3.2 [GHz])
  • RAM: 12 [GB]
  • Chrome: 17.0.963.83 m
  • IE8(64bit): 8.0.7601.17514
  • jQuery: 1.7.2.min
  • jQuery.json: 2.3.min

動作

  1. Stringの配列を作成する。配列の長さは m であり、それぞれ n 文字のアルファベットからなる文字列がセットされている、
  2. 1.で作成したStringの配列をJSON文字列に変換する。JSON.stringify or jQuery.toJSONメソッドを用いる。
  3. 2.で作成したJSON文字列を、JavaScript文字列に変換する。JSON.parse or jQuery.parseJSONメソッドを用いる。
上記の3, 4の時間を計測する。 変数の値域は下記のとおり。
  • n = 1, 2, 3, ... 10
  • m = 20, 21, 22, ..., 219

結果

各シートの記述内容
  • Chrome: 17.0.963.83 m(JSON) JSON.parse, JSON.stringifyを使用
  • Chrome: 17.0.963.83 m(jQuery)  $.parseJSON, $.toJSONを使用
  • IE8_64:8.0.7601.17514(jQuery)  $.parseJSON, $.toJSONを使用
IEが思ったより時間かかりすぎて、グラフを起こす気力がない。 10文字程度のID文字列を複数件やり取りするとして、 レスポンスを5秒以下に抑えようとすると、 IEだと4万件以下、Chromeだと27,8万件以下に抑えないとまず無理なのかもしれんなー。 まぁそんだけの量をHTTPで一度に送受信ってのもちょっと考えづらいか。

テストコード


$(function(){
        function TestCondition(recordLength, recordNumber){
            this.isCalculated = false;
            this.recordLength = recordLength;
            this.recordNumber = recordNumber;
            this.spentTime = {
                constructJsObject: 0,
                serialize: 0,
                deserialize: 0
            };
        }

        TestCondition.prototype = {
            execute: function(){
                var recordString = "";
                for(var i=0; i<this.recordLength; i++){
                    recordString += "X";
                }

                var rawJsObject = {};

                var startTime = new Date();
                for(var i=0; i<this.recordNumber; i++){
                    rawJsObject[i] = new String(recordString);
                }
                this.spentTime.constructJsObject = new Date() - startTime;

                startTime = new Date();
                var serialized = $.toJSON(rawJsObject, null)
                this.spentTime.serialize = new Date() - startTime;

                startTime = new Date();
                var deserialized = $.parseJSON(serialized);
                var deserialize = new Date() - startTime;
                this.spentTime.deserialize = new Date() - startTime;

                this.isCalculated = true;
            },

            writeResult: function(){
                var resultTable = $('#resultTable');
                var resultRecord = $('<tr></tr>');
                resultRecord.append('<td>' + this.recordLength + '</td>');
                resultRecord.append('<td>' + this.recordNumber + '</td>');
                resultRecord.append('<td>' + this.spentTime.constructJsObject + '</td>');
                resultRecord.append('<td>' + this.spentTime.serialize + '</td>');
                resultRecord.append('<td>' + this.spentTime.deserialize + '</td>');
                resultTable.append(resultRecord);
            }
        };

        for(var i=1; i<11; i++){
            for(var j=0; j<20; j++){
                var test = new TestCondition(i, Math.pow(2, j));
                test.execute();
                test.writeResult();
            }
        }

   });

JSON文字列変換後の文字長ぐらい、計算出しておけばよかった。

2012/04/02

svn:externalsをファイル単位・フォルダ単位で設定する

Subversion(ver.1.6以上)であるディレクトリ内に別のディレクトリの内容をミラーリングする、
もしくは特定のファイルをミラーリングするために、svn:externalsを使う。

たとえば、

Subversionのリポジトリ上で複数のプロジェクトを管理している。
後発のプロジェクトBが、先発のプロジェクトAと同一のリソースを常に配置したい。
そしてそのリソースはどちらのプロジェクトでも更新するし、
その反映は常に両方のプロジェクトに対して行われる。

というような状態を作りたい場合。
(そんなシチュエーション、開発の初期フェーズしか通用しないと思うけど。)

ディレクトリの参照設定


[例]

http://localhost/svn/projA/resources

を常にprojBからも参照する。

http://localhost/svn/projB/resources


#参照設定 作成
~projB$ svn propset svn:externals 'resources http://localhost/svn/projA/resources' .
property 'svn:externals' set on '.'
~projB$ svn up
Fetching external item into 'resources'
A    foo
A    bar
Updated external to revision 2.
#参照設定 削除
~projB$ svn propdel svn:externals ./resources

特定の1ファイルの参照設定

※ Subversion 1.6以降に限る。

※ 参照関係を張ることができるのは、同一リポジトリ同士に限る

subversion.apache.org subversion1.6のチェンジログ中、svn:externalsについて記述した項。

svn:forum "svn:externals" 他リポジトリへのファイル参照設定できないんだけど、というフォーラム記事。


[例]

/repo/trunk/libA/resources/shared_file.txt



/repo/trunk/libB/resources/

にから参照設定する。


libB/resources$ svn propset svn:externals '^/repo/trunk/libA/resources/shared_file.txt shared_copy.txt' .
libB/resources$ svn up
libB/resources/shared_copy.txtを編集してコミットすれば、libA/resources/shared_file.txt更新時に反映される。

逆に、libA/resrouces/shared_file.txtを編集してコミットすれば、libB/resources/shared_copy.txt更新により反映される。

2012/03/22

[jQuery]テキストボックスへの「文字入力」の検知

  1. changeイベントを拾う…文字入力ではなくテキストボックスの入力終了でファイアするイベント。ざっくり表現すると、フォーカスアウト時。
  2. キー入力イベント(keypress, keyup, keydown)を拾う…キーボード押下時にファイアするイベント。テキストボックスに文字が入る前を取れる。
後者は、ブラウザ間の差が激しい。差については、下記が詳しい。
JavaScript Madness: Keyboard Events
キーボードからの、タブやカーソル移動、消去などを除いた「文字入力」だけを拾う手段として 下記の形で書いてみた。 ただし、ペーストや日本語変換入力などは検知できない。
// keypressイベントで発火させた後のコード
// @param event {Object} eventObject
// @return {Boolean} true:「文字入力」は行われていない、false:「文字入力」が行われた。


// キーコードの取得
var code = event.which ? event.which : event.keyCode ? event.keyCode : event.charCode ? event.charCode : 0 ;
//
if( (navigator.userAgent.indexOf('Gecko')) || (navigator.userAgent.indexOf('Presto')) ){
    // Firefox and Opera send a code of special key whe user press it.
    // below statements avoid prevented some codes sended.(arrows, delete, home, end, backspace, tab)
    if( (code==37) || (code==38) ||(code==39) ||(code==40) ||(code==46) ||(code==36) ||(code==35) ||(code==8) ||(code==9) ){
      return true;
    }
}
if( (!code) || (code < 32)) ) {
  event.preventDefault();
  return false;
}

日本語変換を検知するのは無理か。 Enter確定やスペース確定を拾うとそれっぽい気がするしたけど でも、普通目的のEnterと変換のためのEnterを分離しきれる気がしない。 あと、日本語IMEをデフォルトから変更されたりしたらお手上げじゃない?

ペーストについては、pasteイベント拾いそうなものだが、 こいつは入力確定前でかつ、何が入力されようとしているかは検知できない。 なにやら、Flashを埋め込んで連携することで可能になるようだが。 まぁ、ブラウザでWeb閲覧するごときでクリップボードの内容なんか引っこ抜かれちゃ困る。 世の中に何人かはパスワード入力でコピペってる人がいるんじゃない?

理想的には、「ペースト操作が完了してブラウザが文字列受取」 ~ 「文字列をテキストボックスに描画」の間がとれればと思うけど、 それが可能な仕組みになってるのかどうかすらわからん。

2012/03/20

[jQuery.inplim]inputの入力文字制限

入力文字種制限jQueryプラグインを作りかけている。 日本語やその他2ストローク入力言語に対応できればなーと思っているけど、 発生させるキーコードのブラウザ間の違いがあるので、そもそもに原理的に不可能なのか? と思いつつ、(Windows IMEで言うところの)直接入力だけ作成。
github:jquery.inplim.js
引数の名前やら中の制御やらについては、いろいろ潜んでいるので余暇でメンテしていきたい。 なんたってテストコードが入ってない体たらく。 jQueryプラグイン作成、githubの使用、gitなど慣れないもの満載だったので、徐々に覚えていきたいところ。
$('#numeric').inplim({regexStr:'[0-9]', jp:true});
$('#alphabet').inplim({regexStr:'[a-zA-Z]', jp:true});
$('#alphanumeric').inplim({regexStr:'[a-zA-Z0-9]', jp:true});
$('#mail').inplim({regexStr:'[a-zA-Z0-9-_\.@]', jp:true});

2012/03/04

Windowsから複数の端末を操作する環境(Cygwin + Cygterm + Putty + GNU Screen)



ホストマシンはWindowsだけど、リモートのLinux上で作業するためのリモートシェル環境。
XWindowはまだ未考慮。まぁOracleインストールするときに慌ててXmingw立てるぐらいなもので、あまり頻度がないからまじめに整備する気が起きない。

今回の環境
ホストOS:  Windows7 x64
リモートOS:  CentOS6.4

達成したい機能

・Windowsからsshでlinuxにリモートログインして、zshで作業。
・ホストOSを一度シャットダウンしても元の作業状態に戻れる。
・画面複数分割 
 複数台に入って同時並行で作業したい、emacsで手順を参照したりメモしたりしたいときのため。
 複数Windowを立ち上げる形式は、個人的に迷子になりがちなので回避。



使用するもの

[Windows]
  1. cygwin  1.7.1 http://www.cygwin.com/ 
  2. putty-gdi  http://ice.hotmint.com/putty/  ターミナルエミュレータ。個人的に手に馴染んではいるが、今回の構成での必然性はない。
  3. cygterm  http://www.dd.iij4u.or.jp/~nsym/cygwin/cygterm/  telentリスナーを空けたcygwinを立ち上げて、さらにこれに接続した状態のターミナルエミュレータを起動する。
[CentOS]
  1. gnu screen  4.1.0  http://www.gnu.org/software/screen/  画面の分割、作業状態の保存・復元(アタッチ・デタッチ)。こいつはゲストOS側に用意。

手順
[Windows]
1. cygwin
何も考えずに最新(Cygwin1.7)を入れる。

導入パッケージは下記を除いて随意に。
cygtermのビルドに必要なもの。

・tar
・make
・gcc

2. putty
GDIで描画してくれるバージョンを突っ込む。
http://ice.hotmint.com/putty/
Windows x64で使うので、putty/x64の*.exeで、putty/の実行ファイルを上書きする。

続いてcygwin実行時に使用するセッションを作成。
セッション名は"cygterm"で。

その他設定項目は、以下の形で設定。
  • セッション>ホスト名: 127.0.0.1
  • セッション>接続タイプ: Telnet
  • 端末>ローカルエコー: 強制的にオフ
  • 端末>ローカルライン編集: 強制的にオフ
  • ウィンドウ>変換>文字セット: UTF-8
  • 接続>データ>端末タイプを表す文字列 xterm

3. cygterm
http://www.dd.iij4u.or.jp/~nsym/cygwin/cygterm/
tarとgccとmakeが必要。全部cygwinに入ってるからよしなに。 からダウンロード、解凍、make, make install。
tar xvfz cygterm107.tgz
cd cygterm
make
#今回は、cygterm.exeとcygterm.cfgを手動でcygwin/binに移動した。
cygterm.cfgの内容
TERM = {puttyをおいた場所}\putty.exe -load "cygterm" telnet://%s:%d/
TERM_TYPE = xterm
PORT_START = 20000
PORT_RANGE = 40
SHELL = /bin/zsh -l -i
ENV_1 = MAKE_MODE=unix
ENV_2 = HOME=/home/{ユーザ名}


[CentOS]
# この辺は事前状態によりけり
yum install git
yum install autoconf
yum install ncurses-devel
yum install texinfo
git clone git://git.savannah.gnu.org/screen.git
cd screen/src
autoconf
autoheader
./configure --prefix=/usr/local --enable-colors256
make
make install
vim ~/.screenrc

attrcolor b ".I"
term xterm
escape ^Zz
zombie ^[
altscreen on
shell /bin/zsh
defscrollback 5000
autodetach on
startup_message off
logfile "$HOME/.screen/screen-%Y%m%d-%n.log"
caption always "%{= Rk} %?%F%{b kr}%? %?%h%:%t (screen #%n)%? %{-}"
hardstatus alwayslastline "%{= Gk} %-Lw%40L>%{b kg} %n %t %{-}%+Lw%-040=%{b km} %l %{.b} %Y/%m/%d %C:%s %A "
sorendition "= Rk"


※アレー、最新ソースなのにCtrl-Z Vで縦分割でないよでないよー
ってやってたらCtrl-Z | にバインドされてた。恥ずかしい。

[ついで]
puttyの英文はConsolasにしておきたい。
けど、日本語がつぶれるのも困る場合。
ここでは、Consolasの日本を表示をOsaka-等幅にリンクさせる。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink
新規>複数行文字列値> "Consolas"
値>665ttc2.ttc,Osaka-等幅

まだ日本語が汚いのでどうにかしたいところ。

JavaScriptのモジュールファイル群の読み込み管理をどうしよう。(2)

全体で共通的に使うモジュールではなく、htmlごとのイベントハンドラ等の初期化スクリプトをどうするか。

前提条件
  • 画面全体は遷移しない。htmlを非同期ロードして一部のdivエリアの置き換えを行う。
    便宜上最初に読み込まれるhtmlを親画面、非同期ロードされる側を子画面と呼ぶ。
  • 親画面は、子画面の初期化に必要なものが何か知らない。どのhtmlを呼ぶかは知らない。
  • 親画面1つに対して、子画面を複数ロードしない。
ファイル構成
  • test.html 親画面
  • test.js 親画面初期化スクリプト
  • screenManager.js 子画面置き換え処理スクリプト
  • subscreen
    • subscreen.html 子画面
    • subscreen.js 子画面初期化スクリプト

概要
  • 子画面は必要とするjavascriptファイルのURLを値に持つhiddenフィールドを持つ
  • 子画面置き換え処理スクリプトは、上記のhiddenフィールド値を参照して、javascriptファイルを取得する
イケてない
  • javascriptファイルのURLを相対パスで書く場合、その基準位置は子画面htmlではない。
  • 子画面初期化スクリプトが、遷移の度にロードされる。
  • CommonJSとかClosureを使えよ。

親画面(test.html)
<html>
  <head>
  </head>
  <body>
    <div class="subScreenArea">
      <p>initial contents</p>
    </div>
    <input type="button" id="changeScreen" value="screenChange" /> <br>
    <input type="text" id="loginId" />
      <input type="button" id="completing" value="completing" />
 <div id="scriptDeclaration">
   <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
   <script src="module.js"></script>
   <script src="test.js"></script>
 </div>
  </body>
</html>

親画面初期化スクリプト(test.js)
var mymodule = mymodule || {};
mymodule.require('./screenManager.js');
mymodule.require('./completed.js');
$(function(){
    $('#completing').click(function(){
 $('#loginId').val(mymodule.compl.getCompletedScript());
    });
    $('#changeScreen').click(function(){
 mymodule.scr.change('subscreen/subscreen.html');
    });
});

子画面(subscreen/subscreen.html)
<html>
  <head>
  </head>
  <body>
    <div class="subscreenArea">
      <input type="text" id="subText">
      <input type="button" id="subButton" value="Push">
      <input type="hidden" class="requireScript" value="./subscreen/subscreen.js">
    </div>
  </body>
</html>
子画面初期化スクリプト(subscreen/subscreen.js)
$(function(){
    console.log('require script loaded.');
    $('#subButton').click(function(){
 $('#subText').val('clicked');
    });
});

子画面取得スクリプト(screenManager.js)
function ScreenManager(){
    console.log('ScreenManager is loaded.');
    this.screenClass = 'subScreenArea';
}

ScreenManager.prototype.change = function(url){
    $('.' + this.screenClass).load(url + ' .' + this.screenClass, function(){
           $.each($(this).find('input.requireScript'), function(){
        console.log('loading ' + $(this).val());
        $.getScript($(this).val());
           });
       });
};

var mymodule = mymodule || {};
mymodule.scr = new ScreenManager();

2012/03/03

JavaScriptのモジュールファイル群の読み込み管理をどうしよう。

ある程度考えがまとまってから、アウトプットに起こすべしと考えていたけど、
そんなことしたら何もかけないのでとりあえず書いてみる方向に転換してみる。

ディレクトリ構成
  • test.html テスト用画面
  • test.js テスト用画面の初期化スクリプト
  • module.js モジュール管理
  • completion.js 管理されるモジュール

概要
  • 指定したURLのjsを読み込む
  • 読み込んだURLを記憶して同URLのjsの読み込みを抑制する
課題
  • URLベースの指定なので相対でURL文字列自体がことなると意味なし
  • 読み込みミスった場合のハンドリングがない
  • アンロードはどうしたらいいの?
  • 名前空間衝突への考慮

URLベースの指定について
以下の選択肢で妄想。
1. モジュールごとに識別名を割り振り、それで管理。どこかでモジュール名を集中管理することになるのでやりたくない。

2. URL指定させずに、ファイル名指定させる。ファイルの存在位置は、表示htmlのURLからの相対か、特定のディレクトリ群で決め打ちする。


test.html
<html>
<head>
</head>
<body>
<input type="text" id="loginId">
<input type="button" id="completing" value="completing">
<div id="scriptDeclaration">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="module.js"></script>
<script src="test.js"></script>
</div>
</body>
</html>


module.js
var console = console || {};
console.log = console.log || function(data){alert(data);};


function ModuleLoader(){
 console.log('construtor worked.');
 this.loadedModules = [];
}

ModuleLoader.prototype.require = function(url) {
    var that = this;
    console.log(url + ' is loading...');
    if($.inArray(url, this.loadedModules) == -1){
        $.getScript(url, function(data, status){
            if(status === 'success'){
         that.loadedModules.push(url);
     } else {
                console.log(status);
            }
 });
    } else {
        console.log(url + ' is already loaded.');
    }
}

var mymodule = mymodule || new ModuleLoader();

test.js
var mymodule = mymodule || {};
mymodule.require('./completed.js');
mymodule.require('./completed.js');
mymodule.require('./completed.js');
$(function(){
    $('#completing').click(function(){
 $('#loginId').val(mymodule.compl.getCompletedScript());
    });
});

test.js
function Completion(){
    console.log('Completion loaded')
}
Completion.prototype.getCompletedScript = function(){
    return 'completed';
}

var mymodule = mymodule || {};
mymodule.compl = new Completion();

2012/01/22

hashchangeイベントをjQueryUIのtabより発生させて、DOMを非同期に切り替える

ファイル構成

  • js
    • south-street (jQueryUIのテーマファイル群)
    • index.js
    • exvalidation.js (exValidation プラグイン http://5509.me/log/exvalidation)
    • jquery.ba-hashchange.min.js (http://benalman.com/projects/jquery-hashchange-plugin/)
  • css
  • exValidation.css
  • first.html
  • first.js
  • fourth.html
  • index.html
  • second.html
  • third.html

機能

index.html上にdiv.contentsFrame要素を定義し、jQuery UIのタブ押下に応じて
内部のdiv.contentsを動的に切り替える。

コンテンツの切り替え処理はContentsManagerクラスが担う。ContentsManagerはDOMの摩り替えを行うが、hashchange自体は意識していない。hashchangeにかかる処理はhashchangeイベント内のみで処理をする。

div.contentsの中身は、リンク先のファイルからjQuery.loadで取得する。
遷移に際してはhashchangeイベントを用いて、history.back, forwardに対応する。

ファイルの取得成功時には、リンク先ファイル上の同名jsファイルを読み込む。


考慮不十分事項

エラーハンドリングがない

jsの名前空間がどうなっているのか分からない。

exValidationのエラーチップ初期化が#formError決めうち。

headにscriptをappendすることの危険性がよく分かってない。

URIはUItabがデフォルトでつけるハッシュURIのまま。

GETのためのクエリは考慮できていない。


処理の流れ

  1. 初期ページindex.jsでの初期化処理
  2. hashchangeイベントをトラップするためにjquery.hashchangeプラグインを導入し、初期化処理をする。
  3. UI Tabが生成するハッシュリンクを無視し、html上で指定したhref属性へのリクエストを発生させる。かつUI Tabの遷移先をhash化するようUI Tabのselectイベントを設定する。
  4. 上記によりUI Tabのリンク押下により '#' + href属性のhashchangeイベントを発火させる。
  5. hashchangeイベントの発火に呼応して処理されるコールバックに対して、ハッシュ化されたuriからハッシュを削除し、元々hrefに指定されていたリクエストからdiv.contensElementの内容を非同期ロードする
  6. 上記ロード完了に伴い、既存contentsの削除と新規contentsのappendを行う。ノリでfadeout/fadeinしているがそこはどうでもいい。
  7. ロード成功時に、リクエストしたurlをhtmlと仮定し、同パス上の同ファイル名のjsを読み込む。このときのscriptタグに特定の非同期読み込みされたスクリプトである旨を示すクラス名を付与する。
    (#/hoge/first.htmlへのリクエスト発生時にhoge/index.jsを読み込むscript#contentsScriptタグをheadにappendする。)
    jQuery.getScript(jsUri);する方がハンドリングがかけたりしてまだマシ。(追記: 2012/01/23)
  8. 上記処理によりjQueryのreadyイベントが発火する。(first.jsであればinputに対するexValidationの初期化が行われる。)
  9. 前回読み込んだscriptタグを削除する。(sciprt#contentsScriptをremoveする)
  10. 以上の処理によりfirst.htmlに単独で読み込んだ場合と同等の初期化が完了したコンテンツが表示され(ているといいな)
index.html
<div id="contentNavigation">
    <ol>
      <li><a href="#">HOME</a></li>
      <li><a href="index.html">Index</a></li>
      <li><a href="first.html">First</a></li>
      <li><a href="second.html">Second</a></li>
      <li><a href="third.html">Third</a></li>
      <li><a href="fourth.html">Fourth</a></li>
    </ol>
    <div id="contentsFrame" style="height:500px;">
      <div class="contents">
 <div class="contentsElement">
   <p>this is a index.html</p>
   <p>http://fdays.blogspot.com/2011/01/jquery-ui-tabs.html</p>
   <p>now</p>
 </div>
      </div>
    </div>
  </div>

first.html
<div id="contentsFrame">
      <div class="contents">
 <div class="contentsElement">
   <form id='loginForm'>
   <p id='first'>this is a first.html</p>
   <div>
   <label for='name'>Name:</label>
   <input id='name' type='text'></input>
   </div>
   <div>
   <label for='password'>Pass:</label>
   <input id='password' type='password'></input>
   </div>
   <input id='regist' type='submit' value='Regist' class='button'></input>
   </form>
   <div id='registedName'></div>
 </div>
      </div>
    </div>

first.js
$(function(){
 // 検証処理追加
 $('Form#loginForm').exValidation({
  rules:
  {
      name:'chkrequired',
      password:'chkrequired chkmin5'
  },
  errTipCloseLabel:'x',
  errInsertPos: 'body',
  customSubmit:function(){
      var registedName = $('#name').val();
      var registedPassword = $('#password').val();
      var appendedHtml = '
' + registedName + ':' + registedPassword + '
'; $('#registedName').append(appendedHtml); } }); });

index.js
// ContentsManagerClass
var ContentsManager = function(){
    this.contentsFrameId = 'contentsFrame';
    this.contentsClassName = 'contents';
    this.contentsElementClassName = 'contentsElement';
    this.contentsScriptClassName = 'contentsScript';
};
ContentsManager.prototype.preLoadInitialize = function(){
    // exValidation初期化処理
    $('.formError').remove();
};
ContentsManager.prototype.contentsReplace = function(uri){
    var contentsManager = this;
    if(uri){
 var contentsTemplateHtml = '
'; $('#' + contentsManager.contentsFrameId).append(contentsTemplateHtml); var newContents = $('.' + contentsManager.contentsClassName + ':last-child'); newContents.load(uri + ' .' + contentsManager.contentsElementClassName ,function(){ var oldContents = $('.' + contentsManager.contentsClassName + ':first-child'); oldContents.animate({opacity:0.0},500,function(){ contentsManager.preLoadInitialize(); contentsManager.unloadScript(); oldContents.remove(); contentsManager.loadScript(uri); newContents.animate({opacity:1.0},500); }); }); } }; ContentsManager.prototype.loadScript = function(htmlUri){ var jsUri = htmlUri.replace(/html$/g, 'js'); var className = htmlUri.replace(/\//g, '_'); //var scriptHtml = ''; //$('head').append(scriptHtml); jQuery.getScript(jsUri); }; ContentsManager.prototype.unloadScript = function(){ $('.' + this.contentsScriptClassName).remove(); }; var contentsManager = new ContentsManager(); //---------------------------------------------- $(function(){ $('#contentNavigation').tabs({ select: function(event, ui) { var url = "#/" + $.data(ui.tab, "load.tabs"); if (url) { location.href = url; return false; } return true; } }); $(window).hashchange(function() { var uriWithoutHash = location.hash.replace('#', ''); contentsManager.contentsReplace(uriWithoutHash); }); $(window).hashchange(); });

2012/01/09

XS35GT V2にCentOS6.2をUSBインストールしてメディアサーバにする(2)


2.5. DLNAサーバをインストールする

a. mediatombを含むリポジトリ、
RPM foregeを yumの対象に追加
http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.i686.rpm
をダウンロードしておく
# rpm --import http://apt.sw.be/RPM-GPG-KEY.dag.txt
# rpm -i rpmforge-release-0.5.2-2.el6.rf.i686
# yum install mediatomb

b. meidatom設定ファイルよりネットワーク経由アクセスを許可
# vim /etc/sysconfig/mediatomb
MT_INTERFACE="ra0"
// ifconfigあたりで使用するネットワークを選ぶ

c. 一旦起動し、初期設定ファイルを生成させる。
# /etc/init.d/mediatomb start
# /etc/init.d/mediatomb stop
# vim /etc/mediatomb/config.xml

d. mediatombの動作設定を行う。
// sqlite3ではなくmysqlを使う
      <sqlite3 enabled="no">
        …
      </sqlite3>
      <mysql enabled="yes">
      </mysql>
// PS3からの接続に対応
    <protocolInfo extend="yes"/><!-- For PS3 support change to "yes" -->
    <commoystem-charset>UTF-8</filesystem-charset>
    <metadata-charset>CP932</metadata-charset> 
  
// 対応メディアをいろいろ追加
    <mappings>
      <extension-mimetype ignore-unknown="no">
        <map from="jpg" to="image/jpeg"/>
        <map from="jpeg" to="image/jpeg"/>
        <map from="gif" to="image/gif"/>
        <map from="png" to="image/png"/>
        <map from="m4v" to="video/mp4"/>
        <map from="mp4" to="video/mp4"/>
        <map from="mpg" to="video/mpeg"/>
        <map from="mpeg" to="video/mpeg"/>

        <!-- Uncomment the line below for PS3 divx support -->
        <map from="avi" to="video/divx"/> 

e. FWを通過するようにする(iptables)
# iptables -A INPUT -p udp --dport 1900 -j ACCEPT
# iptables -A INPUT -p tcp --dport 50500 -j ACCEPT
# service iptables save
# service iptables restart

とりあえずこれで、iPadからの動作を確認。
iPad側のDLNAクライアントにはAirPlayを使っています。
今のところ動作は良好。

これで、やかましい&音量消費のでかいPCを常時起動する必要はなく、
かつ自宅にあるすべての端末でメディアにアクセス可能になった。

その必要があるかと言われれば、まぁ微妙なところではあるが。

2012/01/07

XS35GT V2にCentOS6.2をUSBインストールしてメディアサーバにする(1)

XS35GT V2(http://www.shuttle-japan.jp/barebone/slim/xs35gt-v2)を衝動買いしてしまったので。
起動してみると非常に静かなので、
やかましいデスクトップは休みに気合入れた作業をするときだけにして、
XS35を常時起動の普段使いにしようかと。

小型静音って、ステキやん。

デスクトップもSSD化したところなんだけど、それはそれ。


0. 環境
作業マシン: Windows7(64bit) SP1

1. 構成
ハードウェア
・XS35GT V2(http://www.shuttle-japan.jp/old/old-barebone/xs35)
 - SSD 64GB
 - SO-DIMM 4GB
・WLI-UC-GNHP(無線子機)
ソフトウェア
・CentOS 6.2 (CentOS-6.2-i386-bin-DVD1.iso)
その他使用したもの
・USBフラッシュメモリ (インストールメディアとして使用)
・Syslinux 4.04 (http://www.kernel.org/pub/linux/utils/boot/syslinux/)


2. 手順概要
 2.1. USBメモリのFAT32フォーマット
 2.2. USBメモリのインストールメディア化
 2.3. CentOSインストール
 2.4. USB無線子機の認識

/**** 本ポストは↑まで ***/
/**** ↓は残りの予定 ***/
 2.5. DLNAサーバインストール

3. 使用状況
 3.1. iPhoneからの使用
 3.2. iPadからの使用
 3.3. PS3からの使用

//--------------------------------------------------------------//

2. 手順概要
2.1. USBメモリのFAT32化
syslinux導入のため、USBをFAT32でフォーマット。















 2.2. USBメモリのインストールメディア化

a. CentOS-6.2-i386-bin-DVD1.isoをそのままUSBメモリのルートディレクトリにコピー。


b. CentOS-6.2-i386-bin-DVD1.iso内のisolinuxディレクトリ直下のすべてのファイルを、
USBメモリのルートディレクトリにコピー。
上記ディレクトリ内の"isolinux.cfg"を"syslinux.cfg"にリネーム。

c. 続けてisolinuxを除くすべてのディレクトリ・ファイルをコピー。


d. syslinuxアーカイブ内のwin64ディレクトリに移動し、
 syslinux -maf j:
を実行。(※ USBメモリは j:ドライブとして認識されています)


















 2.3. CentOSインストール
BIOSからUSBからブートするよう設定。
あとはいつもどおりのCentOSインストーラなので
流れでお願いします。

# CentOS5系で記述されている他記事では
デフォルトのgrubローダインストーラ先がUSBメモリ上となることでの
混乱について触れられています。
今回試行したCentOS6.2でも明示的にローダの位置としてSSDを指定しています。

 2.4. USB無線子機の認識
当方は裸のD25HWしかないのでこれをwifiモードで起動し、
Linuxからwifi接続する。

# e-mobile端末をUSB直差しで使うモジュール化何かが入っているようだがうまく機能させられなかった。
D25HWをUSBで直差しすると、
"モバイルエリアネットワーク"として認識される。
さらに"ネットワーク接続"ウィザードから"e-mobile"を選択することで、
APSなどが設定された接続情報ができあがるが、
試みに接続してもすぐ遮断される。何か設定値が違うのか。


a. ドライバダウンロード

http://www.ralinktech.com/en/04_support/support.php?sn=501
よりドライバ(RT8070 /RT3070 /RT3370 /RT5370 /RT5372 USB)をDL

解凍し
# vim os/linux/config.mk
の2値を変更

HAS_WPA_SUPPLICANT=y
HAS_NATIVE_WPA_SUPPLICANT_SUPPORT=y

# vim common/rtusb_dev_id.c
の下記1行を追加
#ifdef RT3070
{USB_DEVICE(0x0411,0x0158)}, //追加行
引数は下記コマンドで確認しておくとなおよし
# lsusb

あとはいつものメイキングでお願いします。
# make
# make install
# insmod rt5370sta.ko
これで無線LANとしてアダプタとして認識される。

ref.
CentOS - USBデバイスからインストール!- mk-mode BLOG
http://www.mk-mode.com/wordpress/2010/01/11002001/
Ubuntu で Buffalo の WLI-UC-GNHP を使う - 試したことのメモ
http://ossan78.blog9.fc2.com/blog-entry-43.html