Proxomitronを使ってtitleを変更する方法

みなさんこんにちは。毎日着実に部屋が汚くなってゆく今日この頃ですが、いかがお過ごしですか?
私はよく、ニュース記事やおもしろ記事なんかをCopy URL+を使ってIRCのみんなに見せたりメッセで知人に送りつけたりするんですが、そんな時、ページのtitleが適切に設定されていなかったりするとイラっと来ちゃうんですよね。キレる17才なので。
で、そんなわけなんで、いつもの通りProxomitron使ってなんとかしちゃえばいいんじゃないのっていう。そういう記事ですよ。だってほら、キレる17才なので。
今回は、活字中毒R。さんの個別記事にタイトルをつけてみます。活字中毒R。さんの記事ページはトップも個別ページも全て

というタイトルになっているので*1、これを

といった感じに直すフィルタを書いてみましょう。

[Patterns]
Name = "KATUJI CHUDOKU R. | title assignment 20080612"
Active = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "(<TD BGCOLOR="#F0F0F0"><B>)\0(^(^ \1</B></TD>))"
Replace = "\0\n<script type="text/javascript">\n"
          "document.title = "\1 - "+document.title;\n"
          "</script>\n"

はい、できました。それではどうぞご利用ください。

…じゃなくて。今回は、なにをしているのかきちんと説明したいと思います。

titleを書き換えるには?

まずはHTMLのお話ですが、titleを書き換えるにはどうすればいいのでしょうか。
正攻法で考えれば、head内に書かれたtitleを直接書き換えるという方法があります。しかしこれをProxomitronでやろうとすると、実は結構大変なんですね。HTMLの構造的に、titleはソースの最初の方にあり、ページのタイトルとして抜き出したい部分は後ろの方にある訳です。ですから、まず後ろにマッチさせてページの最初に戻る…という動作をしなければいけないのですが、これがProxomitron的にはどうにも非効率的なのです。実際にこの方法でフィルタを書いてみましょう。

方法A: フィルタを2つ使い、正攻法で書き換える

最初のフィルタでタイトルとして使用したい部分をグローバル変数に代入し、次に別フィルタでその変数を参照してtitleを書き換えます。

[Patterns]
Name = "KATUJI CHUDOKU R. | title assignment TEST-A1"
Active = TRUE
Multi = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "(<TD BGCOLOR="#F0F0F0"><B>)\0(^(^ \1</B></TD>))$SET(titleKatujichudoku=\1)"
Replace = "\0"

Name = "KATUJI CHUDOKU R. | title assignment TEST-A2"
Active = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "<title>"
Replace = "<title>$GET(titleKatujichudoku) - "

A1のフィルタでタイトルにしたい部分を探し、グローバル変数titleKatujichudokuに代入しています。そして、A2のフィルタで$GETしてtitleに流し込む動作をします。HTMLフィルタはフィルタ一覧の並び順に従い上のフィルタから順に処理されますから、A2はA1よりも下に配置しなければなりません。これでバッチリ…
…と思ったんですけど、動かないですねこれ。あれ??????
理由がわからないですが、とにかく失敗してしまいました。上に置いたフィルタがマッチし終わった後に下のフィルタがマッチするんだと思ってたんだけどなぁ。違ったのか。詳しい方いらっしゃいましたらすみません、ご教示ください…。

方法B: titleタグを途中にムリヤリ追加する

本来titleタグはhead内になければならないのですが、もうこの際そんなことはどうでもいいよ!と、ムリヤリ追加してしまう方法も考えられます。例えばこのように。

[Patterns]
Name = "KATUJI CHUDOKU R. | title assignment TEST-B"
Active = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "$NEST(<title,/title>)|"
        "(<TD BGCOLOR="#F0F0F0"><B>\1</B></TD>)\0"
Replace = "$TST(\0=?*)"
          "\0<title>\1</title>"

あー、今度は動いたー。よかったー。
Matchの1行目では、元々設定されているtitleを消去しています。ブラウザによっては、titleが2つある場合、先に書かれている方を優先することがあるためです。2行目は、タイトルとして抜き出したい部分のMatchですね。
Replaceでは場合分けを使用しています。1行目は$TSTを使って、\0に値が代入されていることをチェック*2。代入されていれば、2行目が実行されます。このように$TSTを使った場合分けを用いることで、複数のフィルタを一つにまとめることができます。
参考までに、1つにまとめないとこのようになります。

[Patterns]
Name = "KATUJI CHUDOKU R. | title assignment TEST-B1"
Active = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "$NEST(<title,/title>)"

