Introduction

ブログ内検索

  • このサイトの記事を検索 by Google

おすすめの一冊!

無料ブログはココログ

« 2010年12月 | トップページ | 2011年2月 »

2011年1月

2011-01-27

「なぜふぉろ」の shortcut 対応


Twitter 上でフォロワーごとにメモが書けるサービス「なぜふぉろ」の
GreaseMonkey スクリプト(ユーザスクリプト)をバージョンアップしました。


  ⇒ ダウンロードは userscripts.org からどうぞ


変更内容は、次の1点です。

  ・Twitter 新デザインで、メモの編集中に、いくつかのキーが Twitter の
   ショートカットに反応してしまって入力できない、という問題を修正。

たとえば、"n" とか "j" とか改行(エンター)とか押すと、
「新しいツイートの作成」ウインドウが開いたりしてしまって、
「なぜふぉろ」のメモ編集欄に入力できなかったのでした。



以下、技術的な話。

正攻法だとショートカット用のイベントハンドラを removeEventListener() すれば
いいのですが、そのためにはイベントハンドラ関数のポインタが必要なので、
GreaseMonkey からは手が出せません。

そこで、「なぜふぉろ」の編集ウインドウで keypress/keyup/keydown の
イベントハンドラを設定し、そのイベントハンドラの中で event オブジェクトの
stopPropagation() を呼ぶ、という技を使いました。

// disable shortcut keys myWindow.addEventListener('keypress', function(e) { e.stopPropagation(); }, false); myWindow.addEventListener('keydown', function(e) { e.stopPropagation(); }, false); myWindow.addEventListener('keyup', function(e) { e.stopPropagation(); }, false);
Event.stopPropagation() を呼ぶと、イベントの伝搬 (bubbling) を抑止できます。 メモ編集中は「なぜふぉろ」の編集ウインドウが最前面 (top) にあるので、 真っ先にこのイベントハンドラが呼ばれます。そこで stopPropagation() すると、 その背後にある要素(たとえば window とか)のイベントハンドラが呼ばれなくなります。 「なぜふぉろ」の編集ウインドウを閉じると、Twitter のページが最前面に戻るので、 ショートカットキーが再び有効になります。かんぺき。 ちなみに、addEventListener() の第3引数を true にしたほうが 確実な気もします。しかし、Firefox でそうすると、キーを押してもテキストエリアに 文字が入力されなくなってしまいました。Google Chrome では文字が入力できたので、 フォーム周りのキー取得の実装が微妙に異なっているのかもしれません。 いずれにしても、より一般的な false のままで目的は達成できたので、 深追いはしていません。 あと、拾うイベントは keypress だけで大丈夫かと思ったのですが、 Twitter 新デザインの UI ではなぜか keyup/keydown も使っている (キーによって異なる・・・)ので、3種類とも無効化してあります。 今後とも「なぜふぉろ」をよろしくお願いします。

2011-01-26

2種類の API keys


予想どおり Evernote API のユーザ認証でハマったので、メモ。


API key を申請すると、consumer key と consumer secret が
メールで送られてきます。Evernote API を叩くときは、
UserStore に対してこれらの値を指定してあげるわけです。

my $userStoreTrans = Thrift::HttpClient->new($config{site} . '/edam/user'); my $userStoreProt = Thrift::BinaryProtocol->new($userStoreTrans); my $userStore = UserStoreClient->new($userStoreProt, $userStoreProt); my $versionOK = $userStore->checkVersion('tw2en/0.0.0.1', EDAMUserStore::Constants::EDAM_VERSION_MAJOR, EDAMUserStore::Constants::EDAM_VERSION_MINOR); die unless $versionOK; my $authResult = $userStore->authenticate($config{username}, $config{password}, $config{consumer_key}, $config{consumer_secret}); die unless $authResult;
実際に試してみると、なにやら例外を上げようとして、 その例外のクラスを use してなくて死亡します。 とりあえず、パッチ。UserStore.pm に use 宣言を1行追加するだけ。
*** UserStore.pm.org 2010-11-30 05:25:37.000000000 +0900 --- UserStore.pm 2011-01-26 18:44:36.000000000 +0900 *************** *** 9,14 **** --- 9,15 ---- use Thrift; use EDAMUserStore::Types; + use EDAMErrors::Types; # HELPER FUNCTIONS AND STRUCTURES
これで実行すると、ちゃんと例外を上げてくれるようになります。
$VAR1 = bless( { 'parameter' => 'consumerKey', 'errorCode' => 8 }, 'EDAMErrors::EDAMUserException' );
API リファレンスの EDAMErrorCode の項を見ると、errorCode=8
INVALID_AUTH Username and/or password incorrect
というらしい。 しかし、指定した username/password はさっき sandbox に 作ったばかりで、普通にログインできるもの。 困ったので Evernote のユーザーフォーラムを検索してみると、 答えがわかりました。 Re: EDAMUserException(errorCode:8,parameter:"consumerKey") API keys には "client keys" と "web service keys" の2種類があって、 後者の API keys は OAuth 認証にしか使えないとのこと。 Seaoak は「Web アプリケーションの作成」という名目で申請したので、 UserStore.authenticate では使えないのでした。 二日連続での徒労に、ちょっとやられてます・・・

