タッチパネルに対応するアプリケーションを開発する場合、開発は従来のマウスを使うブラウザ環境で行いつつ検証を実機でという手順を繰り返すことになるが、その際にイベントハンドラに記述するマウスイベントとタッチイベントの切り替えを自動的に判定して行う方法を Apple が公開している iAd のサンプルコード内に見つけたので開発しているアプリケーションのコードに適用した。
const BGPSupportsTouches = ("createTouch" in document);
const BGPStartEvent = BGPSupportsTouches ? "touchstart" : "mousedown";
const BGPMoveEvent = BGPSupportsTouches ? "touchmove" : "mousemove";
const BGPEndEvent = BGPSupportsTouches ? "touchend" : "mouseup";
このように定義しておいて次の様な例で使うと、実機に移植する際に一々コードを置換修正する必要がなくなって便利この上ない。
Anode.addEventListener(BGPStartEvent, this, false);
前回に引き続き、今度は等幅フォントのコードの様なスタイル付きのテキストでメモを保存しようと、同様の機能を追加するサービスメニュー「クリップボードから新規メモを作成.workflow」を作った。インストール方法は前回の投稿をご参照ください。
具体的には一つ目のアクションを次の様な AppleScript で置換した。
try
set theRTF to the clipboard as «class RTF»
on error
set the clipboard to «class ktxt» of ((the clipboard as text) as record)
end try
操作の流れは次の様なものを想定している。
- (マウスドラッグやキャレットブラウジングで)文書内を文字選択
- プレーンテキストでメモを作成する場合は前回の「選択部分から新規メモを作成」を選択
- スタイル付きテキストでメモを作成する場合は、選択部分をクリップボードにコピー(コマンド+C)してから今回の「クリップボードから新規メモを作成」を選択
ところで Snow Leopard になってから Autometor で作成できるサービスメニューは右図のようにコンテキストメニューの最下部に自動的に追加されるようになることが分かった。また一ヶ月使ってみた結果、Gmail の IMAP を Apple Mail で利用している場合に iPhone との同期に削除したメモが幾度となく iPhone メモアプリ上で復活してしまうという面倒な癖を持っている事が分かったので別の投稿として解決策を書こうと思う。
以前 “SQLiteデータベースのバージョンを変更する“という記事を書いたが、結局実機でもうまく動作しなかった(Safari ブラウザやシミュレータと同じ結果だった)ので、データベースのバージョンによる管理を諦めて単純にカラムを追加することにした。
try {
if (window.openDatabase) {
db = openDatabase("BackgammonPositionsDB", "1.0", "Backgammon Positions DB", 200000); /* 現行バージョンのまま */
if (!db) {
alert("Failed to open the database on disk.");
/* 初期化 */
} else {
db.transaction(function (tx) { /* 新しいカラムがなければ追加する */
tx.executeSql("SELECT crawford, star FROM BGPositions;", [],
function(tx, result) { },
function(tx, error) {
tx.executeSql("ALTER TABLE BGPositions ADD COLUMN crawford INTEGER default 0; VACUUM;");
tx.executeSql("ALTER TABLE BGPositions ADD COLUMN star INTEGER default 0; VACUUM;");
});
});
/* 全行読み込み */
return;
}
} else {
alert("Couldn't open or support the database. Please try to contact with author.");
/* 初期化 */
}
} catch(e) {
/* former changeVerion.js (前回はここにバージョンを変える関数をいれていた) */
}
Webkit のバグが解消されない限りはこの方法が良いと思う。
さてこのコードを埋めたアップデートファイルが、驚愕のわずか12時間程度で承認され公開(Backgammon Position Cards for iPhone, iPod touch, and iPad on the …)されている。また以前の投稿 “iPhone アプリのアップデートで課金する” で話していた様に価格も元の Tier5 に戻した。
更新(2010年5月11日): 一年間十分食べさせて頂いたのと世界的な不況ということを考慮してTier4に値下げした。:-)
次の様な記事を読んで Snow Leopard 標準のメモ(英語名は Notes)がなく可哀想な気もしたので、Evernote の良さであるコンテキストメニューから文書を作成する方法をメモに適応する方法を考えてみた。
タスクとメモ。あなたはふだん、どこに書き付けていますか? 付箋ですか? MOLESKINEですか? RTMですか? Evernote ですか? スティッキーですか? Googleカレンダーですか?
引用元: ただ1つのツールと心中するべし! – ライフハック心理学.
ところがコンテキストメニューについては、(愛用している OpenMenu X にも言えるのだが)将来この機能は無くなる見通しが強いらしい。
Snow Leopard において、コンテキストメニュープラグインの代替は拡張されたサービスメニューになります。
引用元: Mac OS X 用コンテキストメニュープラグイン.
そこで一手間増える事になってしまうが、Hint: Create new mail or mail note with selected text – Mac Forums を参考に日本語部分を次の様に書き換えて「選択部分から新規メモを作成」という名前の workflow 文書で保存した。
click the menu item "新規メモ" of the menu "ファイル" of menu bar 1
click the menu item "ペースト" of the menu "編集" of menu bar 1