Name = "KATUJI CHUDOKU R. | title assignment TEST-B2"
Active = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "(<TD BGCOLOR="#F0F0F0"><B>\1</B></TD>)\0"
Replace = "\0<title>\1</title>"

でもやっぱり、スマートじゃない…。

確かに書き換えはできました。でも、どうもスマートじゃない。俺はW3C信者だから、変なとこにtitleあるのとかやだよ。美しくないよ。
ごもっともです!!
そこで、最終奥義です。JavaScriptを使いましょう。

方法C: JavaScriptで書き換える

いやまぁ、既に冒頭で書いてしまったんですけど。

[Patterns]
Name = "KATUJI CHUDOKU R. | title assignment 20080612"
Active = TRUE
URL = "www.enpitu.ne.jp/usr6/(60769/diary.html|bin/day\?) $TYPE(htm)"
Limit = 200
Match = "(<TD BGCOLOR="#F0F0F0"><B>)\0(^(^ \1</B></TD>))"
Replace = "\0\n<script type="text/javascript">\n"
          "document.title = "\1 - "+document.title;\n"
          "</script>\n"

これがJavaScriptで書き換えるバージョンです。きちんとご説明しましょう。
Matchは非常にシンプルで、タイトルにしたい部分の直前までを\0に代入し、否定先読みを併用して\1にタイトルを代入しています。この否定先読みを多用する手法は私の中ではもはや宗教的なものになってしまっているだけで、そうでない一般ピープルのみなさんはわざわざ無理にこんなことをする必要はありません。下のように書いても同じです。

Match = "(<TD BGCOLOR="#F0F0F0"><B>\1</B></TD>)\0"

ただ、もちろん否定先読みの利点というのもありまして。実は否定先読みを使うと、後半のマッチング(ここでは\1以降のマッチングですね)が短くて済むケースがあるのです。実際問題としてはどっちだろうと大したことではないのですが、見た目のシンプルさと美しさから私は否定先読みを使っているというわけです。はい、すみません。布教活動は自粛しますね。
さて話を戻しましてReplaceですが、まず頭に\0を置いて、元のソースを復元するのは説明不要かと思います。なんつって、説明しちゃった。
その後、次のようなScriptを書いています。

<script type="text/javascript">
  document.title = "\1 - "+document.title;
</script>

document.titleというのは、タイトルバーに表示されてるページタイトルの変数です。普通はtitleで記述されているものがそのまま入っています。で、そのdocument.titleをProxomitronの変数\1…つまり任意のタイトルに書き換え、なおかつその後ろに「 - 元のタイトル」を足しています。元のタイトルってのはつまりこの場合なら「活字中毒R。」ですね。相変わらずの説明下手ですが、わかりますでしょうか?

テンプレ化

色んなサイトに使えるように、テンプレートとして持っておくと便利です。たぶん$LSTで管理する方が楽な方もいるかと思いますが、個人的にはあまり好きではないので…。

[Patterns]
Name = "***TEMPLATE*** title assignment 20080612"
Active = FALSE
URL = "$TYPE(htm)"
Limit = 200
Match = "([CUE])\0(^(^ \1[CUE]))"
Replace = "\0\n<script type="text/javascript">\n"
          "document.title = "\1 - "+document.title;\n"
          "</script>\n"

[CUE]の部分に、必要なマッチを挿入します。それからURL Matchの追加も忘れずに。
例えば、このテンプレをZAKZAKに使うとこうなります。

[Patterns]
Name = "ZAKZAK | title assignment 20080612"
Active = TRUE
URL = "www.zakzak.co.jp/ $TYPE(htm)"
Limit = 200
Match = "(<font class="kijimidashi" size="5"> <!--[^>]++-->)\0(^(^ \1 <!--))"
Replace = "\0\n<script type="text/javascript">\n"
          "document.title = "\1 - "+document.title;\n"
          "</script>\n"

ね、簡単でしょ?

それでは今度こそ

キレる17才としては、そろそろ書き疲れてキレそうですので…
どうぞご利用ください。

*1:『エンピツ』の仕様なのかな?

*2:$TST(\0=?*)という表現を使うと、ローカル変数のboolean型チェックをする事ができます。?は任意の1文字、*は0文字以上の任意の文字にマッチしますから、null以外の全ての場合にTrueを返すわけです。本来は$TST(\0)でこのように動作するはずなのですが、ローカル変数に対する$TSTはバグ持ちのため、このように表現する必要があります。対象がグローバル変数であれば$TST(hoge)で問題なく動作します。