JAMADAM.COM

RSS Subscribe to my RSS feed

Stripe Calendar

Sep, 2010
Aug 1213141516171819202122232425262728293031 Sep 123456

Entry: ついったらータグs API(仮)

ついったらータグs API(仮)

Initial post: 2009.11.03 | Last modified: 2009.11.05

「ついったらータグs API(仮)」は、ついったらータグs(仮)のデータベースへの問い合わせをアプリケーションから利用するためのインターフェースを提供します。ついったらータグs(仮)のデータベースにはTwitterの各ユーザーページに対する、はてなブックマークタグの情報を格納しています。本APIを利用することで、任意のユーザーに対するタグ付けの情報はもちろんのこと、任意のタグがどのユーザーに適用されているかという情報を取得することができます。また、対象となるデータベースとして、独自のルールでタグ文字列の正規化を行ったテーブルを対象にすることもできます(任意)。

 全てのAPIは下記のURLで提供されます。

http://jamadam.com/th/

 現在サポートされている操作は、タグ-ユーザー検索(パラメータt=api/tag.json)とユーザー-タグ検索(パラメータt=api/user.json)です。いずれもレスポンスとしてJSONを返します。また、クエリーとしてcallbackを渡すことでJSONPを受け取ることもできます。

 

タグ-ユーザー検索

任意のタグ文字列を指定することで、このタグがつけられたユーザーのスクリーンネームのリストを得ます。

パラメータ

  • t (必須)
    "api/tag.json"を指定します。
  • str (必須)
    任意のタグを指定します。
  • nn (オプション)
    正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。

 リクエスト例

/th/?t=api/tag.json&str=it

レスポンス例

{"result":[
{"user":"takapon_jp","occurance":"2"},
{"user":"m_kumagai","occurance":"1"},
{"user":"huehara88","occurance":"1"}
]}

 ※実際はレスポンスに改行は含まれません。

 

ユーザー-タグ検索

任意のスクリーンネームを指定することで、このユーザーに付与されたタグの一覧を得ます。

パラメータ

  • t (必須)
    "api/user.json"を指定します。
  • str (必須)
    任意のスクリーンネームを指定します。
  • nn (オプション)
    正規化されたテーブルを使用しないためのパラメータです。1を指定すると、はてなブックマークに登録された通りのタグ文字列が対象となります。デフォルトは0です。

リクエスト例

/th/?t=api/user.json&str=jamadam

レスポンス例

{"result":[
{"tag":"twitter","occurance":"38"},
{"tag":"音楽","occurance":"9"},
{"tag":"有名人","occurance":"4"},
{"tag":"webサービス","occurance":"4"}
]}

※実際はレスポンスに改行は含まれません。

 

実装例

ユーザー-タグ検索を利用してタグ一覧を取得するPerlコードは以下のようになります。

use LWP::UserAgent;
use JSON::XS;

my $tags = _twitterer_tags('jamadam');
foreach my $entry (@$tags) {
    print($entry->{tag});
    print(":");
    print($entry->{occurance}."回");
    print("¥n");
}

sub _twitterer_tags {
    my $user = shift;
    my $ua = LWP::UserAgent->new;
    my $res = $ua->get('http://jamadam.com/th/?t=api/user.json&str='. $user);
    my $obj = decode_json $res->content();
    return $obj->{result};
}

 

Entry: twilogで日本語ドメイン名にリンクを張るGreasemonkeyスクリプトを作った

twilogで日本語ドメイン名にリンクを張るGreasemonkeyスクリプトを作った

Initial post: 2009.10.19 | Last modified: 2009.10.19

開発ネタがぜんぜん思いつかないのでGreasemonkeyスクリプトをもうひとつ作った。

twilogというサービスで、つぶやきの文中の日本語ドメインURLにリンクが張られていないようなので、勝手に張ってくれるGreasemonkeyスクリプトです。

accessible_idnをインストール

// ==UserScript==
// @name           accessible_idn
// @namespace      http://jamadam.com/blog/
// @description    This makes IDN URLs accessible.
// @include        http://twilog.org/*
// ==/UserScript==

