Firefox では createEvent を利用することで要素にクリックイベントを発生させることができます。(IE ではまた別の関数が提供されています)ためしに Yahoo! JAPAN のトップページで Firebug を開いて、下のコードを実行すると経済タブをコードから開けます。
var elem = document.getElementById('economy');
var event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
elem.dispatchEvent(event);
イベントを実行する dispatchEvent には戻り値があって、要素内で発生したイベントリスナーでキャンセルされた場合 false を返し、そうでない場合は true を返します。見た目は anchor だけれども、内部のクリックイベントリスナーで preventDefault 関数を呼び出したり、リスナ内で return false; を返している anchor はクリックして終わり、そうでないものはページを開くといったこともできます。
if (!elem.dispatchEvent(event)) {
return;
}
window.open(elem);
他のコードを見ていて window.open 関数に anchor 要素を渡してもページを開いてくれることを知りました。軽くリファレンスみたり、検索してみたのですがどこにも書いてなくて怖いです。
http://mozilla.jp/events/2009/fxdevcon/
午後のセッションに行ってきました。
はてなブックマークの拡張の話を聞いてみて、普段の開発にも活かせそうなものをピックアップしてなぐり書きしておきます。
ブックマーク保存時はサーバーにポストして、失敗したときはリトライをし、それでも駄目な場合に初めてユーザーに通知しているようです。これは HTML5 のデータベース保存にも使えるテクニックかなと思ったのですが、 HTML の場合ページ移動があって若干スコープが違うので、少しやり方を考えないといけないかもしれません。
ダイアログといった UI はすぐに閉じてしまい、バックグラウンドでサーバーにポスト。「送信中です」みたいなものもなしで、ユーザーは待たされている感を与えないのがいいです。オフラインでもない限りほとんど失敗しないでしょうし。
同期のデータ形式を plain text にし、クライアントで split して解析しているとのこと。 XML/JSON のデータ構造は重く処理に時間がかかってしまうため、 plain text を gzip 圧縮しているそうです。なるほど。
UI をブロックさせないため 200 件ごとに 1 秒のウェイトを入れているようです。 固まらせないようにするためです。
コードの話です。はてなは Perl もソフトタブのスペース 4 でやっているので、それに合わせているとのこと。 nanto_vi さんは Mozilla のコーディング規約の 2 にしたいみたいなことを言っていました。自分も趣味のプログラムは Mozilla のコーディング規約に従ってソフトタブのスペース 2 にしています。ハードタブは使わないです。
Tombloo に使われているモジュールなど、 JavaScript は基本的にコードがオープンなので使えるものは積極的に取り入れているみたいでした。
JavaScript は関数に名前を指定しなくてもいいのですが、無名関数だとエラー発生時にどこでエラーが発生したのか分かりづらいです。素早く発生源を特定できるように常に名前をつけるようにしているとのこと。コーディング規約です。
setTimeout(function() {
console.log(arguments.callee);
},100);
setTimeout(function aaa() {
console.log(arguments.callee);
},100);
他にも色々話しをしてくれていましたが、普段の開発にも使えそうなものを書いてみました。
最後にカンファレンスを開いてくれた関係者の皆さん、発表者の方々に感謝します。
書籍にのっていなくて、日本語の Web では strings.xmlって - oops でしか見つけられなかったのでメモ書きとして置いておきます。
<EditTextPreference ... android:password="true" />
現時点で android:password が Eclipse の補完に出てこないのが辛いです。
それはそうと Android や iPhone のパスワード入力の UI 、入力するまで直前の一文字目を黒丸で塗りつぶさないつくり好きです。
Windows で開発する必要が出たので、少し環境を整備しました。
設定は 22 のもので後述する問題以外は問題なく動作しました。まずフォント周りの設定を修正しました。フォントの設定はこれだけで、かなりすっきりした気がします。
(set-frame-font "Meiryo")
(set-fontset-font (frame-parameter nil 'font)
'japanese-jisx0208
(font-spec :family "Meiryo" :size 12))
Windows7 なら Meiryo UI を設定すると良い感じかもしれないです。ダウンロードの詳細 : メイリオフォント アップデートからダウンロードすれば Vista でも大丈夫みたいですね。自分は XP なので駄目ですが。
で、先に述べた問題点が 2 点ほどあって、ひとつはカーソルが表示されない問題がありました。ひとまずカーソルに色を指定したところ表示されたので、こちらは解決。
(add-to-list 'default-frame-alist '(cursor-color . "White"))
あともうひとつ大きな問題があって M-x が効かないです。 Esc キーを入力しないといけなくて、非常に不便です。おそらく環境依存による問題だと思いますが何がいけないのか追い切れていません。これは時間をみつつ調べたいです。
クライアントからのアクセスを減らすようにするために、リソースファイルにパラメータをつける手法は有名だと思います。とはいえ HTML を返す View にちまちまタイムスタンプを埋め込む作業はしたくないですし、漏れも出てしまうなあと思っていたところ、 mayaa でそれなりにうまく記述できましたのでコードを晒してみます。ただ動的に出力するもの(mayaa ファイル)に関しては、個別に対応する必要があります。それでもだいぶ楽になるかなと思います。
package example.support.mayaa;
import java.io.File;
import java.util.regex.Pattern;
import org.seasar.mayaa.FactoryFactory;
import org.seasar.mayaa.builder.injection.InjectionChain;
import org.seasar.mayaa.builder.injection.InjectionResolver;
import org.seasar.mayaa.engine.specification.NodeAttribute;
import org.seasar.mayaa.engine.specification.QName;
import org.seasar.mayaa.engine.specification.SpecificationNode;
import org.seasar.mayaa.impl.CONST_IMPL;
import org.seasar.mayaa.impl.ParameterAwareImpl;
import org.seasar.mayaa.impl.engine.specification.SpecificationUtil;
/**
* リソースファイルのタイムスタンプをクエリーパラメーターにつける {@link InjectionResolver} 拡張クラスです。
*
* <p>
* img, script, css 要素に対して実行されますが、 mayaa
* ファイルから動的に生成するものには付加しませんので、それぞれ個別に対応する必要があります。
* </p>
*
* <ul>
* <li><script type="text/javascript"
* src="/js/jquery.js?1258000000000"></script></li>
* <li><link type="text/css" href="/css/default.css?1258000000000" />
* <li><img src="/img/logo.png?1258000000000" />
* </ul>
*
* @author ussy
*/
public class ResourceTimestampSetter extends ParameterAwareImpl implements InjectionResolver, CONST_IMPL {
private static final long serialVersionUID = 1L;
private static final QName QH_IMG = SpecificationUtil.createQName(URI_HTML, "img");
private static final QName QH_SCRIPT = SpecificationUtil.createQName(URI_HTML, "script");
private static final QName QH_LINK = SpecificationUtil.createQName(URI_HTML, "link");
private static final QName QH_SRC = SpecificationUtil.createQName(URI_HTML, "src");
private static final QName QH_HREF = SpecificationUtil.createQName(URI_HTML, "href");
private static final QName QX_IMG = SpecificationUtil.createQName(URI_XHTML, "img");
private static final QName QX_SCRIPT = SpecificationUtil.createQName(URI_XHTML, "script");
private static final QName QX_LINK = SpecificationUtil.createQName(URI_XHTML, "link");
private static final QName QX_SRC = SpecificationUtil.createQName(URI_XHTML, "src");
private static final QName QX_HREF = SpecificationUtil.createQName(URI_XHTML, "href");
/**
* リソースファイルへのパスにタイムスタンプを追加します。
*
* <p>
* ファイルが取得できない場合は何も行いません。
* </p>
*
* @param original
* {@link SpecificationNode}
* @param srcName
* ファイル参照パス
*/
protected void appendQueryTimestampParameter(SpecificationNode original, QName srcName) {
NodeAttribute src = original.getAttribute(srcName);
if (src == null) {
return;
}
PathAdjuster adjuster = ProviderUtil.getPathAdjuster();
boolean needAdjust = adjuster.isTargetNode(original.getQName());
if (!(needAdjust && adjuster.isTargetAttribute(original.getQName(), srcName))) {
return;
}
String basePath = EngineUtil.getSourcePath(original);
String value = adjuster.adjustRelativePath(basePath, src.getValue());
String filePath = FactoryFactory.getApplicationScope().getRealPath(value);
File resouruce = new File(filePath);
if (resouruce.exists()) {
String timestamp = String.valueOf(resouruce.lastModified());
original.removeAttribute(srcName);
original.addAttribute(srcName, src.getValue() + "?" + timestamp);
}
}
/**
* {@inheritDoc}
*/
@Override
public SpecificationNode getNode(SpecificationNode original, InjectionChain chain) {
if (original == null || chain == null) {
throw new IllegalArgumentException();
}
QName originalName = original.getQName();
if (QH_SCRIPT.equals(originalName)) {
appendQueryTimestampParameter(original, QH_SRC);
} else if (QX_SCRIPT.equals(originalName)) {
appendQueryTimestampParameter(original, QX_SRC);
} else if (QH_LINK.equals(originalName)) {
appendQueryTimestampParameter(original, QH_HREF);
} else if (QX_LINK.equals(originalName)) {
appendQueryTimestampParameter(original, QX_HREF);
} else if (QH_IMG.equals(originalName)) {
appendQueryTimestampParameter(original, QH_SRC);
} else if (QX_IMG.equals(originalName)) {
appendQueryTimestampParameter(original, QX_SRC);
}
return chain.getNode(original);
}
}
META-INF/org.seasar.mayaa.provider.ServiceProvier に InjectionResolver を追加します。
<resolver class="example.support.mayaa.ResourceTimestampSetter" />
これで Servlet からのレスポンスを見てみると、静的に埋め込んだリソースファイルすべてにファイルのタイムスタンプが、クエリ文字列として追加されます。
<img src="./../img/logo.png" />
<img src="/img/logo.png?1258000000000" />
mayaa は静的な部分に関しては一度だけコンパイルして処理をしてくれるので、自動ビルドなり一度アクセスするとき以外は、この処理は行いません。ブラウザがキャッシュをみてくれるようになることを考えると、十分な見返りがありそうです。
実際の業務ではタイムスタンプを埋め込むものは使用していないのですが、これはこれで、なかなか使えそうな気がしました。
亀さんな開発速度でいじっています。今日は HTTPS 通信をやってみました。
コード前半にカスタマイズしたコードを入れていますが、 new DefaultHttpClient() で HTTPS 通信できます。カスタマイズする必要がなければ引数なしのコンストラクタを呼び出せば問題ありません。
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.path("/login");
HttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params);
HttpPost post = new HttpPost(uriBuilder.build().toString());
List<BasicNameValuePair> postParams = new ArrayList<BasicNameValuePair>();
postParams.add(new BasicNameValuePair("id", username));
postParams.add(new BasicNameValuePair("password", password));
try {
post.setEntity(new UrlEncodedFormEntity(postParams, HTTP.UTF_8));
HttpResponse httpResponse = client.execute(new HttpHost("example.com", 443, "https"), post);
// レスポンスコードなど取得
} catch (ClientProtocolException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
AndroidManifest.xml にインターネット接続を許可することを記述します。
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Thunderbird3 RC1 が出たので、乗り換えてみましたが Growl 通知されません。うーん、と調べてみると設定エディタから以下の項目がデフォルト値でない false になっていたのが原因でした。 Thunderbird2 で利用していた Growl 周りの拡張が原因だったんでしょうか?
mail.biff.show_alert
この項目を true にしたところメール受信されるとともに Growl から通知されました。
Thunderbird3 は検索がいい感じです。
[追記]
メッセージフィルタを介して、移動したメッセージは今のところ通知されないみたいです。ひとまずコピーであれば通知されるようです。