« 2011年06月 | メイン | 2011年08月 »

2011年07月 アーカイブ

2011年07月02日

iOS等のブラウザにJavaScriptを送信して実行できるXiangpian(コマンドラインオプション編)

XiangpianはGUIからだけではなくコマンドラインからも起動できます。コマンドラインから起動すると、WebコンソールからだけでなくCUIからもブラウザを操作することができます。また、起動時にはオプションを指定することができます。今回は、起動時のコマンドラインオプションについて説明します。Xiangpianに関する過去のエントリーは以下のURLを参照のこと。
ここでは、最新のバージョンである0.0.5を例に説明します。コマンドラインから起動するには、ダウンロードして解凍したディレクトリにあるjarファイルを使います。せっかくexe化app化したので、本来なら同じような仕組みを準備すべきですが、現行のバージョンではそこまで対応していません。
コマンドラインオプション
それではまずヘルプを表示してみましょう。「java -cp lib -jar Xiangpian-0.0.5.jar --help」でヘルプが表示されます。
% java -cp lib -jar Xiangpian-0.0.5.jar --help
The options available are:
    [--color -c] : display color
    --command-prefix value : specify command prefix
    [--help -h] : display help
    --port -p value : specify port number
    [--stop-external -s] : stop execute external command from websocket
    --user-interface -u /^(g|c)(ui)?$/ : select user interface [g,gui,c,cui]
重要なオプションから順に解説します。
「--user-interface」「-u」は、GUIモードで起動するかCUIモードで起動するかを選択します。GUIモードは、「基本編」で説明した動作と同じです(厳密にいうとexe化・app化していないため、アイコンが表示されません)。CUIモードで起動するとコンソールでJavaScriptを入力し、他端末のブラウザ上で実行することができます。
# CUIモードで起動
% java -cp lib -jar Xiangpian-0.0.5.jar -u c
# GUIモードで起動
% java -cp lib -jar Xiangpian-0.0.5.jar --user-interface gui
「--port」「-p」では、WebSocketのポート番号を指定することができます。既定値は40320です。
# ポート番号8080で起動
% java -cp lib -jar Xiangpian-0.0.5.jar -u g --port 8080
「--stop-external」「-s」を指定すると、WebコンソールからのJavaScriptの実行ができなくなります。Webコンソールから制御ができないため、GUIモードと共に指定すると何もできなくなります(console.logからの出力は表示されます)。
# Webコンソールからのコマンド実行を抑制
% java -cp lib -jar Xiangpian-0.0.5.jar -u c -s
「--color」「-c」は、CUIモードをANSIカラーに対応させます。実行したコードと結果が色分けされるためお勧めのオプションです。環境によっては対応していません。
# 色分けされたCUIモードを起動
% java -cp lib -jar Xiangpian-0.0.5.jar -u c -c
「--command-prefix」は、CUIモードでコマンドを実行する前に必要なprefixを変更できます。詳細は次回以降のエントリーで解説しますが、JavaScriptのコードとXiangpianへの命令を区別するためのものです。既定値は「;;」です。
# コマンドのprefixをスペースに変更して起動
% java -cp lib -jar Xiangpian-0.0.5.jar -u c --command-prefix " "
「--help」「-h」はコマンドラインのヘルプを表示します。引数には対応していません。
#ヘルプを表示
% java -cp lib -jar Xiangpian-0.0.5.jar --help
コマンドラインオプション処理
コマンドラインオプションの処理にはJewelCliというライブラリを使用しています。
JewelCliはJavaのアノテーションを使ってコマンドラインオプションの定義ができ、使い方も簡単なので興味のある方は以下のURLからどうぞ。
ここまでXiangpianでのコマンドラインオプションの解説をしたので、Xiangpianの実例を見た方が理解がしやすいかもしれません。

2011年07月04日

iOS等のブラウザにJavaScriptを送信して実行できるXiangpian(CUIモード編)