Evernote API の門を叩く


Evernote の API を叩くサービスの開発を再開しました。
とりあえず、Twitter のツイートとかを取り込めるようにするつもり。


今回はもう最初から @nifty の CGI にするのはあきらめて、自前の VPS で
やることにしたので、まずは Thrift を make&install しました。
ただし、単に Evernote API を叩くクライアントを動かすだけなら
Thrift のインストールは要らなかったかもしれません・・・・

Evernote API の zip ファイルに Perl クライアント用のライブラリが含まれていて、
その中に Thrift のライブラリも一式含まれているみたいでした。
それを直接参照できるようにシンボリックリンクを張っておきます。

$ ln -s ~/evernote-api-1.17/lib/perl ./Evernote
続けて、Evernote API Overview のサンプルコードに従って Perl コードを書き始めます。
use strict; use warnings; use utf8; use lib './Evernote'; use Data::Dumper; $Data::Dumper::Purity = 1; use Thrift; use Thrift::HttpClient; use Thrift::BinaryProtocol; use UserStore; use EDAMUserStore::Constants; my $userStoreTrans = Thrift::HttpClient->new('https://sandbox.evernote.com/edam/user'); my $userStoreProt = Thrift::BinaryProtocol->new($userStoreTrans); my $userStore = UserStoreClient->new($userStoreProt, $userStoreProt); eval { my $versionOK = $userStore->checkVersion('tw2en/0.0.0.1', EDAMUserStore::Constants::EDAM_VERSION_MAJOR, EDAMUserStore::Constants::EDAM_VERSION_MINOR); print Dumper($versionOK) . "\n"; }; die Dumper($@) if $@;
ここで、URL を "https" じゃなくて "http" にしてしまって、はまりました。 コンパイルは成功するのですが、実行すると例外を投げてお亡くなりになってしまいます。
$VAR1 = bless( { 'code' => 0, 'message' => 'Missing version identifier' }, 'Thrift::TException' );
この例外を投げている Evernote/Thrift/BinaryProtocol.pm を覗いてみると、 バージョン番号のチェックでエラーになっているみたいでした。 値をデバッグプリントしてみると、0x8001xxxx を期待しているところに 0x35303020 とかいう値が渡されていて、それでエラーになっていました。 とりあえず通信はできてるっぽいのが非常に悩ましくて、もしかして期待値がハードコーディング されているけどその値を Evernote 用に変えないといけないんじゃないか、などと邪推したり。 たくさんググって見ましたけど、関係ありそうな話はどこにも見あたらず・・・ 結局、サーバの URL のプロトコルを "http" から "https" に変えたら あっさり動きました。 実のところ、サンプルコードが https になっているのにはかなり早い段階で気がついていて、 そこは修正したつもりだったのですが、ファイルを VPS にアップロードするときに 宛先フォルダの指定を間違えていて、別のフォルダのファイル(参照されない)が更新されて 実際に使われているファイルは更新されていなかった、というミスでした。 でも、http でも接続できるのって、なにか意味があるんですかね・・・・(くやしい 次のステップはユーザ認証ですが、ここでもまた躓きそうな予感 (-_-;

2011-01-25

算数オリンピック


はてなのエンジニアが「算数オリンピック」に挑戦してみた、
という記事が面白そうだったので、Seaoak も挑戦してみました。


    ⇒ はてな若手エンジニアが「算数オリンピック」の問題を解いてみた算数オリンピック


とりあえず、最初の問題(問題6)は直感で解けました。

2問目(問題1)は、倍数を全部書き出して、出現する数字の数を数えて、正解。

3問目(問題3)は、高校時代に勉強した「整数問題」みたいな印象(なつかしい)。
とりあえず方程式を立てて、各変数が 1~9 の整数であるという条件で
とりうる値を絞り込み、最後は5個くらいのケースを場合分けでしらみつぶし。
かなり時間がかかりましたが、とりあえず全部の値を見つけられました。
# 5個以外の解が無いことも確認できました。

4問目(問題7)は、直感で解いたのですが、不正解。



くやしいので、C言語で解いてみました。

◎ 2問目(問題1)
#include <stdio.h> int populate(int div, int count[]) { int total = 0; int i; for (i=1; i<=9; i++) { int j; for (j=1; j<=9; j++) { if (i==j) continue; { const int x = 10*i+j; if (x % div) continue; count[i]++; count[j]++; total++; } } } return total; } int main(int argc, char *argv[]) { int table2[10] = {0}; int table4[10] = {0}; int table7[10] = {0}; const int total2 = populate(2, table2); const int total4 = populate(4, table4); const int total7 = populate(7, table7); { int i; for (i=1; i<=9; i++) { if (table2[i] != total2 - 21) continue; if (table4[i] != total4 - 12) continue; if (table7[i] != total7 - 8) continue; printf("%d\n", i); } } return 0; }
◎ 3問目(問題3)
#include <stdio.h> int main(int argc, char *argv[]) { int i; for (i=100; i<=999; i++) { if (i % 10 == 0) continue; { const int x = i / 10 + i % 10; if (i % x) continue; } { const int y = i / 100 + i % 100; if (i % y) continue; } printf("%d\n", i); } return 0; }
◎ 4問目(問題7)
#include <stdio.h> int populate(int x) { int count = 0; while (x) { if (x & 0x1) count++; x >>= 1; } return count; } int main(int argc, char *argv[]) { int count = 0; int x; for (x=0; x<(1<<25); x++) { const int mask_column = ((0x1 << (5*4)) | (0x1 << (5*3)) | (0x1 << (5*2)) | (0x1 << (5*1)) | (0x1 << (5*0))); if (populate(x) != 6) continue; if (! ((x >> (5*4)) & 0x1f)) continue; if (! ((x >> (5*3)) & 0x1f)) continue; if (! ((x >> (5*2)) & 0x1f)) continue; if (! ((x >> (5*1)) & 0x1f)) continue; if (! ((x >> (5*0)) & 0x1f)) continue; if (! ((x >> 4) & mask_column)) continue; if (! ((x >> 3) & mask_column)) continue; if (! ((x >> 2) & mask_column)) continue; if (! ((x >> 1) & mask_column)) continue; if (! ((x >> 0) & mask_column)) continue; count++; } printf("%d\n", count); return 0; }
いずれもちゃんと正解が得られました。 ちゃちゃっとビット演算を使うときはC言語でも悪くないですねー

2011-01-10

Opera と AltIME


Opera を常用ブラウザのひとつにすることにしました。

これまでは Sleipnir と Firefox をひとつずつ開いたままにしていたのですが、
タブが増えすぎてタブバーをスクロールさせないといけなくなって面倒なので、
もうひとつブラウザを増やすことにしたわけです。

Opera は素のままでもタブバーを右サイドに縦に置けるのがすばらしい。



で、使い始めたのですが、ひとつ問題が。



Seaoak は英語配列キーボードを愛用しているので、
当然ながら全角半角キーなどというものはありません。
普段は右 Alt キーで IME を ON/OFF してます。
これは AltIME というソフトを使って実現しています
(AltIME の設定メニュー内でチェックを ON にするだけ)。

ところが、Opera のウインドウで右 Alt キーを押すと、
左 Alt キーと同様にメインメニューが開いてしまいました。
IME の ON/OFF はできているのですが、フォーカスが
メニューに移ってしまうので入力が中断されてしまいます。

推測するに、Opera は独特のキー読み取り方法を使っていて、
AltIME での横取りがうまくできないのかもしれません。



ちょっとググってみたら、Opera の設定をカスタマイズして
メニューを非表示にすれば OK と書いてあるのを発見。
いろいろ試行錯誤して、Opera の設定ファイルを直接書き換えて、
2時間くらいかけてなんとかメニューアイコンを非表示に。
しかし、症状は変わらず・・・(ちなみに Opera 11.00 です)。



方針を変えて、AltIME の側でなんとかしてみることに。

AltIME の「キー入れ替え」は効いているっぽいので
(右 Windows キーを右 Alt キーに読み替えるのは効いてた)、
右 Alt キーを全角半角キーに読み替えさせてみる、という手です。
具体的には、AltIME の「高度な設定」メニューの「キー入れ替え」欄に
「/B829」と書いてみます。

これで、右 Alt キーを押しても Opera のメインメニューは
表示されなくなりました!

ただ、もともと英語キーボードのせいか、全角半角キーのコードを
入力しても IME が ON/OFF できなかったので、AltIME の
「高度な設定」メニューの「IME起動キー」欄で「0029」を指定。
これで、右 Alt キーでの IME ON/OFF ができるようになりました。


かんぺき!


ちなみに、右 Windows キーも IME ON/OFF にしたい場合は
AltIME の「高度な設定」メニューの「キー入れ替え」欄に
「/DC29」を追加してあげれば OK です。

あと、AltIME の設定メニューの「右 Alt キー」欄のチェックは
外しておいたほうがいいと思います(試してませんが)。

OS は Windows7 Professional 32bit です。



Opera ユーザで AltIME ユーザなひとにしか役に立たないノウハウですが、
貴重な連休の最終日の夜を費やしたので、記事にしておきました。

お役に立てたらうれしいですー


◎追記 (2011/Nov/12)
上記設定だとバッククォートが入力できなくなります。 バッククォート入力しようとすると IME が ON/OFF されてしまいます。 確認不足ですみません。 (最近シェルスクリプトを書いていないのがバレてしまった・・・) 調べてみると、英語キーボードの「`~」キーと、日本語キーボードの 「半角/全角」キーは、同じスキャンコードを持っていることがわかりました。 物理的に同じような位置にあるキーは同じスキャンコードを割り当てられて しまっているようです。(ハードを共通化してコスト削減をしたかった?) 参考) スキャンコード - Wikipedia 参考) 109キーボードのスキャンコード一覧のページ というわけで、スキャンコード 0x29 ではなくて、他の使われていない値を 踏み台に使うのが良いみたいです。たとえば、+0x80 した 0xA9 とか。 Seaoak の AltIME の今の設定は以下のようになっています。 AltIME - オプション設定 高度な設定 IME起動キー: 00A9 キー入れ替え: /DCA9/B8A9 ご参考まで。

have_read_it_already 第2弾


Read It Later の Web サイトをまるで「Web アプリケーション」のように
快適にしてくれる(かもしれない)ユーザスクリプトを公開しました。


    ⇒ have_read_it_already (userscripts.org)


Read It Later はオンラインのブックマークサービスです。
異なるデバイス間(たとえば iPhone と PC とか)でブックマークを共有できます。
Seaoak は、iPhone で Twitter の TL を眺めていて気になった URL を
後で PC で閲覧するために利用しています。けっこう便利。

ただ、Read It Later の Web サイトを PC のブラウザで見たとき
iPhone App に比べてあきらかに手抜きなのが残念でした。
項目の削除ができないし、短縮 URL はそのままだし、文字化けしてるし・・・


というわけで、先日、項目の削除だけできるようにするユーザスクリプトとして
have_read_it_already を公開しました。

今日はその改良版を公開します。


改良点は以下の4点:

  ・短縮 URL を実際の URL に展開してくれます。
  ・文字化けしているタイトルを直してくれます。
  ・アイコン (favicon) を実際のサイトのものに置き換えてくれます。
  ・ページ切り替えに追従して随時書き換えを行います。

実装としては、Seaoak の自作サービス「URL 情報取得 API」を利用しています。 
あと、情報を WebStorage (localStorage) にキャッシュしています。
サーバ側にはなにも情報を保存していません。


我ながらなかなかいいモノのできたと思っているので、
ぜひお試しいただければと思います!

DOM Events サポート判定


きのうの DOM Events の記事の続き。


Opera 11.00 では DOMSubtreeModified イベントは実装されていませんが、
その代わりに DOMAttrModified イベントが実装されていました。


                       Firefox   Chrome     Opera
    DOMAtttrModified      ○        ×        ○
    DOMSubtreeModified  ○        ○        ×


というわけで、次のような feature detection コードを使うと、
Firefox/Chrome/Opera のどれでも動くコードが書けます。

var hasFeature_DOMSubtreeModified; var hasFeature_DOMAttrModified; (function() { var elem = _doc.createElement('div'); elem.addEventListener('DOMSubtreeModified', function() { hasFeature_DOMSubtreeModified = true; }, true); elem.addEventListener('DOMAttrModified', function() { hasFeature_DOMAttrModified = true; }, true); var elem2 = _doc.createElement('div'); elem.appendChild(elem2); elem2.className = 'foobar'; })(); if ((! hasFeature_DOMSubtreeModified) && (! hasFeature_DOMAttrModified)) throw 'DOM Events are not supported';
このコードは、DOM Events が「同期的」に処理されることに依存しています。 ちょっとあやしいですが、今のところ動いてます。 早く userscrits.org が復旧しないかなぁ・・・

DOM Events


Read It Later の Web サイトの使い勝手を改良するグリモンを作ってます。

Read It Later はオンラインのブックマークサービス。 異なるデバイス間(たとえば iPhone と PC とか)でブックマークを共有できます。
もともとが Ajax なサイトなので、ページの読み込み時に ページ要素(表の中身)をグリモンで書き換えたとしても、 コンテンツの切り替えで元のもくあみ。 Twitter の新デザインも同じような Ajax サイトですが、 Twitter はコンテンツを切り替える際に URL も書き換えて くれるので、それを検出すれば対応できました。 具体的には、hashchange イベントを拾えば OK。 で、調べてみると、DOM Events というのが W3C Draft になっていて、 DOM の変更(要素の挿入・削除や属性の変更など)を「イベント」として 扱えるらしいです。 ⇒ Mozilla の DOM Events の説明W3C の DOM Level 3 Events のドラフト さっそく使ってみた結果、以下のことがわかりました。   ・Google Chrome 9.0.597.45 beta では DOMAttrModified イベントは実装されてない。   ・DOMSubtreeModified イベントは Firefox と Google Chrome で使える。Opera 11.00 では未実装。   ・ある ul 要素に対して addEventListener('DOMSubtreeModified', func, true) とできる。   ・DOMSubtreeModified イベントの処理は比較的重いかもしれない。    (まちがってイベントハンドラを多重に登録したら Chrome がしばらく応答しなくなった)    ※このイベントは「同期的」なので注意しないと重くなるらしい。 あと、Read It Later に固有のノウハウですが、    ページの書き換え中は "list" ul 要素に "loading" クラスが一時的に付与されるが、    要素の書き換えが完全に終わる前に除去されてしまう。DOMSubtreeModified イベントハンドラで    "loading" クラスの有無をチェックして、除去された時点で書き換えを行うと、    その後で favicon の img 要素の書き換えが行われて元のもくあみになることがある。    回避策としては、しばらくの間(たとえば 200ms とか)DOMSubtreeModified イベントが    発生しなければ OK とする、みたいなやり方がある:
(function() { var targetElem = document.getElementById('list'); var timer; var prevValue; var handler = function(e) { var value = targetElem.className; if (timer) { window.clearTimeout(timer); } if (timer || ((prevValue == 'loading') && (! value))) { timer = window.setTimeout(function() { timer = undefined; targetElem.parentNode.removeEventListener('DOMSubtreeModified', handler, true); overwrite(targetElem); // 書き換え実行 }, 200); } prevValue = value; }; targetElem.parentNode.addEventListener('DOMSubtreeModified', handler, true); })();
ちょっと美しくないですが、とりあえず動きます。 このグリモンはひとまず完成したのですが、userscripts.org のサーバが 死んでいるので upload できません・・・ そのうち公開しますー p.s. ちなみに、DOM Events は "deprecated" になったらしいです (-_-;

« 2010年12月 | トップページ | 2011年2月 »