2009年1月7日水曜日

Ubiquity の カスタムコマンドの作り方

ここでバージョンアップしてると見かけたので、久々にインストールしてみた。
プロファイル作り直して以来入れてなかったけど、慣れるとかなり楽なんだろうなぁ。

ついでに今まで敬遠していたカスタムコマンドの作成について調べてみた。

作るんじゃなくて欲しい!って人は こちら で探すと良いかも
こんなサービスもあるみたい

○●○●○●○コマンド作成の基本0 Command Editor の開き方○●○●○●○
Ctrl+Space の後で [ command-editor] と打つか、[ help ] と打って開いた画面の「Command Editor」をクリックする。

○●○●○●○コマンド作成の基本1○●○●○●○


もっともシンプルな function でコマンドを定義する方法
コマンド名の頭に 「cmd_」を付けて関数を定義してあげるだけ。

function cmd_hello_world() {
displayMessage( "Hello, World!");
}

これで [ hello_world ] とコマンドが作れる。

◇◆◇◆◇◆◇トピックス1!◇◆◇◆◇◆◇
displayMessage は 画面右下に通知バルーンを出すメソッド。


○●○●○●○コマンド作成の基本2○●○●○●○

CreateCommand を使ってコマンドを定義する方法。
CmdUtils.CreateCommand()に必要なものを定義したオブジェクトを渡してあげる。

CmdUtils.CreateCommand({
name: "name",
preview : function(pblock){
pblock.innerHTML = 'ぷれびゅー';
},
execute: function() {
displayMessage( 'じっこう!' );
}
})

渡されるオブジェクトで動作に関する定義は、name, preview, execute の三つ。

name にはコマンド名を入れてあげる、ここで設定した名前でコマンドを利用できる。
execute にはコマンド確定した際の処理を記載する。
preview にはコマンド入力した際にプレビュー欄に出すものを定義する。引数の書き換えとコマンドの選択時に実行される。

この状態では引数は最小限。preview の場合に第一引数にプレビュー部分の HTMLElement が入るのみ。

◇◆◇◆◇◆◇トピックス2!◇◆◇◆◇◆◇

javascript で書く部分は jQuery が使える。
ただし、$ には jQuery オブジェクトは入っていないので、jQuery('xxxxx')などとしなくてはならない。


○●○●○●○コマンド作成の基本3 引数を渡したい○●○●○●○

CmdUtils.CreateCommand({
name: "name",
takes : {args:noun_arb_text},
preview : function(pblock, args){
pblock.innerHTML = 'ぷれびゅー'+args.text;
},
execute: function(args) {
displayMessage( 'じっこう!'+args.text );
}
});

先ほどの 基本2のオブジェクトに takes : {引数名 : 指定型} を追加することで、引数を渡せるようになる。

引数名は コマンド入力時に name (arg) のように仮で表示されるので、関連したものを入れたほうがいい。
型は既に幾つか定義されているが、自分で作成も可能。

noun_arb_text テキスト型(基本なんでもOK)
noun_type_email メールアドレス
noun_type_date 日付
noun_type_time 時刻
noun_type_url URL

その型に一致しない引数ではコマンドを実行できない。
たとえば、以下のようにした場合、 [name aaa@example.com] ならば実行可能だが、[name aaaaaa]では実行できない。

CmdUtils.CreateCommand({
name: "name",
takes : {args:noun_type_email},
preview : function(pblock, args){
pblock.innerHTML = 'ぷれびゅー'+args.text;
},
execute: function(args) {
displayMessage( 'じっこう!'+args.text );
}
});

他にも以下のようなものがある。詳しくは、feed-parts\header\en\nountypes.js を参照。

noun_type_contact
noun_type_percentage
noun_type_async_address
noun_type_address
noun_type_language
noun_type_tab
noun_type_searchengine
noun_type_tag
noun_type_geolocation
noun_type_livemark


◇◆◇◆◇◆◇トピックス3!◇◆◇◆◇◆◇

