日記はScrapboxに移動しました。

  • 「Exif JSONサービス」から Exif 情報を引っ張ってきて表示したい

    , ,

    Exif JSONサービス – Ogawa::Memoranda」なんつって、画像の URL を指定すると、その画像の Exif 情報を返すなんてなステキ API が公開されたので、ぐりもんでいじってみたりしました。……が、以下のような感じで作ってみたのだけど、なんかマウスをガシガシ動かしまくって、Exif情報を出したり消したりしまくってると、Firefoxが固まる……。なんでー。
    まぁ以下、メモ掲載。

    // ==UserScript==
    // @name          Show Exif
    // @namespace     http://kentarok.org/
    // @include       *
    // ==/UserScript==
    //
    // ==Acknowledgments==
    //
    // This script appears thanks to:
    //
    // Exif JSON API - Ogawa::Memoranda
    // http://as-is.net/blog/archives/001206.html
    //
    // ==Author & Copyright==
    //
    // Copyright (C) 2007 by Kentaro Kuribayashi
    // This script is distributed under the MIT License
    //
    (function(){
    var popupId  = 'GM-Show-Exif';
    var callback = 'GM_showExif';
    var jsonp    = new JSONP;
    unsafeWindow.GM_showExif = showExif;
    document.body.appendChild($N('div', {
    id   : popupId,
    style: {
    color          : '#333333',
    border         : 'solid 1px #333333',
    backgroundColor: '#ffffb2',
    padding        : '1em',
    display        : 'none',
    position       : 'absolute',
    },
    }));
    $A(document.images).forEach(function (image) {
    var jsonpURI = ['http://as-is.net/exif/json?url=', image.src, '&callback=', callback].join('');
    image.addEventListener('mouseover', function (event) {
    jsonp.executeRequest(jsonpURI);
    $(popupId).style.display = 'block';
    $(popupId).style.top     = (event.pageY + 10) + 'px';
    $(popupId).style.left    = (event.pageX + 10) + 'px';
    }, false);
    image.addEventListener('mouseout', function () {
    jsonp.removeRequest(jsonpURI);
    $(popupId).style.display = 'none';
    }, false);
    });
    function showExif (exif) {
    var lines = [];
    for (var p in exif) {
    lines.push(['<strong>', p, ':</strong> ', exif[p], '<br>'].join(''));
    }
    $(popupId).innerHTML = lines.join("\n");
    };
    function JSONP () {
    this.requests = {};
    this.head = document.getElementsByTagName('head')[0];
    this.executeRequest = function (jsonpURI) {
    var script         = document.createElement('script');
    script.type    = 'text/javascript';
    script.charset = 'utf-8';
    script.src     = jsonpURI;
    this.head.appendChild(script);
    this.requests[jsonpURI] = script;
    };
    this.removeRequest = function (jsonpURI) {
    this.head.removeChild(this.requests[jsonpURI]);
    delete this.requests[jsonpURI];
    };
    }
    function $ (id) {
    return document.getElementById(id);
    }
    function $A (arg) {
    var array = [];
    for (var i = 0, l = arg.length; i < l; i++) {
    array.push(arg[i]);
    }
    return array;
    }
    function $N (tag, props, children) {
    var element = document.createElement(tag);
    for (var k in props) {
    var v = props[k];
    if (k == 'style') {
    for (var p in v) {
    element.style[p] = v[p];
    }
    }
    else if (k == 'event') {
    v.forEach(function (e) {
    element.addEventListener(e.name, e.callback, e.flag);
    });
    }
    else if (k == 'class') {
    element.className = v;
    }
    else {
    element.setAttribute(k, v);
    }
    }
    if (children instanceof Array) {
    children.forEach(function (child) {
    if ((typeof(child) == 'string' || child instanceof String)) {
    element.appendChild(document.createTextNode(child));
    }
    else {
    element.appendChild(child);
    }
    });
    }
    return element;
    }
    })();
    
  • Asamasi ID Filter その後

    ,

    以前晒した “Asamasi ID Filter” について、amazon の商品ページの URL がなんか変更されてるなーというのは知ってたけど、このスクリプトを自分で使ってる分には困る場面があんまりなかったので放置してたのですが、どうにかしろよ!といわれたので、激しくいまさらですが対応しました。

    // ==UserScript==
    // @name          Asamasi ID Filter
    // @namespace     http://antipop.gs/ns/greasemonkey/asamasiidfilter
    // @description   convert malicious things to something good
    // @include       *
    // @exclude       http://www.amazon.co.jp/exec/obidos/tg/browse/-/896244/*
    // @exclude       http://www.amazon.co.jp/exec/obidos/tg/browse/-/10667101/*
    // @exclude       http://www.amazon.co.jp/exec/obidos/tg/browse/-/927712/*
    // ==/UserScript==
    (function() {
    var ids = new Array(
    'foo-22',
    'bar-22',
    'baz-22',
    //      'antipop-22',
    );
    var links   = document.links;
    var pattern = '^http://(?:www.amazon.co.jp/(?:exec/obidos/ASIN/|o/ASIN/|gp/product/)|d.hatena.ne.jp/asin/)([^/]+)/?.*';
    var regexp  = new RegExp(pattern, 'i');
    for (var i = 0, length = links.length; i < length; i++) {
    if (links[i].href.match(regexp)) {
    links[i].href = new Array(
    'http://www.amazon.co.jp/exec/obidos/ASIN/',
    RegExp.$1,
    '/ref=nosim/',
    ids[Math.floor(Math.random() * ids.length)]).join('');
    }
    }
    })();
    
    JavaScriptビジュアル・リファレンス
    シーズ
    エムディエヌコーポレーション (2004/11)
    売り上げランキング: 29,745
  • livedoor Reader から、ショートカットキー一発ではてなブックマークにぶくまする greasemonkey スクリプト

    ,

    2006-04-28 追記。
    以下のスクリプトの機能に追加して、del.icio.us へも対応したものを、yoko さんが作成しました。

    del.icio.us ユーザ、あるいは、はてぶと del.icio.us 兼用ユーザは、そっちを使う方がいいと思います。

    まぁ見出しがすべてを物語っていて、それ以上の説明の余地がないのですが、ともあれ livedoor Reader で読んでいるエントリを、はてなブックマーク AtomAPI を通して、ショートカットキー一発ではてなブックマークにぶくまするための greasemonkey スクリプトを書きました。
    以下、設定方法。

    1. 下記のスクリプトをエディタかなんかに貼り付けて、はてなのユーザ名、パスワードを記入
    2. Blogging in the wind: WSSE for JavaScript” から wsse.js をダウンロードしてきて、どこか適当な場所におき、その URL を記入
    3. あらかじめ設定されているショートカットキーが “b” ってのがイヤなら、なんか適当に変更
    4. 以上が済んだら、そのファイルを保存して、それを Firefox で開くと現れる、”Install” ボタンを押してインストール

    以上の設定・インストールが成功したら、以下の画像ような感じで、上で設定したショートカットキーによりプロンプトが出てきて、livedoor Reader でエントリを読みながらはてなブックマークにぶくまできるようになります。
    WS000000
    少しわかりにくいのですが、ショートカット “p” でピンが立つ対象がぶくま対象のエントリです。
    プロンプトを空のまま OK するか、あるいはキャンセルすると、ブックマークしません。また、ブックマークが成功したら “201 – Created” とかなんとか alert が表示されます。
    外部 JavaScript ライブラリを持ってこなきゃならなかったりしてちと導入がめんどうだけど、わりと便利かも。

    // ==UserScript==
    // @name        livedoor Reader 2 Hatena::Bookmark
    // @description bookmark the entry gets your attention on the spot
    // @namespace   http://antipop.gs/ns/greasemonkey/ldr_post2hatebu
    // @include     http://reader.livedoor.com/reader/*
    // ==/UserScript==
    (function(){
    // 設定
    // wsse.js は http://rvr.typepad.com/wind/2005/07/wsse_for_javasc.html から取ってきてぉ><ノ
    var username    = 'hatena_id';
    var password    = 'hatena_password';
    var shortCutKey = 'b';
    var libWSSE     = 'http://yourdomain/path/to/wsse.js';
    var postURI     = 'http://b.hatena.ne.jp/atom/post';
    var w = unsafeWindow;
    var _onload = w.onload;
    function ATOM (username, password) {
    this.username = username;
    this.password = password;
    }
    ATOM.prototype = {
    post: function (postURI, permalink, comment) {
    var requestBody =
    <entry xmlns="http://purl.org/atom/ns#">
    <title>dummy</title>
    <link rel="related" type="text/html" href={permalink} />
    <summary type="text/plain">{comment}</summary>
    </entry>;
    GM_xmlhttpRequest({
    method: 'POST',
    url: postURI,
    headers: {
    'Accept': 'application/x.atom+xml, application/xml, text/xml, */*',
    'X-WSSE': w.wsseHeader(this.username, this.password),
    },
    data: requestBody.toString(),
    onload: function (details) {
    alert([details.status, details.statusText].join(' - '));
    }
    });
    }
    };
    var onload  = function () {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src  = libWSSE;
    document.getElementsByTagName('head')[0].appendChild(script);
    var atom = new ATOM(username, password);
    w.Keybind.add(shortCutKey, function(){
    var item = w.get_active_item(true);
    if (item) {
    var permalink = item.link.replace(/#/, '%23');
    var comment   = w.prompt('comment for "' + item.title + '"');
    if (!comment)
    return;
    else
    atom.post(postURI, permalink, comment);
    }
    });
    };
    w.onload = function(){
    _onload();
    onload();
    };
    })();
    Greasemonkey Hacks (Hacks)
    Greasemonkey Hacks (Hacks)

    posted with amazlet on 06.04.27
    Mark Pilgrim
    Oreilly & Associates Inc (2005/11)

  • Shibuya.js 結成

    id:secondlife さんを中心に、Shibuya.pm インスパイヤリスペクトShibuya.js が結成されたとのこと。

    以前、他の言語はコミュニティがあって、勉強会なりカンファレンスなりいろいろ開かれて活発だけど、去年から見直されてきた JavaScript はそういうコミュが無くて悲しいよねという話をしました。その時、コミュを作ったら参加するよというありがたい言葉を数人からもらったので思い切って作ってみました!ネーミングは Shibuya.pm リスペクト。

    Shibuya.js は主に東京近辺の JavaScript プログラマによる非営利団体です。各個人のプログラミングスキルの向上を目的に、勉強会ならびにカンファレンスの開催、情報交換などを行うコミュニティを目指しています。
    Shibuya.jsJavaScript を利用し、スキル向上を望む方であればどなたでも無料で参加できます。

    田舎者なので、例によって他のイベント同様、直接には参加とかまったくできそうにないのがアレなのですが、期待しまくりですね。

  • Hatena Rolling を update した

    機能もコードも非常にショボくてだめだめな感じの拙作 greasemonkey スクリプトの Hatena Rolling ですが、自分的には非常に便利で、愛用していたりします。先日、サーバが死んだことにともない、そのスクリプトも消え失せてしまってたかと思いきや、Userscripts.org: Hatena Rolling に残っているのを発見し、ちと安堵したりもしました。

    さて、その間にもみんな大好きはてなさんは次々と新しいサービスをリリースしまくりで、Hatena Rolling を新サービスに対応しる! との声も聞かれたり聞かれなかったりしたので、10 文字ぐらい追加すればオケなはずってんでやってみると、あれ、うまくいかないなぁ……という事態に。

    そのうまくいかなかったところってのは、はてなマップのユーザごとのページでのリンクボックス表示(僕とこだとはてなマップ – antipopの地図)。元々は for (var i in obj) { ... } としてまわしていたのですが、はてなマップprototype.js を利用している関係で、prototype.js が Object を拡張しちゃってて、単にそゆふうにまわすだけではうまくいかないわけです。困りました。

    そこで、ニート言語の使い手に泣きついたら、さくっと解決してくれました。さすが過ぎ。要は、「最速インターフェース研究会 :: prototype.jsのObject汚染を回避する方法」で述べられている通り、あるオブジェクトが持つプロパティが、元々そのオブジェクトが持っていたものなのか、あるいは prototype.js がやるように、あとから拡張されたものなのかを、hasOwnProperty メソッドで判別してやればいい、と。たとえば、以下のような感じ。

    for (var i in obj) {
    // もともと持っているプロパティかどうか調べ、
    // 後付されたものならスキップ
    if(!obj.hasOwnProperty(i)) continue;
    // 以下、通常の処理
    ...
    }

    とまぁ、そんなこんなで、Hatena Rolling を update しましたよ、というお話でした。

  • JSAN (JavaScript Archive Network) 本格始動

    JavaScriptCPAN というべき JSAN (JavaScript Archive Network) が、CPAN シェルに似たインストール法を提供する JSAN モジュールが CPAN にあがったり、また、ライブラリも整備され始める等、本格始動したようです。

    てな感じで、CPAN に馴染みのある Perl 使いのひとにはすっと入りやすい仕組みになっているようですが、他の言語スキーのひと的にはどうなんだろう……。ともあれ要注目、というか普通に便利ちっくなので、JS ハカーな方々ががんがん便利ライブラリをあげてくれると素晴らしいなぁと思います。

  • もんたメソッド祭り

    Blog Hackers Conference 2005 で話題になったという「もんたメソッド」について、さっそく以下のもんたメソッドによるプレゼン作成ツールが公開されています。

    • 最速インターフェース研究会 :: もんたメソッドなプレゼン作成ツール
    • もんたメソッドを組み込む JavaScript を公開。

      おぉ、超お手軽だー! などといいつつ、IRC で突如「もんたメソッド JavaScript 作成プチ祭り」が勃発したりりしつつ、僕もちとやってみました。てゆか、すでにお気づきの通り、このエントリで実験してみてるわけですがw

      ソースは以下の感じ。

      こんな感じで使います。

      var config = {
      'bgColor'     : '#000000',
      'targetNodes' : [{
      'element'   : 'strong',
      'class'     : ''
      },
      {
      'element'   : 'div',
      'class'     : 'more'
      }]
      };
      var monta = new MontaMethod(config);
      monta.init();

      「続きを読む」と、Blog のユーザビリティ」にからめていえば、「続きを読む」でクリックする手間を閲覧者に強いるよりもむしろ、どうせクリックさせるならもんたメソッドにしてしまうのがいま一番アツイ Blog 作法です! たとえば以下のように隠します。いかにも重要そうだ!

      otsune さんによれば「単純に文章の最初に全体の概要を簡潔に書くという英語圏でよくある習慣が、日本だと一般的ではな」く、とすれば、そのような習慣を前提としない Blog で「続きを読む」機能を使うのは、単なる舶来物を無意味にありがたがるのといっしょ、つまり猿真似なのでは? そして、そういう批判って、かつての Blog 論争の時に、m.e.s.h. を攻撃する時に用いられた論法だったりしますね。その辺、あのとき批判してたひとは大丈夫なのかしらん、とか思ったよ。

      というかそんなことよりもむしろ、「続きを読む」をわざわざクリックして、ページ遷移をする手間をかけたところで、たいていは、わざわざクリックしてまで読むようなことなど書かれてはない。

  • 続・リファラを残さずにリダイレクトする

    以前「リファラを残さずにリダイレクトする」というエントリで述べた、リファラを残さないリンクの作り方について、せきむらさんに、より簡単な方法を教えていただきました。

    なるなる。window.open で新しい window を開いて、そこに meta 要素書き出し & refresh する、と。CGI 経由でリダイレクトするよか、楽ちんですね。

    通常は、リファラを送信して問題になるということはあんまりないかと思うのですが、以前そのようなことを試したのは、Web 上から参照できる IRC のログヴューワ内のリンクをどうにかしたいということで、つーのは、チャンネルのメンバーしか見ることができないよう認証をかけているのですが、認証のかかったところからのリファラがあると、リンク先のひとがヤな思いをすることもあるかなぁといった事情で、そゆことをやっておったわけです。同様の問題を抱えている方は、この方法を試してみるといいかもしれませんね。

  • greasemonkey で、どこでもあれこれポップアップ

    この Blog でも使わせていただいている、ありみかさとみさん作の「あれこれポップアップ」。どんなものかってーと…。

    ウェブページ (HTML) にて、マウスポインタの下にある任意の箇所(要素)の情報(属性値)あれこれをポップアップに出す、 JavaScriptCSS のセット。 Web サイトの製作運営者向けの一種の「素材」。(中略)

    (中略)

    多くの典型的なブラウザでは、 title 属性に何か書いてある要素部分にマウスポインタをかざすと、その title 属性値がツールチップとしてポップアップしますよね。あれのゴージャス版と思ってもらえればよいです。典型ブラウザ本来のポップアップとの違いは、ポップアップするのが title 属性だけではない点。それと自分で言うのも何だけど、見てくれがカコイイ事(笑)

    あれこれポップアップ

    すでにお気づきの方も多いかと思いますが、たとえば上の引用箇所にマウスポインタをかざすと、どういうものかわかると思います。ここでは、この「あれこれポップアップ」を、Web サイトの製作運営者向けの一種の「素材」としてのみならず、Firefox の拡張 greasemonkey を用いることによって、ユーザサイドでも使えるようにしてみました。

    といってもたいしたことをするわけでもなく、「川o・-・)<2nd life – bookmarkletの文字数制限を無くす」で述べられている方法を greasemonkey に適用し、ページロード時に外部 JavaScript(この場合は、あれこれポップアップ本体の ArekorePopup.js)と、画像の指定等を記述したスタイルシートを読み込むようにしただけ。

    greasemonkey をすでにインストールしているならば、上記リンク上で右クリックして出てくるコンテキストメニューに、”Install User Script…” というメニューが出てくると思います。それを選択してインストール。これだけでオケ。たとえば、以下ははてなダイアリー日記にて動作している例。なかなかかっけーよね。

    どこでもあれこれポップアップ

    詳しくは先述の「あれこれポップアップ」のドキュメントを見ていただくとして、たとえば title 属性や cite 属性が指定されている要素の上にマウスポインタをかざすと、リッチなポップアップが出てくるようになります。

    ここで注意。上にリンクした user.js は、この Blog を置いてあるサーバのドメイン上の JavaScript ファイルを、greasemonkey スクリプトが実行される場所の権限で読み込んで動作させることになります。よって、上の user.js をそのままいじらずにインストールすると、僕が悪いことをしようと思えばなんだってできちゃうということになります。じゃーどうすりゃいいのよ、ってことになるわけですが、常用しようという奇特な方は、その場でインストールするのではなく必ずダウンロードして user.js の該当箇所を適宜書き直してインストールし、また、あれこれポップアップ一式を自分の利用しているサーバにでもおくようにしてください。セキュリティ上の問題に加え、あれこれポップアップは設定項目が多岐にわたりますので、自分の好みに合わせて使う方がよりよいとも思います。

    あと、遠くにあるサーバにおくよりも、ローカルにおいてそこから読み込めばいいじゃないか(file: スキームで)、ってんでちとやってみたのですが、ローカルファイルをリモートサイトの権限で読み込むことになり、セキュリティ上のエラーが出てだめでした。回避できるのかもしれないけど、めどいので放置。まぁそんなこんなで、単に外部 JavaScript をざくざく読み込むだけでも、あれこれ便利に使えたりする greasemonkey マンセーということで。

  • 日本の Ajaxer(何)と、ガイシュツな「問題」について

    なんというか、意味のない愚痴みたいなことですが。

    たとえば “Ajax: 99% Bad” という、ヤコブ・ニールセンさんの “Flash: 99% Bad” を文字った Ajax 批判エントリを読むと、なんかガイシュツな話をしてるなぁとか思うわけです。というのは、Ajax という言葉が作られて 10 日もたたないうちに、すでに「nazonoDiary – AjaxでもURIで状態を示せるようにお願いします。」というエントリに見られる通り、一般的な Flash アプリケーションが、ある状態を示す URL によって状態の復元が可能であるようには作られていないことが多い(単に一般的にそうなってないというだけで、可能ではあるようです)ことのユーザビリティ的問題を挙げて、Ajax に対する問題提起が行われています。

    その提起に対して、mala さんが即日「location.hashを使ったセッション復元」というエントリを起こし、Google Maps が行っている実装の検討とともに、それとは別に location.hash を用いた状態保存方法を紹介し、問題解決の糸口を示しています。また、その方法ではマルチバイト文字を扱う上で不都合が生じることがあるということで、location.href から取得することで文字化けを回避する方法を示しています

    それを受けて nazoking さんの複合検索サービス「ご一緒にポテトはいかがですか?」に、検索結果ページを示す URL を扱えるよう改良がなされ、上述の “Ajax: 99% Bad” にて提起されている問題うち、URL の問題については、上記の通り一応の解決をすでに見ており、そのため、日本語のリソースで「Ajax は状態保存できないからうんこだ」などと書けば、「まぁ落ち着けよ。Ajax がどーのとかいうなら、せめて「最速インターフェース研究会」ぐらいはチェキっとこうな」と諭されることになるわけです。

    しかし、考え直してみるに、単に僕が知らないだけで同様の問題は世界中の各言語で議論されていて、たまたまこの ”Ajax: 99% Bad” が目にとまり、「ガイシュツ」呼ばわりしているのかもしれないなぁ、と思いました。しかし、”AjaxPatterns” なる、Ajax に関するかなり高度な Tips 等がまとめられているように見受けられる Wiki を拝見しても、”UniqueURLs” に関する同様の問題提起はなされているものの、ソリューションとしては Google Maps が挙げられているのみです。そのようなことから、上述した nazoking さんと mala さんのやりとりで展開されたような方法は、単に知られていないということなのではないか、と思いました。

    とはいえ、location.hash を用いた状態保存・復元という手法は mala さんのオリジナルというわけではなく、たとえば TiddlyWiki においてすでに試みられています。しかし、Tiddly Wiki の実装では、Google Maps と同じく、状態保存用の URL へのリンクがしめされ、それをクリックするなりコピーするなりして、当該 URL を取得しなければならず、ひとつ手間がかかります。先に挙げた nazoking さんの「ご一緒にポテトはいかがですか?」においては、URL が自動的に変遷していき、そのような手間は必要ありません。

    要するになにがいいたいかというと、本件問題についてはすでに解決方法、あるいは、よりよいと思われるアイディアが提示されているのにも関わらず、それが言語を異にするという理由でうまいこと伝わっておらず、相も変わらず “Ajax: 99% Bad” などと無知に基づく因縁をつけられているような事態になっているのは、見ていてなんだか歯痒いなぁと思うわけです。やっぱ英語で書かないと読まれないし、読まれなければいいアイディアであっても広まらず、なんだか悲しいことになるのだなぁ、と。じゃぁおまえが英語で書けばいいじゃないか! と、いまこの文章を読んでいるあなたは思ったでしょうが、僕にそんな能力があるわけないじゃないか! そんなことできるなら最初からやってるよ! つーか、そんな説教をたれてくださる英語ペラペラなあなたがまずは “UniqueURLs – AjaxPatterns” に上記のような内容を書いて、世界に広めてくださいよ! と逆ギレ気味に、このエントリおしまし。