これまで、Xiangpianの基本的な使い方や、詳しい設定方法、起動時のオプションの指定の仕方を説明してきました。
今回は、バージョン0.0.5を実際に操作し、CUIモードの使い方を説明します。制御される端末はiOSを用いています。

それでは、CUIモードで起動します。「java -cp lib -jar Xiangpian-0.0.5.jar -u c -c」を実行してください。ANSIカラーに対応していない端末であれば、「java -cp lib -jar Xiangpian-0.0.5.jar -u c」を実行します。
% java -cp lib -jar Xiangpian-0.0.5.jar -u c -c
2011-07-01 23:19:25.583:INFO::jetty-7.1.6.v20100715
2011-07-01 23:19:25.745:INFO::Started SelectChannelConnector@0.0.0.0:40320
  ';;' is necessary for the command as the prefix.
    ;;display [startLineNumber [length]] | display multilines stack code.
    ;;clear                              | clear multilines stack code.
    ;;execute                            | execute multilines stack code. (don't execute when parse error)
    ;;execute_force                      | execute multilines stack code by force. (execute even when parse error)
    ;;insert lineNumber insertCode       | insert code into specify point.
    ;;replace lineNumber replaceCode     | replace code as specify point.
    ;;remove lineNumber                  | remove code from specify point.
    ;;exit                               | exit application.
    ;;help                               | display help.

起動時にコマンドのヘルプが表示されます。コマンドについては後述しますが、「;;exit」を実行すると終了することは覚えておいてください。

基本編で説明したブックマークレットを実行します。
status>
open

「open」が表示され接続したことが確認できました。ここで「location」を入力しenterしてください。
% location
command>
location

result>
{
    "hostname": "www.kanasansoft.com",
    "hash": "",
    "href": "http://www.kanasansoft.com/",
    "protocol": "http:",
    "port": "",
    "host": "www.kanasansoft.com",
    "pathname": "/",
    "search": ""
}

locationの値が表示されました(JSONの解析ライブラリはJSONICを使用しています)。実際には全ての値が返ってくるのではなく、JavaScriptのJSON.stringifyの出力に対応しているものだけです。locationはプロパティですが、関数や文も実行できます。
% (function(){return "foo";})();
command>
(function(){return "foo";})();

result>
"foo"
% if(Math.random()>0.5){"foo";}else{"bar";}
command>
if(Math.random()>0.5){"foo";}else{"bar";}

result>
"foo"
% if(Math.random()>0.5){"foo";}else{"bar";}
command>
if(Math.random()>0.5){"foo";}else{"bar";}

result>
"bar"

無理矢理ワンライナーで書く必要もありません。構文解析を行なって、文が終っていないと判断すると次の入力を待ちます(構文解析のライブラリにRhinoを使っています)。
% if(Math.random()>0.5){
% "foo";
% }else{
% "bar";
% }
command>
if(Math.random()>0.5){
"foo";
}else{
"bar";
}

result>
"foo"

入力したコードは履歴に保存されています。カーソルキーの上下かcontrol+Pやcontrol+Nで履歴を辿ることができます(こちらはJLineというライブラリを使っています)。
コマンド
コマンドは補完機能に対応しています。コマンドのprefix(既定値は「;;」)を入力した後にtabを押すと、補完機能が実行されます。
% ;;      <= ここでTABを押すと一覧が表示される
;;clear           ;;display         ;;execute         
;;execute_force   ;;exit            ;;help            
;;insert          ;;remove          ;;replace         
% ;;d     <= ここでTABを押すとdisplayが補完される
編集用のコマンドについては、以下の一連の流れを見てください。

複数行の入力途中に、入力ミスに気づいた場合は入力を修正することができます。
% if(Math.random()>0.5){
% }else{
% "bar";
% ;;display
   0 : if(Math.random()>0.5){
   1 : }else{
   2 : "bar";
% ;;replace 0 if(Math.random()>0.1){
% ;;display
   0 : if(Math.random()>0.1){
   1 : }else{
   2 : "bar";

新しい行を挿入することも可能です。
% ;;display
   0 : if(Math.random()>0.1){
   1 : }else{
   2 : "bar";
% ;;insert 1 "foo";
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }else{
   3 : "bar";

特定の行を削除することもできます。
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }else{
   3 : "bar";
% ;;remove 3
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }else{

複数行入力を全て削除するコマンドもあります。
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }else{
% ;;clear
do you clear really? y/n
clear all.
% ;;display

複数行入力で編集を行なった場合、構文解析は行なわれません。このため、文が終っていても実行されません。
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }else{
% ;;replace 2 }
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }

このような場合には、次のようにすると構文解析と実行処理が走ります。
% ;;display
   0 : if(Math.random()>0.1){
   1 : "foo";
   2 : }
% ;;execute
command>
if(Math.random()>0.1){
"foo";
}

result>
"foo"

構文解析を無視して実行したい場合は以下のようにします。
% if(Math.random()>0.1){
% ;;execute_force
command>
if(Math.random()>0.1){

error>
SyntaxError: Parse error

コマンドのprefixの既定値は「;;」ですが、これはXiangpianの起動時に変更することができます。
% java -cp lib -jar Xiangpian-0.0.5.jar -u c -c --command-prefix "#"
2011-07-02 00:32:50.472:INFO::jetty-7.1.6.v20100715
2011-07-02 00:32:50.541:INFO::Started SelectChannelConnector@0.0.0.0:40320
  '#' is necessary for the command as the prefix.
    #display [startLineNumber [length]] | display multilines stack code.
    #clear                              | clear multilines stack code.
    #execute                            | execute multilines stack code. (don't execute when parse error)
    #execute_force                      | execute multilines stack code by force. (execute even when parse error)
    #insert lineNumber insertCode       | insert code into specify point.
    #replace lineNumber replaceCode     | replace code as specify point.
    #remove lineNumber                  | remove code from specify point.
    #exit                               | exit application.
    #help                               | display help.
% #help
  '#' is necessary for the command as the prefix.
    #display [startLineNumber [length]] | display multilines stack code.
    #clear                              | clear multilines stack code.
    #execute                            | execute multilines stack code. (don't execute when parse error)
    #execute_force                      | execute multilines stack code by force. (execute even when parse error)
    #insert lineNumber insertCode       | insert code into specify point.
    #replace lineNumber replaceCode     | replace code as specify point.
    #remove lineNumber                  | remove code from specify point.
    #exit                               | exit application.
    #help                               | display help.

JavaScriptの入力を邪魔しないようなprefixになるように注意してください。
ライブラリ
CUIモードで使用しているライブラリの情報源を列挙しておきます。

Emacsでの見た目そのままにソースコードをブラウザで開くコマンドを作った

せっかくなのでコマンド化してみた。
openコマンドを使っているのでMac OS X専用。
以下のコードを「.emacs」等に追記してEmacsを再起動。
(defun htmlize-and-browse ()
  (interactive)
  (defcustom
    htmlize-and-browse-directory-path temporary-file-directory
    "htmlize-and-browse-temporary-file-directory"
    :type 'string
    :group 'htmlize-and-browse)
  (setq htmlize-and-browse-buffer-file-name (concat "htmlize-and-browse-" (format-time-string "%Y%m%d%H%M%S" (current-time)) ".html"))
  (setq htmlize-and-browse-buffer-file-path (concat htmlize-and-browse-directory-path htmlize-and-browse-buffer-file-name))
  (with-current-buffer (htmlize-buffer)
    (write-file htmlize-and-browse-buffer-file-path)
    (set-buffer-modified-p nil)
    (kill-buffer htmlize-and-browse-buffer-file-name)
    (shell-command (concat "open " htmlize-and-browse-buffer-file-path))
  )
)

(defun htmlize-and-browse-by-safari ()
  (interactive)
  (defcustom
    htmlize-and-browse-directory-path temporary-file-directory
    "htmlize-and-browse-temporary-file-directory"
    :type 'string
    :group 'htmlize-and-browse)
  (setq htmlize-and-browse-buffer-file-name (concat "htmlize-and-browse-" (format-time-string "%Y%m%d%H%M%S" (current-time)) ".html"))
  (setq htmlize-and-browse-buffer-file-path (concat htmlize-and-browse-directory-path htmlize-and-browse-buffer-file-name))
  (with-current-buffer (htmlize-buffer)
    (write-file htmlize-and-browse-buffer-file-path)
    (set-buffer-modified-p nil)
    (kill-buffer htmlize-and-browse-buffer-file-name)
    (shell-command (concat "open -a safari " htmlize-and-browse-buffer-file-path))
  )
)
標準ブラウザで開くなら「M-x htmlize-and-browse」で、今回はSafariで開く必要があるので「M-x htmlize-and-browse-by-safari」を使う。

保存場所を変更したい場合は「.emacs」に以下のようなコードを追記する。
(setq htmlize-and-browse-directory-path "~/htmlize/")
Lisp初心者なのであまり綺麗なコードではないと思う。
聡明なLisperが綺麗に書き直してくれると良いなぁ...。
というか、自分で書いておいてなんだけど、これ便利だな。
参考URL
以下、参考にしたページ。

2011年07月06日

ブラウザで開いているタブが最前面かどうかを判断する

最新のブラウザの一部は、パフォーマンス向上を目的として、バックグラウンドタブのsetTimeoutとsetIntervalのタイマー間隔が最短で1000msになった。この時間差を利用すると、タブが最前面かどうかを判断することができる。以下、実証コード。
var xGround=function(listeners){
    var interval=100;
    var preTime=new Date().getTime();
    var preStatus=null;
    var timer=function(){
        var nowTime=new Date().getTime();
        var xGround=(Math.abs(nowTime-preTime-interval)<100)?"fg":"bg";
        var switchFlag=((preStatus||xGround)!==xGround);
        switch(true){
        case (xGround==="fg")&&switchFlag:
            listeners.switchForeGround&&listeners.switchForeGround();
        case (xGround==="fg"):
            listeners.foreGround&&listeners.foreGround();
            break;
        case (xGround==="bg")&&switchFlag:
            listeners.switchBackGround&&listeners.switchBackGround();
        case (xGround==="bg"):
            listeners.backGround&&listeners.backGround();
            break;
        }
        preTime=nowTime;
        preStatus=xGround;
    };
    setInterval(timer,interval);
};
var obj={
    switchForeGround:function(){console.log("switch foreground");},
    switchBackGround:function(){console.log("switch background");},
    foreGround:function(){console.log("foreground");},
    backGround:function(){console.log("background");}
};
xGround(obj);
解説は省略。

2011年07月15日

第19回HTML5とか勉強会発表資料

HTML5とか勉強会で発表したときのスライドを公開します。
5つのツールを紹介し、weinreを一押ししていますが、他のツールが劣っていると言っているのではないのでお間違えなく...。
(口頭では説明したのですが、スライドには書いていないので念のため...。)

リモートデバッグの前準備を理解してしまえば、使い方はFirebugやブラウザの開発ツールとほぼ一緒なので、是非試してみてください。

Operaのリモートデバッグ環境のDragonflyもありますし、Webkitでもリモートデバッグができるようになるらしいですが、ブラウザ側がXMLHttpRequestに対応していれば、同じ環境で作業ができるのがweinreの利点ではないでしょうか。
(ブラウザのネイティブなリモートデバッグが、共通のAPIを持つのが理想かもしれませんね。)
Google

タグ クラウド