Ubiquity Command Editor で編集したコマンドで console.log をしたい場合
CmdUtils.log(Object);でOK Ubiquity:Object といった形で Firebug に表示される


○●○●○●○コマンド作成の基本4 複数の引数を渡したい○●○●○●○

CmdUtils.CreateCommand({
name: "name",
takes : {what:noun_arb_text},
modifiers: {with: noun_arb_text, in: noun_arb_text},
preview : function(pblock, what, modifiers){
pblock.innerHTML = 'ぷれびゅー'+what.text + modifiers.with.text + modifiers.in.text;
},
execute: function(what, modifiers) {
displayMessage( 'じっこう!'+what.text + modifiers.with.text + modifiers.in.text);
}
});

型指定 については 基本3と同じ。

modifiers を記載すると複数の引数を指定できる。
第二引数以降は modifiers で指定したオブジェクトのキーを文字列の前にスペース区切りで指定する必要がある
上のように定義した場合は、 [ name xxxxx with aaaaaa in bbbbbb ] といった感じで指定すると
基本の引数には xxxxxx が入り、modifiers オブジェクト内の with に aaaaaa 、 in に bbbbbb が入る。

xxxxxx に with / in といった文字列を使いたい場合に問題が出るので、modifiers 内で指定するキーはしっかり考えないといけない。文章のように使うのかも。[ replace aaa to bbbb ] みたいに


◇◆◇◆◇◆◇トピックス4!◇◆◇◆◇◆◇
便利なコマンド作成用関数

CmdUtils.CreateCommand を拡張したコマンド作成コマンドが幾つかある

CmdUtils.makeSearchCommand 検索コマンドを簡単に作成できる。
CmdUtils.makeBookmarkletCommand ブックマークレットを簡単に実行できる
CmdUtils.makeContentPreview コンテンツのプレビュー機能を簡単に実装できる

このあたりはまた別途紹介したい。


○●○●○●○その他の情報 ○●○●○●○
拡張のフォルダに以下のようなものがあるので、参考に。
standard-feeds/
builtin-feeds/en/builtincmds.js
feed-parts/header/en/nountypes.js

WindowsXP でが 拡張のフォルダはデフォルトだと以下になる。[USER]と[PROFILE]は各自の設定で書き換えること
「C:\Documents and Settings\[USER]\Application Data\Mozilla\Firefox\Profiles\[PROFILE]\extensions\ubiquity@labs.mozilla.com\」


また、もっと詳細な内容は以下を参考に。

https://wiki.mozilla.org/Labs/Ubiquity/Ubiquity_0.1_Author_Tutorial#Commands_with_Arguments

2008年12月14日日曜日

jquery + jquery-sizzle.js のセレクタの違い(2)

jquery + jquery-sizzle.js のセレクタの違い(2)

昨日の続き

jglycy を動かすための改造

jglycy が動かない原因は sizzle の セレクタの場合 HTMLElement が持っているプロパティとして存在する属性以外は取得できなくなっているから
で、その問題の行は jquery-sizzle.js の 561行目

561行目 var result = elem[ match[1] ], value = result + "", type = match[2], check = match[4];

match[1] には $('[xxx]'); とした際の xxx が入る。 elem.className や elem.id などは存在するが
elem.jg などと言うものは存在しない。
(xxx が class の場合は preFilter で className に変更されている)

elem.getAttribute(name) としてならば jg は取得できるので以下のように変更することで利用できるようになる。

var result = elem.getAttribute(match[1]), value = result + "", type = match[2], check = match[4];


ただし、この場合 preFilter の ATTR で class -> className と変更されているので [class] が使えなくなる。
275行目を削除する必要がある。
(elem.getAttribute() の場合は class , elem のプロパティは className と名前が違う。)

275行目 "class": "className"

削除するのも嫌だし、速度も多少遅くなりそうな気もするので、以下のように Sizzle.selectors を拡張し、
[@xxx] とした場合は、jg など定義されていない属性値も取れるようににした。
(速度は計っていないので近いうちに・・・遅くならないならこんなことする必要もない)


