antipop

2008-01-20

Googleで℃-uteを正しく検索する

昨年はレコード大賞最優秀新人賞を獲得し、また、紅白歌合戦への出場も果たしたことにより、℃-uteは既に国民的な存在になったといえますが、まだそれが何なのか良く知らない方も、もしかしたらいらっしゃるかもしれません。そのような方が、たとえばGoogleで「℃-ute」を検索してみた場合、おそらくは、下記のような残念な表示を見ることになると思います。

℃-ute に一致するページは見つかりませんでした。

かといって、「C-ute」で検索したところで、まともな検索結果は期待できません。SEO的に最悪なことになってしまっています。

界隈では随分前からFAQなのですが、正しくは「"℃-ute"」といった具合に、二重引用符で囲んだ状態で検索する必要があります。面倒ですよね。そこで、以下の通り、「℃-ute」と検索キーワード入力した際に、自動的に二重引用符で囲むGreasemonkeyスクリプトを作成しました。

これで℃-uteを検索しまくりですね。どうぞご利用ください。

2007-03-27

Rimoの人気/新着ページに、RimoChにチャンネルを追加するリンクを表示するGreasemonkeyスクリプト

RimoChへの、機能追加のお知らせです。

Rimo人気/新着チャンネルページに、それらのチャンネルをRimoChに追加するリンクを表示するGreasemonkeyスクリプトを作りました。下記リンクにより、スクリプトをインストールできます。インストール後には、Rimoの人気/新着ページに「RimoChに追加する」というリンクが、各チャンネル名の下部に表示されるようになります。

スクリプトのインストール後、初めてRimoの人気/新着ページに訪れた際にユーザID入力を求められますので、RimoChで利用しているはてなIDを、正しく入力してください。間違えて登録してしまうと、正しいリンクが表示されなくなりますので、その場合は、以下の画像の要領で、正しいユーザIDに修正してください。

2007-02-07

「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;
    }
})();

2006-07-05

livedoor Reader に ping を送信する Greasemonkey スクリプト

livedoor Reader が ping の受け付けを開始したとのことだけど、「はてなダイアリーのヘルプ - リンク元・Ping送信設定を変える」に見られる通り、はてなの ping 送信先は決め打ちで、かつ、LDR に対応してないので、それ Gre(ry

つかこれテキトー過ぎ。まぁ、テキトーじゃなかったことなど、これまで一度もなかったのだが!つか、これでうまくいってるのかどうか、よくわかりません><

UPDATE: なんかうまくいってない気がする。まぁどうでもいいけど。

MORE UPDATE: 編集画面の submit と同時に ping を送っちゃうと、エントリが保存されていないため、更新前のフィードが取得されちゃうようなので、画面遷移後に ping を送信するようにした。

// ==UserScript==
// @name          LDR UpdatePing
// @namespace     http://antipop.gs/ns/greasemonkey/ldr_updateping
// @include       http://d.hatena.ne.jp/*
// ==/UserScript==
//
// This script was written by Kentaro Kuribayashi
//
// Homepage: http://antipop.gs/
// Weblogs : http://d.hatena.ne.jp/antipop/ (in Japanese)
//           http://antipop.typepad.com/    (in English )
//
// Copyright (C) 2006 by Kentaro Kuribayashi
//
// This script is distributed under the CCPL by-sa
//  - See: http://creativecommons.org/licenses/by-sa/2.5/
//

(function(){
    var SCRIPT_NAME = 'LDR UpdatePing';
    var VERSION     = '0.02';
    var isAfterEdit = GM_getValue('isAfterEdit');
    var pingUri     = 'http://rpc.reader.livedoor.com/ping';

    if (!isAfterEdit && location.href.match(/http:\/\/d\.hatena\.ne\.jp\/[^\/]+\/edit.*/)) {
        $X('//input[@name = "edit"]')[0].addEventListener('click', function () {
            GM_setValue('isAfterEdit', true); 
        }, false);
    }
    else if (isAfterEdit) {
        var title = document.title.split(' - ')[0];
        var link  = location.href.match(/(http:\/\/d\.hatena\.ne\.jp\/[^\/]+\/).*/)[1];
        var call  = <methodCall>
                      <methodName>weblogUpdates.ping</methodName>
                      <params>
                        <param><value><string>{title}</string></value></param>
                        <param><value><string>{link}</string></value></param>
                      </params>
                    </methodCall>
                    ;

        GM_xmlhttpRequest({
            method  : 'POST',
            url     : pingUri,
            headers : {
                'User-Agent'   : [SCRIPT_NAME, '/', VERSION].join(''),
                'Content-Type' : 'text/xml',
            },
            data    : call.toString(),
            onload  : function (res) {
                alert(new XML(res.responseText.replace(/^<\?xml.+?\?>/, ''))..string[0]);
                GM_setValue('isAfterEdit', false); 
            },
            onerror : function (res) {
                alert(['Error', res.statusText].join(' - '));
            },
        });
    }

    // $X was originally written by cho45 (http://lowreal.net/logs/2006/03/16/1), and
    // it is distributed under the CCPL by-sa same as this script
    function $X (exp, context) {
        if (!context) context = document;
        var resolver = function (prefix) {
            var o = document.createNSResolver(context)(prefix);
            return o ? o : (document.contentType == 'text/html') ? '' : 'http://www.w3.org/1999/xhtml';
        }
        var exp = document.createExpression(exp, resolver);

        var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
        switch (result.resultType) {
            case XPathResult.STRING_TYPE : return result.stringValue;
            case XPathResult.NUMBER_TYPE : return result.numberValue;
            case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
            case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
                result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                var ret = [];
                for (var i = 0, len = result.snapshotLength; i < len ; i++) {
                    ret.push(result.snapshotItem(i));
                }
                return ret;
            }
        }
        return null;
    }
})();

未読フィード恐怖症について、あるいは、それ Greasemonkey でよくね?

ただ、RSSリーダーに慣れてくると、陥りがちな病気があるんです。

その名も「未読RSS恐怖症」です。RSSリーダーに調子に乗って大量のブログやニュースサイトを登録したことで、毎日更新通知される大量の記事に処理が追いつかなくなり、徐々にRSSリーダーの未読記事が膨れ上がり、だんだんRSSリーダーを開くのが嫌になってくるという恐ろしい(?)病気です。

ITmedia Biz.ID:“情報を読まない”情報収集術――「未読RSS恐怖症」対策

それGre(ry)

詳細については、livedoor Readerとは - はてな に掲載されている vi モードのあたりを。

とはいえ、まぁ、LDR であれば「最速インターフェース研究会 :: how many feeds do you subscribe to?」を参考にして、レートを駆使すれば、かなりの程度までどうにかなるのではないでしょうか。

僕の LDR は、毎日だいたい 1,500 から 2,000 くらい新着エントリがあって、フィードにレートをつけるのがめんどうくさくて放置してあるので、フラット表示にして、端から端まで全部、少なくともタイトルだけは眺めています。まぁ、レート管理しないでも済む程度の情報にしか触れていないってことですが。

今後はぼちぼちレートつけるようにしていきたいなぁ。

ストレスフリーの仕事術―仕事と人生をコントロールする52の法則
デビッド アレン David Allen 田口 元
二見書房 (2006/05)

2006-07-04

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