(function() {
var tltexts = document.getElementsByClassName('tl-text');
for (var i = 0; i < tltexts.length; i++) {
    var tmp = tltexts[i].innerHTML;
    for (var j = 0; j < tmp.length;) {
        if (tmp.substr(j, 3) == '<a ') {
            var closepos = tmp.indexOf('</a>', j);
            if (closepos == -1) {
                break;
            } else {
                j =  closepos + 4 + 1;
                continue;
            }
        }
        if (tmp.substr(j, 2) == 'ht' && 
            tmp.substr(j, 8).match(/^https?:¥/¥//)) {
            var end = tmp.substr(j).search(/¥s| |$/);
            var url = tmp.substr(j, end);
            var gen = '<a href="%1">%2</a>'.replace('%1', url)
                                           .replace('%2', url);
            tmp = tmp.substr(0, j) + gen + tmp.substr(j + end);
            continue;
        }
        
        j++;
    }
    
    tltexts[i].innerHTML = tmp;
}
})();

 前回のネタよりちょっと難しかった。既設リンクをスルーする仕組みが多少いい加減。

Entry: 誤ってエンコードされたIDNを直すGreasemonkeyスクリプトを作った

誤ってエンコードされたIDNを直すGreasemonkeyスクリプトを作った

Initial post: 2009.10.18 | Last modified: 2009.10.18

開発ネタがぜんぜん思いつかないのでGreasemonkeyスクリプトでもやってみようと思った。

世の中のWEBシステムの多くは国際化ドメイン名(IDN)に対応していないと最近気づいた。例えばTwitter検索の結果とかにhttp://日本語.jp/とあっても飛び先がエラーになるのはサーバープログラムが「日本語」を誤ってパーセントエンコードしているため。このリンクを正しく動作させるためにドメイン名をデコードするGreasemonkeyスクリプトを作りました。

// ==UserScript==
// @name           idn_fixer
// @namespace      http://jamadam.com/blog/
// @description    This fixes wrongly percent encoded domains in hrefs.
// @include        *
// ==/UserScript==

(function() {
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
    var pos1 = links[i].href.indexOf('//', 0) + 2;
    var pos2 = links[i].href.indexOf('/', pos1);
    var domain = links[i].href.substr(pos1, pos2 - pos1);
    if (domain.indexOf('%') != -1) {
        links[i].href = decodeURI(links[i].href);
    }
}
})();

idn_fixerをインストール

初めてのGreasemonkeyスクリプトなので作法とか間違ってるかも知れません。

Entry: 開発小ネタその2(改)

開発小ネタその2(改)

Initial post: 2009.10.14 | Last modified: 2009.10.14

開発小ネタ集その2のネタ、なんか違和感あると思ったらゼロがなかったんだ。という訳で書き直した。66進数。縮.jpは差し詰め、1945進数か。

use strict;
use warnings;

### 文字リスト
our @ascii = qw(
    0 1 2 3 4 5 6 7 8 9
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    _ + - =
);

my $num1 = str2num("0");     ### 0
my $num2 = str2num("=");     ### 65
my $num3 = str2num("10");    ### 66
my $num4 = str2num("A359");  ### 10363263
my $str = num2str(10363263); ### A359