Sizzle.selectors.match.EXTATTR = /\[@((?:[\w\u0128-\uFFFF_-]|\\.)+)\s*(?:(\S{0,1}=)\s*(['"]*)(.*?)\3|)\]/
Sizzle.selectors.filter.EXTATTR = function(elem, match, i, array){
var result = elem.getAttribute(match[1]), value = result + "", type = match[2], check = match[4];
return result == null ?
false :
type === "=" ?
value === check :
type === "*=" || type === "~=" ?
value.indexOf(check) >= 0 :
!match[4] ?
result :
type === "!=" ?
value != check :
type === "^=" ?
value.indexOf(check) === 0 :
type === "$=" ?
value.substr(value.length - check.length) === check :
type === "|=" ?
value === check || value.substr(0, check.length + 1) === check + "-" :
false;
}


また、この変更をするだけでは jglycy は動かないので jglycy の 27 行目を以下のように変更する。

$[jg].invoke($("*[" + prefix + "]", node));

$[jg].invoke($("*[@" + prefix + "]", node));

これで動作するようになる

ここまでやって 対象を絞るために htmlに準拠しない attribute を追加するよりも、 class="jglycy" と
書くようなのでもいい気がしてきた。

こんな感じ?
<a href="" class="jglycy" jg="xxxx" jg:xxxx="aaa:'bbb'">link</a>

ちょっと長くなるのが嫌ならこんな感じかな、この方がシンプルか
<a href="" class="jglycy" jg="xxxx:{aaa:'bbb'}">link</a>


これで動くための jqlycy の変更はまた今度

2008年12月13日土曜日

jquery + jquery-sizzle.js のセレクタの違い(1)

sizzle については 「高速な CSS セレクタエンジン「Sizzle」「Peppy」を試す 」 を参考にさせてもらいjquery用に作成。


一部のプラグインがどうしても動かないので、セレクタ内部を調査してみた。

jQuery の 標準のセレクタの attribute 部分の正規表現

1441行目 /^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/

sizzle の attribute 部分の正規表現

267行目 ATTR: /\[((?:[\w\u0128-\uFFFF_-]|\\.)+)\s*(?:(\S{0,1}=)\s*(['"]*)(.*?)\3|)\]/

見た目からして違っているが、最初に気づく違いは @? の部分

jquery の場合 $('a[@href]') のようにかけるけど
sizzle で同じように書くと例外が発生する。sizzle の場合 $('a[href]')であれば問題ない。

と言うわけで、 対象プラグインの @href を href に変更し動くようになった。
sizzle側の正規表現の「 \[ 」の直後に 「 @? 」 と追加しても動くようになるが、sizzle のバージョンアップ時に問題が出るのでお勧めできない。

と言うか、これは仕様なのかバグなのか・・・。

次回は jglycy が動かないのでその対応を考えようと思う。
そもそも html に存在しない属性は取れなくなってるような気がするけど。


追記

@ を使う書き方どこかでできたよなぁとなやんだけど、たぶんXPathかなという結論に
CSSで @はimportとかそのあたりくらいしか見つからなかった。

2008年11月22日土曜日

jQuery プラグイン : Ajax で読み直してもイベント再登録不要にする

reglib versus JQuery に触発されて
機能としてほしくなったので一部を jQuery で実装してみた。
#Ajax で画面ロードしたときイベント登録しなおしたくない


実装したのは、DOM を再構築してもイベント登録が消えない部分。
DOM 構築までイベントが登録されない点は悪い面と良い面があるのでとりあえず不要かな。

jquery.hijack.js

まだ click イベントでしか動作チェックしてないのでご利用は自己責任でお願いします。
名前を適当につけちゃったので名前も募集してます。

こんな変な実装ダメよとかあったらご指摘いただけるとうれしいです。

===使い方===
・イベント登録
$.hijack.click(selector, function);

click以外のイベントでもOK
selector には イベント対象になる要素を選択するための jQuery のセレクタ を入力
function には イベント発生時の処理を記載する。

例)
$.hijack.click('#click', function(){alert('click!');});

id="click" を持つ要素をクリックすると click! とアラーとを出す。

なお、function は 引数として event オブジェクトをとり、 function 内部での
this は イベント発生したオブジェクトなので、いままで通常のイベント登録で
登録していたものも利用可能(のはず)。


その他メソッド
・イベント登録
$.hijack.bind(event, selector, function);
これでも登録可能、第一引数にイベント名、第二引数以降はセレクタと関数を入れる。

・イベント削除
$.hijack.unbind(event, selector, function);
引数は上と同じ、ただし引数は必須ではない。
引数がない場合、すべてのイベントを削除する。
引数を増やすごとに削除するイベントが制限される。

$.hijack.unbind('click'); ならば click イベントすべて削除
$.hijack.unbind('click', 'a'); ならばアンカータグへの click イベントをすべて削除

と言った感じ

jQuery + IE6 ではまった

Ajax で json を取得して json で持っている HTML 要素と入れ替えようとしたときに
IE6 でだけどうしても重くて仕方なかった。

コードはこんな感じ

function(json){
var html = $(json.html);
$('#main').empty().append(html);
}
#json は HTML のほかにいろいろ入っている

html が小さければ特に問題ないけれども、ある程度以上のHTMLを挿入しようとすると
とたんに重くなってずっと悩んでいた。
#実際 Firefox だと 400ms なところが、IE だと12000ms くらいかかる。

empty が重いのか? append が重いのか?と悩んでいたが、重かったのは $(json.html) の部分。

以下のようにして解決。コードもすっきり。
function(json){
$('#main').html(json.html);
}

それでも大きい HTML だと 1000ms くらいかかるけど、もう仕方ないかなぁ

2008年9月20日土曜日

Smalltalk ベストプラクティスパターンを読んでのメモ2

これの続き



Comparing Method


オブジェクト同士を並び替えたいときは比較用メソッドを定義しよう
メソッド名で le だと分かりにくい気もする。lessThanOrEqual のほうが良いか。。。
<=って書きたい。


class Event {

public function le($anEvent) {
return ($this->timestamp <= $anEvent->timestamp);
}

}



Reversing Method


メッセージの流れをスムーズに。
よくわからないけど


class Point {

public function printOn($asStream) {
$this->x->printOn($asStream);
$asStream->nextPutAll('@');
$this->y->printOn($asStream);
}

}


class Stream {
public function _print($anObject) {
$anObject->printOn($this);
}

}
class Point {
public function printOn($asStream) {
$asStream->print($this->x);
$asStream->nextPutAll('@');
$asStream->print($this->y);
}

}



Method Object

たくさんの引数や一時変数であふれているメソッドを単純化する。


class Obiligation{

public function sendTask($aTask, $aJob) {
//一時変数いっぱい
$notProcessed = null;
$processed = null;
$copied = null;
$executed = null;
// たくさんの処理
}
}



class Obiligation{

public function sendTask($aTask, $aJob) {
//一時変数いっぱい
$notProcessed = null;
$processed = null;
$copied = null;
$executed = null;
$this->prepareTask($aTask, $aJob, $notProcessed, $processed, $copied, $executed);
}

public function prepareTask($aTask, $aJob, $notProcessed, $processed, $copied, $executed){
//ComposedMethodで処理を請け負うと引数が多くなりすぎ、コード量の削減にもならない
}
}



class TaskSender{
private $obiligation;
private $task;
private $job;
private $notProcessed;
private $processed;
private $copied;
private $executed;

public function __construct($anObiligation, $aTask, $aJob) {
$this->obiligation = $anObiligation;
$this->task = $aTask;
$this->job = $aJob;
}
public function compute(){
//ここにたくさんの処理。
}
}

class Obiligation{

public function sendTask($aTask, $aJob) {
$taskSender = new TaskSender($this, $aTask, $aJob);
$taskSender->compute();
}
}



Execute Around Method

続けて実行しなければならないアクションのペアを表現

class Cursor {
public function showWhite($aBlock) {
$old = null;
$old = Cursor::currentCursor();
$this->show();
$aBlock->value();
$old->show();
}
}

class File{
public function openDuring($aBlock) {
$this->open();
$aBlock->value();
$this->close();
}

}


class File{
public function openDuring($aBlock) {
$this->open();
try{
$aBlock->value();
}catch(Exception $e){
//更に例外を投げるならその前に$this->close()をする。
}
$this->close();
}
}



Debug Printing Method

デバッグ用のメッセージを出力する。


class Association{
public function printOn($asStream) {
$asStream->print($this->key);
$asStream->nextPutAll('->');
$asStream->print($this->value);
}
}


Method Comment
メソッドのコメントの書き方

メソッドの先頭にコメントを書き、コードから明白に読み取れない重要な
情報を伝える。

2008年9月15日月曜日

Smalltalk ベストプラクティスパターンを読んでのメモ1



PHPで書いてみる。

3 振る舞い
3.1 メソッド

ComposedMethod


1つのことのみするメソッドに分割しよう。


class Controller{
public function controlActivity() {
$this->controlInitialize();
$this->controlLoop();
$this->controlTerminate();
}
public function controlInitialize(){
//初期化
}
public function controlLoop(){
//ループ処理
}
public function controlTerminate(){
//切断
}
}


Constructor Method

インスタンス生成時にいろいろな引数を渡したいときは適切に作ってくれる
メソッドを用意しよう。


class Point{
public function __construct($x, $y){
// .....
}
public function Polar($r, $theta) {
$x = $r * cos(deg2rad($theta));
$y = $r * sin(deg2rad($theta));
new self($x, $y);
}
}

new Point(1, 1);

Point::Polar(sqrt(2), 45);


Constructor Parameter Method

インスタンス変数を一度に設定するメソッドを定義しよう


class Point {
public function __construct($x, $y) {
$this->x = $x;
$this->y = $y;
}
}



class Point {
public function __construct($x, $y) {
$this->_setXY($x, $y);
}
private function _setXY($x, $y) {
$this->x = $x;
$this->y = $y;
}
}




Shortcut Constructor Method
ショートカットメソッドを定義しよう

こんな感じ?


class Number {
public function __construct($x) {
$this->x = $x;
}
public function getPoint($y){
return new Point($this->x , $y);
}
}

$number = new Number(1);
$point = $number->getPoint(2);



Conversion

オブジェクトのプロトコルを増やすのではなく、別のオブジェクトに変換しよう

Converter Method

オブジェクトを同一プロトコルのオブジェクトに変換するメソッドを用意しよう
メソッド名はasXXXとする。

こんな感じ?微妙・・・

class Collection {
private $collection = array();
public function __construct(array $array = array()){
$this->collection = $array;
}
public function asSet() {
return array_unique($this->collection);
}
}

class Number {
public function __construct($x){
$this->x = (int)$x;
}
public function asFloat() {
return (float)$this->x;
}
}



Converter Constructor Method

変換もとのオブジェクトを引数として受け取りインスタンスを生成しよう


Query Method
真偽値を返すメソッドを用意しよう。メソッド名はisXXXとする。


class SwitchClass {
public function makeOn() {
$this->status = 'on';
}
public function makeOff() {
$this->status = 'off';
}
public function status() {
return $this->status;
}
}

class WallPlate{
public function __construct(){
$this->switch = new SwitchClass();
$this->lignt = new Light();
}
public function update() {
if($this->switch->status == 'on'){
$this->lignt->makeOn();
}elseif($this->switch->status == 'off'){
$this->lignt->makeOff();
}
}
}




class SwitchClass {
public function makeOn() {
$this->status = 'on';
}
public function makeOff() {
$this->status = 'off';
}
public function status() {
return $this->status;
}
public function isOn() {
$this->status == 'on'' ? true : false;
}
}

class WallPlate{
public function __construct(){
$this->switch = new SwitchClass();
$this->lignt = new Light();
}
public function update() {
if($this->switch->isOn){
$this->lignt->makeOn();
}else{
$this->lignt->makeOff();
}
}
}


続く