文字スタイルを保持できないのは少し残念な反面、起動の速さを考えると iPhone ではメモを使う頻度が高く、またメモを階層化して保存する必要を感じないのは、いつまで経っても整理や計画化がされていない証拠のような気もするので、一先ずこれで Evernote と併用してみることにした。
Autometor マンドクサ(´A`)という方は、サーバに置いた 選択部分から新規メモを作成.workflow をダウンロードした後にできる “新規メモを作成.workflow” ファイルを ~/ライブラリ/Services に置いてお使いくださいませ。
AppleScript を拡張してコマンド+数字で呼び出せる Safari のブックマーレットと連携するのも面白いかもしれないと思った。
iPhone アプリで利用している SQLite のスキーマを変更する必要に迫られ開発中に予期せぬ出来事があった。具体的にはテーブルに一列追加する changeVersion という関数を利用する際に必ずバージョンがマッチしないというエラーを出力するというものだ。
success callback である第五引数内でもoj.versionがoldVersionを返すのが気になります、、、。リロードするとようやくnewVersionに変わるんですけれど、これで良いのかなぁ。
引用元: JavaScript++かも日記: 【iPhone】iPhone用 JavaScriptデータベースプログラミング入門 (6).
Safari の JavaScript のデバッガでこの問題を確認し回避策を模索していたところ、Apple の文書に次のような回答を見つけた。
Unfortunately, the only way for your code to see the new version number is by closing the browser window. If you get an error code 2 (see “Error Codes”) and the database version you passed in for the old version matches the version in db.version, you should either assume that the version change already happened or display an alert instructing the user to close and reopen the browser window.
引用元: Safari Dev Center: Safari Client-Side Storage and Offline Applications Programming Guide: Using the JavaScript Database.
結局のところ挙動は問題ないのだけど、ブラウザを閉じるか再読み込みしないと利用者に反映されないということで、次のようなコードにして iPhone の UIWebView 用に移植する前段階として一旦作業を終えることにした。
try {
if (window.openDatabase) {
// db = openDatabase("BackgammonPositionsDB", "1.0", "Backgammon Positions DB", 200000); /* 現行バージョンの記述 */
db = openDatabase("BackgammonPositionsDB", "2.0", "Backgammon Positions DB", 200000);
(2.0バージョンは開けずエラーをキャッチするので通常の処理は省略)
} catch(e) {
if (e.code == DOMException.INVALID_STATE_ERR) { // Version number mismatch.
var prevdb = openDatabase("BackgammonPositionsDB", "1.0", "Backgammon Positions DB", 200000);
prevdb.changeVersion("1.0","2.0",
function(tx){
tx.executeSql('ALTER TABLE BGPositions ADD COLUMN crawford INTEGER default 0;VACUUM;');},
function(error){ /* SQLエラー処理 */ },
function(){ /* 成功後初期化処理 */ });
return;
}
alert("Unknown error " + e + ".");
}
またこの検証作業に関して SQLite のバージョンを戻す(1.0 に下げる)必要があり、前述した Apple の文書にもある ~/Library/Safari/Databases/ にあるファイルを削除して Safari を再起動するという若干面倒な手続きを取るしか方法がないので、Rails のような管理手法を Automator や AppleScript で作ることが望ましい気がした。
実際の iPhone 上の UIWevView ではどのような挙動になるのか、悩ましいことになるようであれば追って報告しようと思う。
これまでは Transmit と Smultron、SmartCVS という組み合わせでウェブサイトやウェブアプリケーションの開発を行っていたが、Smultron の開発が終了したことで Coda を導入することにした。加えて FireWire 400 や パラレル ATA、冷陰極管バックライト液晶といった旧システムに別れを告げるべくハードウェアを iMac に変更し、まっさらの状態から Snow Leopard にウェブ開発環境を整えることになったので、Coda と Subversion との連携作業について内容を書き留めておく。
- 最新の Subversion を CollabNet からダウンロードしてインストールする。
- Subversion のディレクトリを作成する。(例:
~/svn)
- 保管プロジェクトのディレクトリを作成する。(例:
~/svn/project、実際は上記2と併せて mkdir -p ~/svn/project で一手順とした。)
- Subversion のリポジトリ作成コマンドを入力する。(例:
svnadmin create ~/svn/project)
- 上記4で作成される
~/svn/project/conf/svnserve.conf の次の4行のコメントを外し先頭の空白を詰める。
anon-access = read
auth-access = write
password-db = passwd
authz-db = authz
- 同階層にある
~/svn/project/conf/authz に次の内容を追加する。(斜体のユーザ名は適宜調整する。)
[/]
hkitago = rw
* = r
- 更に同階層にある
~/svn/project/conf/passwd に次のようにユーザ名=パスワードの形で1行追加する。
hkitago = passwd
- Subversion を起動する。(例:
svnserve -d -r ~/svn/project)
更に最後の手順で行った Subversion の起動を OS X の起動後に自動的に行う様に設定する。以前は Lingon というGUIアプリケーションを使っていたのだが、こちらも開発が終了するとのことで Apple が公開している Mac OS X Manual Page For launchd.plist(5) を参照しつつ手作業とした。どちらの場合も管理権限が必要となる。今回は org.tigris.subversion.svnserve という一意の名前で呼び出す事ができるように launchd daemon に登録した。
org.tigris.subversion.svnserve.plist という XML ファイルを次のような内容で作成する。(例:sudo vi /Library/LaunchDaemons/org.tigris.subversion.svnserve.plist)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>org.tigris.subversion.svnserve</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/svnserve</string>
<string>-i</string>
<string>-r</string>
<string>/Users/hkitago/svn</string>
</array>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<false/>
</dict>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockServiceName</key>
<string>svn</string>
</dict>
</dict>
<key>OnDemand</key>
<true/>
</dict>
</plist>
- 権限を変更する。(例:
sudo chown root:wheel /Library/LaunchDaemons/org.tigris.subversion.svnserve.plist)
- launchd daemon に読み込む。(例:
sudo launchctl load /Library/LaunchDaemons/org.tigris.subversion.svnserve.plist)
- launchd daemon を起動。(例:
sudo launchctl start org.tigris.subversion.svnserve)
子供が昼寝から起きたので Coda 側の設定は次回に。
wordpressのタイトルを表示形式を変更[to-R]にて説明されているやり方を参考に/blog/wp-content/themes/default/header.phpのtitleタグの中身を下記の通りに設定した。
引用元: WordPressでtitleタグの中身をカスタマイズする.
Twitter や WordPress に投稿をする際にブックマークレットを使う機会が増えているのだが、WordPress のタイトルタグの出力方法に癖があって空白文字が題名そのものの先頭や末尾に付与されてしまう問題を解決しようと試みた。条件としては引用した投稿で述べられているようにトップページの表示にのみブログのタイトルを表示し、個々の投稿や検索、カテゴリ別に表示する場合はブログのタイトルを表示せず、投稿の題名や検索語、カテゴリ名だけを表示するというものになる。

具体的な作業は wp-includes/general-template.php にある wp_title 関数の $sep 前後に付いている空白文字を削除するだけだ。
if ( !empty($title) )
$prefix = "$sep";
// Determines position of the separator and direction of the breadcrumb
if ( 'right' == $seplocation ) { // sep on right, so reverse the order
$title_array = explode( $t_sep, $title );
$title_array = array_reverse( $title_array );
$title = implode( "$sep", $title_array ) . $prefix;
} else {
$title_array = explode( $t_sep, $title );
$title = $prefix . implode( "$sep", $title_array );
}
余談まで、この関数を呼び出している header.php のタイトルタグの中にある PHP コードも三項演算子を使うと3バイト分だけより短いものになる。:-)
!wp_title('', false)?bloginfo('name'):wp_title('');
wp_title('');if(!wp_title('', false))bloginfo('name');
WordPress のコードそのものは学習するのに不向きと言われているが、製品として多くの人に愛されているのは素晴らしいことだと思う。経験上から現実はそんなものだと理解しているつもりでも実践するのは難しい。
具体的に文字列連結はHTMLタグを出力するために使用することが多く処理の速度は気になるところだ。
文字列を加算していくのは、計算の途中で、abcde、abcdeabcde、abcdeabcdeabcdeという文字列オブジェクトがその都度生成されていくのでメモリにやさしくない。巨大な文字列の連結にはjoinを使ったほうが良い。数が大きくなると速度に差が出てくる。
引用元: 最速インターフェース研究会 :: 実践JavaScriptリファクタリング.
PHPの結果が気になったのでこちらの記事を参考に(若干コードを変更)して実験してみたところ、連結の方法が最も速いと言う結果になった。元記事のサンプルコードでも統計的に同じ結果(DoS攻撃してスミマセン)なので、PHPのバージョンや環境の明記が必要だと思った。尚今回の環境はMac OS X 10.5.7のPHP 5.2.8 (cli) (built: Feb 5 2009 21:21:13) をApache/2.2.11 (Unix) (Server built: Feb 3 2009 01:54:45)のモジュールとして利用している。
特にIEで顕著なのだが、JavaScriptはappendChild関数を使うより文字連結したタグをinnerHTML関数で差し込む方が速いということも同種の問題が起因しているのだろうか。これらの問題は、コンピュータ上のWebkitやChromeであれば意識する必要もあまりないのかもしれないが、iPhoneなど非力ハード上ではまだまだ注意すべき問題だろう。

// 追記編
$time1 = getmicrotime();
$str = null;
for($n = 0; $n < $count; $n++){
$str.= $str1;
$str.= $str2;
$str.= $str3;
}
$time1 = getmicrotime() - $time1;
// 連結編
$time2 = getmicrotime();
$str = null;
for($n = 0; $n < $count; $n++){
$str.= $str1.$str2.$str3;
}
$time2 = getmicrotime() - $time2;
// 非連結編
$time3 = getmicrotime();
$str = null;
for($n = 0; $n < $count; $n++){
$str.= "$str1$str2$str3";
}
$time3 = getmicrotime() - $time3;
// 配列編1
$time4 = getmicrotime();
$arr = array();
for($n = 0; $n < $count; $n++){
array_push($arr, $str1, $str2, $str3);
}
$str = implode('', $arr);
$time4 = getmicrotime() - $time4;
// 配列編2
$time5 = getmicrotime();
$arr = array();
for($n = 0; $n < $count; $n++){
$arr[] = $str1;
$arr[] = $str2;
$arr[] = $str3;
}
$str = implode('', $arr);
$time5 = getmicrotime() - $time5;
ヤフーオークションは、決済に関する利益も大きいのか管理側も表示側も使い勝手が日増しに良くなっているが、一つ問題を挙げるとするとテーブルの項目、例えば「現在価格」をクリックして並び順を変えると最初に小さい数字が上から並ぶようなことが、全ての項目について「昇順」となることだ。これは利用者の状況にもよるが、中には「降順」を最初の命令にした方が良い場合がある。
そこで進行中の計画物を調べてみたところ、Table Sorter ライブラリを使っているアプリケーションの中に同様の問題を見つけたので、tablesort.js の最後の関数を次のように修正した。
// Numeric sorting.
// Compare two dictionary structures and indicate which is larger.
TableSort.compare_num = function(a, b) {
var aNum = parseFloat(a[0]);
if (isNaN(aNum)) {
aNum = -Number.MAX_VALUE;
}
var bNum = parseFloat(b[0]);
if (isNaN(bNum)) {
bNum = -Number.MAX_VALUE;
}
if (aNum == bNum) {
return 0;
}
return (aNum > bNum) ? -1 : 1;
}
1と-1を入れ替えるだけなので、ヤフーもぜひ検討してほしいと思ったのだが、オークションのアプリケーションは同期通信で引数を渡して返してもらうという伝統的な造りになっていた…。