### --------------
### 文字列を数値に変換
### --------------
sub str2num {

    my $num = 0;
    my @id_array = split(//, $_[0]);
    @id_array = reverse(@id_array);

    for (my $i = 0; $i < scalar @id_array; $i++) {
        for (my $j = 0; $j < scalar @ascii; $j++) {
            if ($ascii[$j] eq $id_array[$i]) {
                $num += (scalar @ascii ** $i) * $j;
                last;
            }
            if ($j == scalar @ascii - 1) {
                return;
            }
        }
    }

    return $num;
}

### --------------
### 数値を文字列に変換
### --------------
sub num2str {

    my $sho     = int($_[0] / scalar @ascii);
    my $upper   = ($sho > 0) ? &num2str($sho) : "";
    my $current = $ascii[($_[0] % scalar @ascii)];
    return $upper. $current;
}

Entry: GreaseMonkey用pbtweetでIDNを正常動作させる

GreaseMonkey用pbtweetでIDNを正常動作させる

Initial post: 2009.10.12 | Last modified: 2009.10.12

縮.jpがFirefoxのGreaseMonkyスクリプト「pbtweet」のv1.4.10 GreaseMonkey 005810で動作しなかったので、pbtweet側を直してみた。

pbtweet_revamped.user.js

[2009.10.12追記] 本家に取り込んで頂いたのでこちらから最新版をDLするといいと思います。ちなみに、同様の現象はTwitter本家の検索結果でも起きていて、pbtweetの最新版を使うとこれを回避できます。

231-236行に下記を追加。

//jamadam added below
var links = 
    entry[i]
        .getElementsByClassName('entry-content')[0]
        .getElementsByTagName('a');
for (var cnt = 0; cnt < links.length; cnt++) {
    links[cnt].href=decodeURI(links[cnt].href);
}
// jamadam added above

 

うちだけの問題かもしれないけど、Mac版Firefox3.5.3のJavascriptでinnerHTMLを取得した場合、子要素のaタグのhref属性が、パーセントエンコードされてしまっている。上記ではこの現象をキャンセルしている。

HTML

<body id="main">
<a title="縮" href="http://縮.jp/">http://縮.jp/</a>
</body>

Javascript

var hoge1 = document.getElementById('main').innerHTML;
var hoge2 = document.getElementById('main').getElementByTagName('a')[0].href;
alert(hoge1);
alert(hoge2);

Firefoxの場合

<a title="縮" href="http://%E7%B8%AE.jp/">http://縮.jp/</a>
http://縮.jp/

Safariの場合

<a title="縮" href="http://縮.jp/">http://縮.jp/</a> 
http://縮.jp/

 

 

Entry: 開発小ネタ集その2 - 任意の文字で数値を表現したりその逆だったり

開発小ネタ集その2 - 任意の文字で数値を表現したりその逆だったり

Initial post: 2009.10.10 | Last modified: 2009.10.10

開発小ネタ集その2。任意の文字の組み合わせで数値を表現したり、数値から文字に戻したりするPerlスクリプト。暗号化ではありませんので注意。str2numはもっといい方法ないかなあ。

use strict;
use warnings;

### 文字リスト
our @ascii = qw(
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
    1 2 3 4 5 6 7 8 9 0 _ + - =
);

my $num1 = str2num('a'); ### 1
my $num2 = str2num('='); ### 66
my $num3 = str2num('aa'); ### 67
my $num4 = str2num('aabcae'); ### 1271895443
my $str = num2str($num4);     ### aabcae

### --------------
### 文字列を数値に変換
### --------------
sub str2num {

    my $hash  = shift;
    my $num = 0;

    my @id_array = split(//, $hash);
    @id_array = reverse(@id_array);

    for (my $i = 0; $i < scalar @id_array; $i++) {
        for (my $j = 0; $j < scalar @ascii; $j++) {
            if ($ascii[$j] eq $id_array[$i]) {
                $num += (scalar @ascii ** $i) * ($j + 1);
                last;
            }
            if ($j == scalar @ascii - 1) {
                return;
            }
        }
    }

    return $num;
}

### --------------
### 数値を文字列に変換
### --------------
sub num2str {

    my $idx    = $_[0] - 1;
    my $result = '';
    my $sho    = int($idx / scalar @ascii);

    if ($sho > 0) {
        $result .= &num2str($sho);
    }

    return $result. $ascii[($idx % scalar @ascii)];
}

Entry: Perlで差分バックアップスクリプト

Perlで差分バックアップスクリプト

Initial post: 2009.06.24 | Last modified: 2009.08.15

[2009-08-15] バキューム機能つけた

2006年にpdumpfsっぽいPerlスクリプトを書いた。glastreeの存在は知っていたけど、敢えて自分で作ってみた。それ以来、仕事でもプライベートでも重宝していたんだけど、最近問題発生。職場のファイルサーバーには百万越えのファイル数があって、毎日数万の差分が発生し、バックアップに5時間かかっていた。こいつはまずいということで手直しした。結果、取りあえず、自宅では以前の10倍高速になった。

ちなみに、今回はglastreeのソースを追って参考にしてみたらほぼ同じ内容になってしまったんだけど、glasstreeとの違いは下記のとおり。

  • バックアップの管理が日付に依存しないので任意の頻度(例えば2時間おきとか)で実行できる
  • コピーされたログを残す
  • コアモジュール以外に依存しない
  • 差分がない場合はバックアップを自動削除

ログが残るってのはなかなか便利です。差分リスト=作業履歴なので。

使い方

perl ddump.pl [バックアップ対象] [保存場所]

バックアップポイントの数の上限を指定。あぶれた分は古い方から削除される。

perl ddump.pl --vacuum=400 [バックアップ対象] [保存場所]

ソース

use strict;
use warnings;
use utf8;
use English;
use Getopt::Long;
use File::stat;
use File::Copy;
use File::Path;

binmode(STDIN,  ":utf8");
binmode(STDOUT, ":utf8");
binmode(STDERR, ":utf8");
use open IO  => ":utf8";

use vars qw(%QUERY %stat_idx); # 設定
use vars qw($log $copy_count $file_count $symlink_count); # ログ関係変数
use vars qw($tgt_path $newdir $olddir); # ディレクトリ

main();

sub main {
    %stat_idx = ('mode' => 2, 'size' => 7, 'utime' => 9);

    %QUERY = ('mode' => 1, 'size' => 1, 'utime' => 1);
    GetOptions(\%QUERY, 'mode=s', 'size=s', 'utime=s', 'vacuum=i');

    ### 引数の整備と検証
    $ARGV[0] = &fixPath($ARGV[0]);
    $ARGV[1] = &fixPath($ARGV[1]);
    -d $ARGV[0] or die "$ARGV[0] not found";
    -d $ARGV[1] or die "$ARGV[1] not found";
    ### 引数を分離
    ($tgt_path, my $tgt_dir) = ($ARGV[0] =~ m!(.*)?/([^/]+)$!);

    if (opendir(DIR, "$ARGV[1]")) {
        my @dirs = sort grep{ -d "$ARGV[1]/$_" && /^\d+$/} readdir( DIR );
        close(DIR);
        ### 古いディレクトリを削除
        if ($QUERY{vacuum}) {
            for (my $i = 0; $i < $#dirs - $QUERY{vacuum}; $i++) {
                #print $dirs[$i] . " ";
                rmtree("$ARGV[1]/$dirs[$i]");
            }
        }

        ### 前回バックアップの時刻ディレクトリ名を取得
        $olddir = "$ARGV[1]/". (pop(@dirs) or '');
    }

    ### 時刻ディレクトリの作成
    my ($sec, $min, $hour, $mday, $mon, $year) = localtime($BASETIME);
    my $new_time =
        sprintf('%04d%02d%02d%02d%02d%02d', $year+1900, $mon+1, $mday, $hour, $min, $sec);
    $newdir = "$ARGV[1]/$new_time~";
    umask 0;
    mkdir($newdir, 0755) or die "cannnot make $newdir";

    ### ログファイル生成
    open($log, ">>$newdir/ddumplog.txt") or die "cannnot make log file";
    print $log <<EOF;
-----------------------------------------------------------------
-- ddump -- $new_time
-----------------------------------------------------------------
EOF

    ### ログ記録用変数
    $copy_count = 0;
    $file_count = 0;
    $symlink_count = 0;

    ### バックアップ
    &backup($tgt_dir);

    ### ログファイルに結果出力
    print $log "$copy_count/$symlink_count/$file_count (Copyed/symlink/Total)\n";
    print $log (time() - $BASETIME). "sec passed\n";
    close($log);

    if (! $copy_count) {
        ### コピーなしならディレクトリ削除
        rmtree($newdir);
    } else {
        ### ファイル名確定
        rename($newdir, substr($newdir, 0 ,length($newdir) - 1));
    }
}

### --------------------------------------------
### 再帰的ディレクトリバックアップ
### --------------------------------------------
sub backup {

    my $cwd = shift;
    my $tgt_cwd = "$tgt_path/$cwd";
    my $old_cwd = "$olddir/$cwd";
    my $new_cwd = "$newdir/$cwd";

    ### ディレクトリの作成
    &copydirstat($tgt_cwd, $new_cwd);

    ### カレントディレクトリ内の全ファイルを配列に格納
    opendir(DIR, $tgt_cwd) or return 0;
    my @files_and_dirs = readdir(DIR);
    close(DIR);

    my @files = grep { -f "$tgt_cwd/$_" or -l "$tgt_cwd/$_" } @files_and_dirs;

    ### @files_and_dirsのうち、ファイルを処理
    foreach my $a_file (@files) {
        my $old_file = "$old_cwd/$a_file";
        my $new_file = "$new_cwd/$a_file";
        my $tgt_file = "$tgt_cwd/$a_file";
        my $command = 1; # 0: nothing, 1: hard link 2: copy

        if (-l $tgt_file) {
            symlink(readlink($tgt_file), $new_file);
            $symlink_count++;
            $command = 0;
        }

        elsif (-f $old_file) {
            my $old_stat    = stat($old_file);
            my $crnt_stat   = stat($tgt_file);

            ### 更新ありならコピー
            foreach my $key (keys %stat_idx) {
                if ($QUERY{$key} and
                    $old_stat->[$stat_idx{$key}] ne $crnt_stat->[$stat_idx{$key}]) {

                    $command = 2;
                    last;
                }
            }
        } else { ### ファイルがなければコピー
            $command = 2;
        }

        ### コピー
        if ($command == 2) {
            copystat($tgt_file, $new_file);

            $copy_count++;

            ### ログ出力
            utf8::decode($new_file);
            print $log "$new_file\n";
        }

        ### ハードリンク
        elsif ($command == 1) {
            link($old_file, $new_file);
        }

        $file_count++;
    }

    ### @files_and_dirsのうち、ディレクトリを再帰処理
    my @dirs = grep { -d "$tgt_cwd/$_"
                    and not -l "$tgt_cwd/$_"
                    and $_ ne '.'
                    and $_ ne '..' } @files_and_dirs;

    foreach my $a_dir (@dirs) {
        &backup("$cwd/$a_dir") or print "Error at $cwd/$a_dir\n";
    }

    return 1;
}

### --------------------------------------------
### 属性丸ごとファイルコピー
### --------------------------------------------
sub copystat {
    my ($from, $to) = @_;
    my $stat = stat $from;

    copy($from, $to) or die "cannot copy $from to $to";
    chown($stat->uid, $stat->gid, $to) if $EUID == 0;
    chmod($stat->mode, $to);
    utime($stat->mtime, $stat->mtime, $to);
}

### --------------------------------------------
### 属性真似てディレクトリ作成
### --------------------------------------------
sub copydirstat {

    my ($from, $to) = @_;
    my $stat = stat $from;

    mkdir $to, 0555 or die "cannot mkdir $to";
    chown ($stat->uid, $stat->gid, $to) if $EUID == 0;
    chmod ($stat->mode, $to);
    utime ($stat->mtime, $stat->mtime, $to);
}

### --------------------------------------------
### ファイルパスの整備
### --------------------------------------------
sub fixPath {
    my $in = $_[0];
    $in =~ s!^!\./! unless ($in =~ m!^\.*/!);
    $in =~ s!/$!!;
    return $in;
}

 

Entry: マルチプラットフォームなアプリ一覧

マルチプラットフォームなアプリ一覧

Initial post: 2009.01.14 | Last modified: 2009.08.01

いつでもMacOSとLinuxとWindowsを行き来できるように。

ブラウザ Firefox 定番ブラウザ。下記はおすすめプラグイン。
  • ViewSourceWith(任意のエディタでHTMLソースを表示。マック版は多少工夫しないと使えない。)
  • Firebug(WEB開発の便利ツールがいっぱい)
  • YSlow(WEB開発時のパフォーマンスチェック。その他、スクリプトなどを外部も含めて全て結合表示してくれたりもして超便利。)
  • FoxyProxy(サイト毎にプロキシを設定)
  • Html Validator(HTML文法チェック、実はMac版もあった)
  • Link Evaluator(ページ内の全リンクを検証)
  • Resizeable Textarea(ルーターの設定画面がマックで散々だったので導入)
  • Ubiquity(コマンド。今のところalcコマンド専用と化している)
  • XUL/Migemo(ページ内検索の機能拡張。Migemoは置いといて、正規表現検索は完全に使える)
メーラー Thunderbird 定番メーラー。下記はおすすめプラグイン。
  • Lightning(スケジューラー)
  • Provider for Google Calendar(Lightningをgoogleカレンダーと同期)
  • Google Contacts(アドレス帳をGoogleの連絡先と同期)
テキストエディタ jEdit Mac版は割と軽い。下記はおすすめプラグイン。
  • BufferTabs(バッファーをタブ表示)
  • Editor Scheme(フォントや色を一元管理)
  • FTP(リモートファイルの編集。SFTPにも対応)
  • Highlight(任意のキーワードをハイライト)
  • TextTools(ソートなど)
  • WhiteSpace(インデントの可視化に使える)
Editra 日本語入力に難あり。作者も認識してるけど直せないらしい。
Scintilla Text Editor EUC-JP不可。
Komodo Edit jEdit使うようになってから出番がめっきり減った。
ファイル転送

Filezilla mac版は驚くほど使いづらい。主な用途はファイラであるmuCommanderで事足りるので、Filezillaは今のところ不要。

波形編集 Audacity
オフィススィート OpenOffice 定番。下記はおすすめ機能拡張。 GanttProject 結構あやしい動きをするけどOPENPROJよりずっと使いやすい。今後に期待。
OPENPROJ プロジェクト管理
アーカイバ PeaZip Mac版なかった..どうしよう
ファイラ muCommander
動画エンコード HandBrake
マインドマップ XMind 中国製。結構おいしい操作性。でも、win版はよく落ちる。Mac版はちょっと前まで起動すらしなかったけど、最近のバージョンで改善。将来に期待。
メディアプレーヤー Songbird 最近本格的にiTunesから乗り換えたけど、特に問題ないんじゃないかと思う
Miro 動画専用かな。今のところ満足な出来。
Amarok
DTP Scribus インストールが激しく難しい
GIMP たいていの人はPhotoshopなんて要らないんじゃないかと思う。Perlでマクロも組める。
フォント IPAフォント OOoのPDF出力時にはTTFフォントが必要なようで、日本語用にIPAフォントがおすすめ。
UML Jude/community 国産。MacOSX版もざっと見たところ、普通に動く。

 

Entry: 縮.jpのバグを直した

縮.jpのバグを直した

Initial post: 2009.07.30 | Last modified: 2009.07.30

先日、縮.jpを国際化ドメイン対応にした際にパス名がぶった切られてたのに気づいたので直した。そして、せっかくなので国際化ドメインへの対応方法について、ちらしの裏的メモ。

Locationヘッダにutf8のバイト列を流し込んでもうまく行かなかったのでpunycodeに変換した。

use Net::IDN::Punycode;

my $url = 'http://例示.日本/path/';
my $fixed_url = '';

my ($schema, $domain, $uri) = ($url =~ m!(https?)://([^/]+)(.+)!);

foreach my $part (split(/¥./, $domain)) {
    if ($part =~ /¥P{IsASCII}/) {
        $fixed_url .= 'xn--'. encode_punycode($part). '.';
    } else {
        $fixed_url .= $part. '.';
    }
}
chop($fixed_url);

printf("Location: %s://%s¥n¥n", $schema, $fixed_url. $uri);

cpanでいくつかモジュールが見つかるけど、Net::IDN::Punycodeが正解。IDNA::Punycodeは古いから利用率高いけどバグもの。

上記の結果は

http://xn--fsq192h.xn--wgv71a/path/

ちなみに国際化ドメインと国際化URI(って呼んでいいのか?)への対処方法は全く別で、さらに国際化URIは国際化ファイル名と国際化クエリーに対処しないとならない気がするので、その辺を今度整理してみよう。

Entry: Javascriptオフ対策、その2

Javascriptオフ対策、その2

Initial post: 2009.06.12 | Last modified: 2009.06.12

検索とタグクラウドもノースクリプトで利用できるようにした。ページナビゲーションはまだない。

JS用に用意した部品のDOMをNoscriptな人に見せないために、最初は<script></script>内に書けばいいのかと思ったら失敗。<script>タグは<noscript>タグのnotでは全然なかったのだ。考えたこともなかったけど、<script>タグ内にはスクリプトしか書いちゃいけない、とのこと。今さらながら。

スクリプト用のDOMはスクリプトで書くしかないのかと思い、document.writeの列挙が頭をよぎったものの、メンテナンス性に配慮してjQueryのappendだのprependだののDOM操作で何とかする。それでも、文字のエスケープの問題もあるし、本来HTMLで書くべきものをJavascriptで書き出すのはやはり手間がかかる。でもまあいいか。

と思っていたのが昨日の話で、今日、あっけない結論に達する。まあ、ここに書くまでもないことかも。

<div class="dom_for_script" style="display:none"></div>
<script type="text/javascript">
//<![CDATA[
$(".dom_for_script").show();
//]]>
</script>