■ このページについて ■
パソコン関係の雑記。ソフトからハードまでまんべんなく。
2001年が最盛期で、「テキストだけで90KB」もザラ。
一時期おとなしくなったけど、管理のCGI化によって息を吹き返し中。
2004.4.5.Mon
スクリプト内部のリンク動作を管理する中央リンク制御装置 conlnk
(たいそうな名前だが、なんの変哲もないただの関数である)が、本日めでたく退役。思えば、JavaScript を始めて半月かそこら、siteCTSがまだ Provisional Edition だった頃に生まれたコイツ。スクリプトの中心核的関数のひとつで、siteCTSのリンクを一手に管理させようとしたこともあった。
何度も改修を受けながら今まで生き延びてきたけど、いかんせんブラウザ依存である JavaScript では「できること」に限界がある。機能をオフにしたらリンクが機能しないなんて、今の俺にはできない。昔もできなかったけど。ゆえに、もともと活躍の場が限られている関数だった。
それ以上に、XHTML1.1で作るようになってスクリプトもDOM化されると、前時代設計の conlnk
はさらに活躍の場がなくなる。Public Edition ではそこらかしこから呼ばれていた人気関数も、Transient Edition ではページの右上にあるコンテンツ移動セレクトボックスでしか動作してなかったりする。(このセレクトボックスは consel
という conlnk
と同期の古株だ。しかしこっちの方は Valid Edition でも現役である。リンク動作は conlnk
が担当していたので、自力で動くように改修された)
退役の記念に、conlnk
のコードを遺しておくことにしよう。
JavaScript を始めたばかりの頃の労作。ていうか、当時のコードを保管している俺にご苦労様。
function conlnk(call,jump) {
//ディレクトリネーム・GTSとNCFの各ファイルネームを定義
if (jump == "ind")
{dir = ""; GTSfile = "index2.htm"; NCFfile = "index2.htm"}
if (jump == "eh")
{dir = "eh/"; GTSfile = "_ehfrm.html"; NCFfile = "eh0.htm"}
if (jump == "sri")
{dir = "sri/"; GTSfile = "frm_sri.htm"; NCFfile = "frm_sri.htm"}
if (jump == "snd")
{dir = "sn/snd/"; GTSfile = "sndfrm.htm"; NCFfile="snd01/_snd0.htm"}
if (jump == "snp")
{dir = "sn/snp/"; GTSfile = "snpfrm.htm"; NCFfile = "snpfrm.htm"}
if (jump == "prfl")
{dir = "prfl/"; GTSfile = "_prflfrm.html"; NCFfile = "_prflindex.htm"}
if (jump == "mfa")
{dir = "mfa/"; GTSfile = "_mfafrm.html"; NCFfile = "_mfa.htm"}
if (jump == "sg")
{dir = "sg/"; GTSfile = "sg.htm"; NCFfile = "sg.htm"}
if (jump == "bbs")
{dir = "bbs/"; GTSfile = "_bbsfrm.html"; NCFfile = "_bbsfrm.htm"}
if (jump == "lnk")
{dir = "lnk/"; GTSfile = "_lnk.htm"; NCFfile = "_lnk.htm"}
// 同位用(indexのみ)
if (call == "ind") {
if (mark == "GTS") {top.location.href = dir + GTSfile}
else {top.location.href = dir + NCFfile}
}
// 2階層用(sndとsnpのみ)
else if (call == "snd" || call == "snp") {
if (mark == "GTS") {top.location.href = "../../" + dir + GTSfile}
else {top.location.href = "../../" + dir + NCFfile}
}
// 1階層用(その他)
else {
if (mark == "GTS") {top.location.href = "../" + dir + GTSfile}
else {top.location.href = "../" + dir + NCFfile}
}
}
当時はフレーム版とノンフレーム版があったので、自動識別して飛ばす機能を内蔵。ついでにターゲットウィンドウも選択できるようになった。
function conlnk(dirLv, jumpDir, modeSelect, nmlSelect) {
var jumpAnc, hashPos = jumpDir.indexOf("#");
if (hashPos != -1) {
jumpAnc = jumpDir.substring(hashPos, jumpDir.length);
jumpDir = jumpDir.substring(0, hashPos);
}
var dir = jumpDir + "/"; //dirとfileの識別子が違うものだけ個別指定
var nmlFile, pscFile;
if (jumpDir == "top") {
dir = ""; nmlFile = "top.html"; pscFile = "top.html";
} else if (jumpDir == "psc") {
dir = ""; nmlFile = "top.htm"; pscFile = "top.htm";
} else if (jumpDir == "sui") {
nmlFile = "suiFrm.htm"; pscFile = "sui.htm";
} else if (jumpDir == "goc") {
dir = "sui/"; nmlFile = "gocFrm.htm"; pscFile = "goc.htm";
} else if (jumpDir == "cts") {
dir = "sui/"; nmlFile = "ctsFrm.htm"; pscFile = "cts.htm";
} else if (jumpDir == "snd") {
nmlFile = "sndFrm.html"; pscFile= sndCurrentURL;
} else if (jumpDir == "snp") {
nmlFile = "snpFrm.html"; pscFile = snpCurrentURL;
} else if (jumpDir == "ysr") {
nmlFile = "ysr.html";
} else if (jumpDir == "pjs") {
dir = "ysr/pjs/"; nmlFile = "pjsFrm.html"; pscFile = "pjs.html";
} else if (jumpDir == "fht") {
dir = "ysr/fht/"; nmlFile = "fht.html";
} else if (jumpDir == "sss") {
dir = "ysr/sss/"; nmlFile = "sssFrm.html"; pscFile = "psc.html";
} else if (jumpDir == "wmp") {
nmlFile = "wmpFrm.html"; pscFile = "wmp.html";
} else if (jumpDir == "scg") {
nmlFile = "scgFrm.htm"; pscFile = "scg.htm";
} else if (jumpDir == "bbs") {
nmlFile = "bbsFrm.htm"; pscFile = "bbs.shtml";
} else if (jumpDir == "dia") {
nmlFile = "diaFrm.html"; pscFile = "dia.shtml";
} else if (jumpDir == "cha") {
nmlFile = "chaFrm.html"; pscFile = "cha.html";
} else if (jumpDir == "ind") {
dir = ""; nmlFile = "index.html";
} else {
dir = ""; nmlFile = jumpDir; pscFile = jumpDir;
}
if (!pscFile)
pscFile = nmlFile;
var path = makeCdupCmd(dirLv) + dir;
if (nmlSelect)
path += nmlFile;
else if (nmlSelect == 0)
path += pscFile;
else
path = path + (TFL ? nmlFile : pscFile);
if (jumpAnc)
path += jumpAnc;
if (!modeSelect)
top.location.href = path;
else if (modeSelect == 1)
top.location.replace(path);
else if (modeSelect == 2)
window.open(path);
else if (modeSelect == 3)
opener.top.location.href = path;
else if (modeSelect == 4)
opener.top.location.replace(path);
else
alert("ScriptError:conlnk.modeSelect.unknown");
}
オブジェクト指向の波に呑まれ、クラス navs
のメソッドとして再定義される。validなHTMLの採用でフレームが撤廃されたので、それ関係の機能が削除された。
navs.prototype =
conlnk : function (toDir, mode) {
var p = this.root, anc = "", hashPos = toDir.indexOf("#");
if (hashPos != -1) {
anc = toDir.substring(hashPos);
toDir = this.cutQueryString(toDir);
}
switch (toDir) {
case "top": p += ""; break;
case "bbs": p += "bbs/"; break;
case "scn": p += "scn/"; break;
case "dia": p += "scn/dia/"; break;
case "wdn": p += "scn/wdn/"; break;
case "psn": p += "scn/psn/"; break;
case "pcl": p += "pcl/"; break;
default : p = toDir; //ファイル直指定の時
}
p += anc;
switch (mode) {
case 1: location.replace(p); break;
case 2: window.open(p); break;
case 3: window.opener.location.href = p; break;
case 4: window.opener.replace(p); break;
default:location.href = p;
}
return false;
}
}
さよなら conlnk
。
…とか言いながら、CGIの発展次第ではPerlで生まれ変わる可能性も大きかったりする。conlnk
は本来、ブラウザに影響されないCGIでこそ活きる性格のものだから。(インデックスサーチがいい例である)
2004.4.7.Wed
CGIでcookieが焼けない。うまくいかない。HTTPヘッダに
print "Set-Cookie: name=value; path=/valid/; expires=Wed, 07-Apr-2004 00:00:00 GMT;\n";
…と入れてやればいいはずなのに、うまくいかないのである。JavaScriptで焼く分は問題ないのだが、CGIで焼く分がうまくいかない。
onsubmit
で捕まえて下焼きというコンビネーションが問題なのだろうか?
cookieを消しては焼いて色々やっているうち、CGIで記録した分が完全に無視されてるわけでなく、最初のひとつだけしか焼けてないことが判明。各ブラウザのcookie仕様書では
print "Set-Cookie: name1=value1; name2=value2; name3=value3;\n";
で name1〜3
が記録される…と書いてあるのだけど、これではうまくいかなかった。JavaScriptも一度に複数の値を焼くことはできないので、
document.cookie = "name1=value1;";
document.cookie = "name2=value2;";
document.cookie = "name3=value3;";
というように重ね焼きしないといけない。そこで、CGIも同じように
print "Set-Cookie: name1=value1;\n";
print "Set-Cookie: name2=value2;\n";
print "Set-Cookie: name3=value3;\n";
てな感じでcookieヘッダを連ねていた。
…が、cookieヘッダはひとつだけしか有効にならないという仕様であれば、かのような動作も理解できる。(色々検索はしてみたが、お目当ての情報は見つからなかった)
でもこれが事実だとしたら、ずいぶん難儀な仕様だな。たとえば掲示板でユーザーの名前とかメールアドレスとかサイトURLとかを
print "Set-Cookie: name=$queries{$name};\n";
print "Set-Cookie: mail=$queries{$mail};\n";
print "Set-Cookie: site=$queries{$site};\n";
で焼けないことになる。つまり、これらをいっぺんに焼きたいなら、
%queries = クエリの連想配列;
print "Set-Cookie: data=$queries{$name},$queries{$mail},$queries{$site};\n";
みたいにひとつにまとめて、読み出す時に
%cookies = cookie の連想配列;
my ($name, $mail, $site) = split(',', $cookies{'data'});
としないといけない。
通常はそうすればいいんだけど、siteCTSのcookieはJavaScriptと共用しているので、そうなると根本的な部分(cookieの値を分割してクラスのプロパティに保持させる部分)を大きく組み替えないといけない。なんと面倒くさい。これは予定外だ。時間外労働をしても残業はつかないのだぞ?(そもそも労働ではない)
でもまぁとりあえず障害は取れたので、さっさとやって次へ進もう。
そういえば、JavaScriptで初めてcookieを扱った時も苦労したっけなぁ…。「なんで焼けないんだよー?」とかいって。
2004.4.20.Tue
2001年2月〜4月までの記事をXHTML化。うあぁ。疲れた…。誰も読みゃしねえってのにご苦労だね俺も。
あと、過去サイトへ迷いこんだお客様が勘違いしないように、ほぼ全部のページにナビゲーションを仕こんだ。たとえばSample Versionのパチョ奮闘記に迷いこんだとしても、ページの上に出てるナビゲーションリンクを辿っていけば最新版に漂着する仕組み。
これにはJavaScriptとSSIを併用してみた。
<!--#include virtual="/xxx/yyy/zzz/movetouri.ssi"-->
HTMLにはこの1行しか書いてない。movetouri.ssi
の中身は
■movetouri.ssi
<script type="text/javascript">
function movetouri(f) {
…移動先URIを出力するスクリプト…
}
</script>
<div>
<p>
<script type="text/javascript">
movetouri("currentedition"); //今いるエディションを出力
</script>
このページは <a href="/?index">siteCTS</a> の旧版です。<br>
URI確保のために保存してあるだけで、二度と更新されません。<br>
JavaScript が有効の時は以下に移動先URIが表示されます。<br>
<script type="text/javascript">
movetouri(); //移動先URIを出力
</script>
</p>
</div>
…となっていて、各HTMLに埋めこまれたこの1行が上のファイルを読みこんで、その位置に挿入する。サーバー側で加工するから、ブラウザにとっては普通のHTMLと同じである。ソースを見ても、SSIが暗躍した痕跡は少しもない。(別にあっても困らないけど)
これがなにを意味するかってと、このSSIファイルをいじるだけですべての修正ができるってことだ。特に俺はどうでもいいことにこだわるので、無駄な労力を極力節約するためにも、これは大事な要素である。
その気になれば、SSIがCGIを起動、CGIがブラウザに合わせたJavaScriptを出力、さらにJavaScriptが状況に応じたHTMLを書き出し…なんて芸当も可能だ。
■SSI
<!--#exec cmd="script.cgi"-->
■CGI+JavaScript+HTML
if (index($ENV{'HTTP_USER_AGENT'}, 'MSIE 6.0') != -1) {
$browser = 'IE6';
} else {
$browser = 'IE6以外';
}
print <<"hereDocument";
Content-Type: text/html;
<script type="text/javascript">
document.write("<p>あんたのブラウザは $browser のようですわ<\/p>");
</script>
hereDocument
こんな感じに。専用のレンタルサーバーだとこういうことがお手軽にできて助かるわぁ。(それがやりたくてレンタルサーバーを借りてるわけですが)
さて、残る記事修正は2001年5月〜12月。気が遠いわぁ。
2004.4.21.Wed
ようやく登場したScrawl Notesの統合管理CGI。皆様には突然の登場であるけど、去年の6月13日の日記にも記述が登場してるように、ずいぶん時間がかかってたりする。(実質の作業期間は一週間ほどだけど)
このCGIはとても働き者で、これひとつでほとんどのことをやってくれるのだ。
その辺の配布ものとは違って自分に最適化されてるので、とにかく(俺にとって)使いやすい。このメリットがあるからこそ自作したんだけど。
配布ものの場合、サーバーにCGIをアップしてアクセスし、管理モードなりのボタンを押してパスワードを入れて編集画面に移るのがほとんどだ。(レンタル掲示板と同じだね)
毎日書くようなコンテンツでそんなまだるっこしいことやってられない。ましてやHTMLを作っていちいちアップだなんて無理。
規模のわりにテキスト量が異常に多い当サイトだから、管理の自動化は昔々からの悲願だったのだ。JavaScriptでやろうとしてた時期もあったが、ブラウザ依存のJavaScriptは、やはりこういう用途には向かなかった。CGIでないと不可能なのである。JavaScriptで修行を積みながら思っていたのさ。「いつかCGIが使えるようになったら絶対やってやる…!」と。
そしてその第1弾が、やっとお目見えなのである。
素敵CGIの索敵ポイント
処理のステップとしては、
という感じ。
下手な配布ものよりよっぽど高性能になってるはずだけど、最適化しすぎてsiteCTSでしか使えないのがなんとも。(汎用にデチューンして配るのも一手ではあるけど)
なにより悲しいのは、このCGIを没にしてしまったことである。没になったからこそ、このTransient Editionに登場したわけで、本当はValid Editionでデビューするはずだったのだ。
そして、これから作る新型CGIは、これを凌ぐ性能…になる予定。
では恒例のCGIソースを…といきたいとこなのだが、CGIはJavaScriptと違ってセキュリティに直接関係するので、迂闊に公開できない。まだまだひよこだった頃のソースなら見せても大丈夫だけど。(実装版はこれの3倍以上のサイズだ)
仕方ないので書込画面の体験版モードを作ってみた。お楽しみあれ。(それは無理な相談だ)
※IE6専用なので他のブラウザで入るとエラー出まくるわよ。
このCGIの導入で一番しわ寄せを食ってるのは、気分日常雑記帳。このコンテンツはのんびり更新したいので、日付管理だとバックナンバーのリストがひどい虫食いになっちゃうのよね。だから記事番号管理にしてたのだけど。まぁValid Editionになったら全部ひとつに統合されるので、それまでは我慢か。
2004.4.24.Sat
スクリプトをちょこちょこ修正したので、念のために小春のIE5.5でチェックしてみたら、エラーが出てビックリした(出ると思ってなかった)。まさかと思ってWebの方を見てみたら、やっぱりエラーが出た(悪夢だ)。いつからだ。こないだのアップデートからか。
IE5.5ではマメにチェックしないゆえ、こういう事態は不測ではない。…が、siteCTSのスクリプトは(確認環境のある範囲では)かなり完成度が高いのである。これだけのJavaScriptを積んでてNetscape3がエラーを起こさないサイトなんて、滅多にあるものではない。
なぜ驚いたか。
単純な構文ミスであれば、IE5.5と言わずすべてのブラウザでエラーが起きて、すぐ気がつくはずだからだ。しかしIE6もNetscape7もOpera6もOpera7も、どれひとつとして問題なく動いていたのである。(これらは全部雪子に入ってる)
それどころか、三夫のIE4・Netscape3でさえエラーを起こさない。
つまりIE5.5だけで起きるエラーなのであるが、IE5.5で非対応の命令を使えば、IE4もエラーになるはずだ。普通、IE4とIE6で動けば、IE5.0とIE5.5でも動くと思うじゃないか。(実際にはNetscape6も起きていたはずだが、運悪く確認してなかった)
エラーメッセージを頼りに該当のコードを探してみるも、原因がさっぱりわからない。
if (t) return (y ? y : "") + (d.getMonth() +1) + "月" + d.getDate() + "日";
else return (y ? y : "") + (d.getMonth() +1) + "." + d.getDate();
これのどこに問題があるというのだ。さらに、問題の部分を削除しても、今度は別のところでエラーが起きる。もうなにがなんだかさっぱりわからん。
…というほど俺は素人でないわけで、外部スクリプトファイルの文字コードが原因と判明。(先日のアップデートで該当ファイルの文字コードを変えたからだ)
外部スクリプトファイルを呼び出すタグは、通常
<script type="text/javascript" charset="Shift_JIS" src="file.js"></script>
と書く。charset="Shift_JIS"
でファイルの文字コードを指定できるはずなんだけど、これを解釈するブラウザは少ない。
てなわけで、自分の環境にあるすべてのブラウザで実験してみたところ、こういう結果になった。
charset
対応状況
※全部Windows版。下に行くほどダメダメ。
charset
を見ている。ない時はHTMLと同じ文字コードで解釈する。違う charset
を指定すると即エラー。charset
を見ているようだが、自動判別してるような動きもある。融通を利かそうとしてるのだろうか。違う charset
を指定すると、リロードを繰り返すごとにエラーが出たり出なかったりJavaScriptが実行されなかったり、挙動不審になる。charset
は見てないが問題なし。自動判別してるようだ。charset
は見てない。HTMLと同じ文字コードで解釈する。charset
は見てない。HTMLがShift-JISの時は自動判別するが、HTMLがEUCの時には同じ文字コードで解釈するようだ。Shift-JISにすると、どうやってもエラーが出た。charset
は見てない。HTMLの文字コードがどうであれ、Shift-JISとして解釈する。Netscape4.78は意外であった。なにをどうやってもエラーが起きなかった唯一のブラウザであった。ただ、ここではEUCとShift-JISでしか実験してないので、UTF-8とかでどうなるかは知らない。
つまり、今回のエラーの原因はこうだ。
結局のところ、一番安全なのはHTMLもスクリプトもShift-JISで書くことに間違いはない。WindowsもMacもデフォルトはShift-JISだから、普通にやってる分には問題は起きないだろう。(ただしMacの一部のIEは強制的にUTF-8として解釈するらしい)
ただ、CGIにPerlを使い始めると話が違ってくる。PerlはもともとUNIXの言語なので、デフォルトの文字コードがEUCだ。Shift-JISでも作れないことはないけど、正規表現で問題が起きたり、「表」「ソ」といった特定の文字を使うと化けてしまう。(「ソ\ースの表\示」みたいにエスケープすると回避できるが、これがまた面倒くさい)
だから、CGIからHTMLを出力する時は、EUCが一番気軽で安全。
これはなかなか由々しき事態だ。ネットでちょっと調べてみたら、結構いっぱいヒットした。これだけ致命的な問題なら、そりゃ話題にもなるか。俺個人は今まで全部Shift-JISで問題なかったから、関心が薄かった。
でも、俺の今の旬はCGI。EUCを譲る気はない。つまりHTMLも自動的にEUCになり、スクリプトもEUCが一番問題が少ない。IE4・Netscape3・Opera6なんて早々いないわけだし。
だけど、当サイトのスクリプトは「Netscape3がエラーを出さないこと」を指標としていて、それを実現するために相当の労力を費やしている。(Netscape3は正規表現や switch
構文、アノニマス関数に対応してないので、実行しなくても存在するだけで構文エラーになる)
ここまで苦労してあきらめるのも悔しいではないか。
対策方法として、JavaScriptのCGI化がある。CGIでブラウザに応じたスクリプトを書き出すのは簡単なことだ。…が、それができるんならとっくにやっている。滅多に来ないブラウザのために、余計なサーバーリソースと俺リソースを費やす気はない。
じゃあどうするかっつと、なにもしない。スクリプトはEUCのままだけど、エラーも出ない。
つまりだ、Shift-JISもEUC-JPも、ASCII文字の文字コードは同じであるから、EUCをShift-JISと誤解釈されたところで支障はないっちうわけだ。
もうお分かりだね。スクリプトファイルに日本語を使わなきゃいいのだ。そしたらバッチリ、スクリプトファイルをEUCにしてもエラーが起きなくなった。
普通はこんな方法は実用的でないが、当サイトのスクリプトは「ブラウザを調べる斥候ファイル」と「許可されたブラウザにのみ読みこませる本体ファイル」の2段階式であり、IE4やNetscape3には(日本語使いまくりの)本体は読ませないから、その辺は無問題。フレームを強制解除するフレームブレイカーのメッセージが英語になっただけ。スクリプトのコメントに日本語を書けなくなったのは痛いけど。
先ほどの実験を見てわかるように、JavaScriptの文字コードの問題は長いこと放ったらかしにされてたのが露出している。しかし、IE・Netscape・Operaともに、最新バージョンでようやく charset
を見るようになったこともわかった。旧ブラウザの淘汰が進むにつれて、この問題も風化していくことだろう。
だから未だにIE4とか使ってる輩は、とっとと新しいパソコンに買い換えてください。
siteCTSは「Validだけど旧ブラウザにもそれなりに優しいサイト」であり続けようと思う今日この頃。