Page Index
日付 題名 記事の要約
2005.5.7 新マシンプロジェクト(代理) 自作PCのおつきあい
2005.5.15 モエモエ誕生 PC代理セットアップ記
2005.5.25 新生yukina JavaScript大改造
2005.5.26 文字化けGecko ActiveX版のバグ?
2005.5.27 思わぬ超バグ EUC-JP vs EUC_JP
2005.5.28 グッバイNetscape6 サポート終了
2005.5.29 CSSと正規表現とIEと CSS大掃除の巻
2005.5.30 グッバイ拡張子 コンテントネゴシエーション対応

 友人がパソコン買うとかでお任せされたので、なにを選ぼうか考え中。予算が15万もあるので、結構な贅沢マシンにできます。以前、別の友人の時は「予算5万」とかだったし、その前は「予算3万」とかだったし。(ともにモニタ抜きとはいえ)

 まず最初に考えないといけないのが、自作にするか、DELLにするか。それが悩みどころです。俺に任せた以上、VAIOだのFM-Vだのは視野に入ることさえ許されません。
 んー、初めて扱うパソコンなわけだし、無難なのはDELLです。でも、DELLのサイト見る限り、なんか魅力感じないんですよね。俺の場合、不良交換以外のサポートいらないし。スペックを自由に選べない、いらないもんがついてきて割高になるなど、デメリットの部分ばかりが目についてしまうのです。
 だからといって自作にするのは、俺がすべてサポートするよ!と言ってるようなもんです。いや、金のかからないサポートならいつでもやってあげますが、やっぱ専門職にはかなわないと思うんだよね。

とりあえず自作濃厚として考えると、

  • CeleronD 320以上
  • 915Gマザー(グラボは必要だと思った時に増設)
  • メモリ512MB×1〜2(デュアルチャンネルが必要かは微妙)
  • HD160GB〜200GBのを×2(これは必須)
  • 2層対応スーパーマルチドライブ
  • 19インチCRTか17インチ液晶(ここが一番のネック)
  • 適当なケース(7000円クラスで冷却性重視)
  • そこそこの電源(5000円クラス)
  • XP Home
  • マウスやらキーボードやらスピーカーは適当なの(3点どう?とかw)

 …と、こんなあたりかな。
 こんなスペックをメーカー品に要求したら20万軽く超えるけど、自作なら15万に収まります。ええ時代やなぁ、と嘆かずにはいられません。

 CGとか動画とか高負荷作業を日常的にやりそうな人ではないし、Pen4は必要ないと思うんだけど…どうだろう。
 完全趣味マシンだからソフトウェアはほとんどフリーウェアでまかなえるし、どうしても必要なものは後から順次買い足していく方向で。(さすがに初心者にOpenOfficeはすすめられないが)

 あぁ、あとADSLとかの手配もしなきゃいかんのか。気軽に引き受けちゃったけど、これまでと違って最初の一歩からの世話なので結構忙しくなるかもなー。
 まー俺に任せときー。


モエモエ誕生

2005.5.15.Sun

 5.7に言ってた計画が実行に移されました。店でパーツを選んでる間にも中古PCやメーカーマシンにすぐ目移りして大変でした。あなたはお菓子に目がくらむ小学生ですか。
 いきなり店員に「これで自作するんだけど、それに対するサポートはあるのか」などと真顔で聞き始めたり、顔から火を吹きそうな思いもたくさんしました。自作のデメリットはサポートがないことだと、あれほど言っておいたのに。「聞くのはタダ」はたしかにそうですが、俺のいないとこで聞いてください。
 メモリをデュアルチャネルにするんだから、どうせなら相性保険といったオプションがあるのかを聞いてください。(というのは無理な話なので俺が聞きました。無料で10日間返品OKでした)

 そんな紆余曲折を経て、自作になりました。初めてPC触る人に自作をすすめるのもどうかと思いますが、「初心者」を自称できる期間は最初の数ヵ月だけです。そりゃまぁ進歩のない万年初心者もいますが、下手なメーカーマシンを買って不便な思いをする可能性は避けたいところです。いざとなったら俺がいるし、リモートアシスタンスだってあります。便利な世の中です。

  • CPU … CeleronD 325J(LGA775 / 2.53GHz)
  • マザー … Intel D915GAVL(915G+ICH6)
  • メモリ … ブランド品512MB×2(デュアルチャネル)
  • HD … 160GB×2(シリアルATA・NCQ対応)
  • 光学系 … ハイパーマルチドライブ(2層対応)
  • モニタ … 17インチ液晶(スピーカー内蔵)
  • マウス … ワイヤレスマウス(チルトホイール付き)
  • キーボード … 俺の余り物(ダイソーで売ってそうなチープ品)
  • ケース … 5"×4・3.5"×2・シャドウベイ×4・フロントファン8cm×2・ケースファン12cm×1・パッシブダクト付き・電源400W
  • OS … WinXP Home Edition

 こんな感じのスペック。キーボードはいいのがなくて、俺の余り物で間に合わせ。予算が充分にあって楽しいお買い物でした。
 例によって安定志向です。上を見たらキリがないPCの世界、15万で組めるマシンとしてはこの辺が妥当かな。値段のこなれているパーツを選んだのでハイエンドではありませんが、使い道も決まってない初心者には無駄すぎるほどのオーバースペック。それでいて、さらにパワーアップの余地も残してあります。(CPUはPen4への換装を考えて一番安かったやつをチョイス)
 現時点では千枝に勝てないですが、所詮は旧世代のPGA+865PEですし、底力ではLGA+915Gのこっちが上です。

[ 買ってきたパーツ / 39KB ] [ マザー / 62KB ] [ 組み立て中 / 37KB ] [ OSインストール / 45KB ]

 彼はなかなか向上心旺盛で、組み立ても自分からすすんでやりました。まず最初に静電気を逃がすあたりちゃんと勉強してるね!…などと感心したのも束の間、慎重な扱いを要するLGAだというのにCPUをガチャンと置いたり、CPUクーラー底面の熱伝導シートを触ったり、マザーごと割りそうな力強さでメモリを押しこんだり、ミリネジにインチネジを強引にねじこもうとしたり、さすが車しか改造いじったことない人です。手さばきの大雑把なことといったら見てられません。こっちが冷や冷やです。
 それでもなんとか組み上がって、無事に起動。英語ができない人なので、BIOSの設定は俺が代行。
 せっかくだからOSのインストールもやらせてみました。自分でやらないと憶えないしね。やってみりゃ簡単でしょ。

 で、そこで夜も遅くなりタイムアップ。残りのセットアップは俺に一任することに。
 それはいいんですが、パーティションの概念すら知らない人にも使えて、その上で利便性も確保するのは結構難しいです。自分でやる気のさらさらない人が相手なら、俺が面倒を見るの覚悟で専門的なカスタマイズもやっちゃうのですが、彼はやる気満々です。OSの再インストールも自分でやろうとするでしょう。そうすると、あまり専門的なカスタマイズをしたら同じ状態に復旧できません。

 というわけで、俺にしてはかなりデフォルト重視のセットアップです。最初から便利になってるよりも後から便利にした方が憶えやすいし、ありがたみも理解できるというものでしょう。
 つーか、まだ終わってないんだけど。
 早く快適ないんた〜ねっとライフを堪能したいだろうし、さっさと終わらせて引き渡してあげたいところです。(でもADSLがまだ開通してない)

追記:2005.6.10
引き渡しは6.4です。

「モエモエ」はPCのネットワーク名。こんなの真っ先に出してくるあたりで、彼が濃ゆいオタクであることがうかがえます。


新生yukina

2005.5.25.Wed

 6月1日のアップデートを目指してこりこりやってます。今回は新コンテンツが超フライングしてきたり懐かしいコンテンツが3年ぶりに復活したり、久しぶりの大型更新です。

 これに伴って、サイト設定のひとつを試験的に組みこんでみることにしました。それを管理するクラス "envs" はTransient Editionにも使われてますが、Valid Editionの方から save() メソッドを輸入。これは設定をcookieに保存します。

//   envs.save //
function Envs_save() {
  if (this.disabled) {
    window.alert(this.cookieDisableMsg);
    return false;
  } else {
    if (!this.enabled && !window.confirm(this.cookieSaveConfirmMsg))
      return false;
    var ex = new Date();

    var limitDays = envs.cookie.cookielimit ? envs.cookie.cookielimit : 30;
    ex.setTime(limitDays *24 *60 *60 *1000 + ex.getTime());
    ex = "path=" + navs.root + "; expires=" + ex.toGMTString();

    var i;
    if (!arguments[0]) {
      for (i in envs.cookie)
        document.cookie = i + "=" + envs.cookie[i] + "; " + ex;
    }

    if (typeof(arguments[0]) == "object") { //Array("key1", "val1", "key2", "val2"...)
      for (i = 0; i < arguments[0].length; i++)
        document.cookie = arguments[0][i] + "=" + escape(arguments[0][++i]) + "; " + ex;

    } else { //("key1", "val1", "key2", "val2"...)
      for (i = 0; i < arguments.length; i++)
        document.cookie = arguments[i] + "=" + escape(arguments[++i]) + "; " + ex;
    }
    document.cookie = "savedate=" + (new Date()) + "; " + ex;
    return true;
  }
}

 さらに、DOMを一括管理するクラスに "yukina" っつのがあり、これはValid Edition用だった新型に丸ごと換装。まったくのフルリニューアルです。

function Yukina() {
  this.d = document;
  this.head = suzuna.head;
  return this;
}

Yukina.prototype = {
  id : function (i) {
    return typeof i === "object" ? i : this.d.getElementById(i);
  },

  //ノード挿入
  insert : function (where, thisNode, appendNode) {
    thisNode = this.id(thisNode);
    switch (where) {
      case 1: thisNode.parentNode.insertBefore(appendNode, thisNode); break;
      case 2: thisNode.insertBefore(appendNode, thisNode.firstChild); break;
      case 3: thisNode.appendChild(appendNode); break;
      case 4: thisNode.parentNode.insertBefore(appendNode, thisNode.nextSibling); break;
    }
    return thisNode;
  },

  //ノード生成
  create : function (e, p) {
    e = this.d.createElement(e);
    if (p)
      for (var i in p)
        e[i] = p[i];
    return e;
  }
}

/*
TransientEdition使用不可
  yukina.insert(新型)
  yukina.create(新型)
  yukina.text()
  yukina.multiLine()
  yukina.nest()
  yukina.createInsert()
  yukina.createNest()
  yukina.remove()
  yukina.createSelectbox()
*/

 これが今日現在の yukina のソースコード。id() insert() create() の3つのメソッドだけ実装してます。Transient EditionではDOMからの要素生成はほとんど使ってないので、こんなんで充分です。
 で、下が6月1日に適用される新型。

function Yukina() {
  this.d = document;
  this.body = brws.IE && !brws.compatMode ? this.d.documentElement : this.d.body;
  this.head = suzuna.head;
  return this;
}

Yukina.prototype = {
  id : function (i) {
    return typeof i === "object" ? i : this.d.getElementById(i);
  },

  //ノード挿入
  insert : function (where, thisNode, appendNode) {
    thisNode = this.id(thisNode);
    var appendNodes, i;
    if (appendNode instanceof Array)
      appendNodes = appendNode, i = 0;
    else
      appendNodes = arguments, i = 2;

    do {
      switch (where) {
        case 1: thisNode.parentNode.insertBefore(appendNodes[i++], thisNode); break;
        case 2: thisNode.insertBefore(appendNodes[i++], thisNode.firstChild); break;
        case 3: thisNode.appendChild(appendNodes[i++]); break;
        case 4: thisNode.parentNode.insertBefore(appendNodes[i++], thisNode.nextSibling); break;
      }
    } while (i < appendNodes.length);

    return thisNode;
  },

  //ノード生成
  //生成されたノード = yukina.create(要素名, {属性 : 値});
  //テキストノード = yukina.create(偽, 文字);
  //ノード(配列) = yukina.create({ element : 要素名1, 属性1 : 値1}, { text : 文字2}, テキストノード3);
  //ノード(配列) = yukina.create({ element : 要素名1, 属性1 : 値1}, { element : 要素2});
  create : function () {
    var i, r, createNodes = arguments[0] instanceof Array ? arguments[0] : arguments;

    if (typeof createNodes[0] === "string") {
      r = this.d.createElement(createNodes[0]);
      if (createNodes[1])
        for (i in createNodes[1])
          r[i] = createNodes[1][i];
      return r;

    } else if (typeof createNodes[1] === "string") {
      return this.d.createTextNode(createNode[1]);

    } else {
      for (i = 0, r = new Array(); i < createNodes.length; i++) {
        if (createNodes[i].element) {
          r[r.length] = this.d.createElement(createNodes[i].element);
          for (var j in createNodes[i])
            if (j !== "element")
              r[r.length -1][j] = createNodes[i][j];
        } else if (createNodes[i].text)
          r[r.length] = this.text(createNodes[i].text);
        else
          r[r.length] = createNodes[i];
      }
      return r;
    }
  },

  //複数ノードの入れ子挿入
  nest : function () {
    var i, j, k, nestNode = arguments[0] instanceof Array ? arguments[0] : arguments;
    var reg = /^(br|hr|img|input|col|meta|link|\#text)$/i;
    for (i = nestNode.length -1; i > 0; i--) {
      if (nestNode[i -1].nodeName.match(reg)) {
        for (j = i -2; j >= 0; j--) {
          if (!nestNode[j].nodeName.match(reg)) {
            var insertNode = nestNode[j];
            for (k = j +1; k <= i; k++)
              this.insert(3, insertNode, nestNode[k]);
            i = j +1;
            break;
          }
        }
      } else
        this.insert(3, nestNode[i -1] , nestNode[i]);
    }
    return nestNode[0];
  },

  //親ノード生成+複数ノード挿入(尻にどんどん追加・文字列はテキストノードを生成)
  createInsert : function () {
    var createNode = typeof arguments[0] === "string" ? this.create(arguments[0]) : this.create(arguments[0])[0];
    for (var insertNodes = new Array(), i = 1; i < arguments.length; i++)
      insertNodes[insertNodes.length] = (typeof arguments[i] === "string") ? this.text(arguments[i]) : arguments[i];
    return this.insert(3, createNode, insertNodes);
  },

  //複数ノード一括生成+入れ子挿入
  createNest : function () {
    var arg = arguments[0] instanceof Array ? arguments[0] : arguments;
    for (var i = 0, a = new Array(); i < arg.length; i++)
      a[a.length] = arg[i];
    return this.nest(this.create(a));
  },

  //text  テキストノード生成
  text : function (i, s) {
    try {
      if (arguments.length > 1)
        throw new Error();
      return this.id(i).firstChild.data;

    } catch (err) {
      if (typeof i === "string")
        return this.d.createTextNode(i);

      else if (i instanceof Array) {
        for (var j = 0; j < i.length; j++)
          i[j] = this.d.createTextNode(i[j]);
        return i;

      } else
        return this.insert(3, i, this.d.createTextNode(s));
    }
  },

  //ノード削除
  remove : function (i) {
    i = this.id(i);
    i.parentNode.removeChild(i);
  }
}

 これ作ったのはもう1年以上も前(それだけリニューアルが長引いてるってことです)であり、やっとのデビューです。まぁフライングなんですが、そろそろライブラリ自体の実践試験をしておこうと思いまして。
 …思い出すなぁ。yukina.insert()初めて do 〜 while を使ったんだよなぁ。それまでは「これ絶対一生使わないよね」なんて思ってたっけなぁ。まさか必要になる局面が出るとは夢にも思ってなかったっけなぁ。
 yukina.nest() とか、もうなにがなんだかわかりません。憶えているのは、

node = yukina.nest(ノードA, ノードB, テキストノード1, ノードC, テキストノード2);

 とやると、

<A><B>テキスト1<C>テキスト2</C></B></A>

 みたいに入れ子にして、先頭ノード(A)を返してくるメソッド…ってことだけです。
 ちなみに、オプションメソッドの yukina.createNest() を使えば、ノードの生成と入れ子挿入を同時にこなすこともできます。DOMでは基本的に内側のノードからひとつひとつ入れていかないといけないので、面倒くさいだけでなく感覚的に逆です。そこで、上から一発で入れられるものを作った次第。自画自賛ですが便利です。…作った本人が構造も忘れちゃってるけど。(まぁ再解析すればすむことです)

 他にも navs.reference() navs.dialog() nazuna.onscroll() nazuna.hook() nazuna.dsk() などの追加メソッドが、Valid Editionから続々輸入されました。それでいて未輸入のメソッドがまだまだあるわけだから、Valid Editionのライブラリがいかに巨大なものかがわかるってものです。"wisteria" と "narcissus" に至っては、クラス自体が未使用だし…。うーん。

wisteriaサイト設定のサンプルでテスト版が使われてますが、現在は跡形もないくらい新しく組み直されてます。


文字化けGecko

2005.5.26.Thu

 俺が愛用のブラウザはSleipnirですが、これはActiveXモジュールを組みこむとGeckoでの表示もできます。いちいちクソ重たいNetscapeを起動しなくても手軽に表示確認ができて、かなり重宝してます。(でも完全に同じじゃないのが悩みどころ)

 このモジュールは日本語にも対応してますが、基本的に英語版であるため、ところどころで無理があります。
 その代表が「JavaScriptから日本語を出力すると化ける」です。

[ 文字化けしたセレクトボックス / 29KB ]

 こんな具合に。ページの右上にあるセレクトボックスはJavaScript出力で、DOM非対応ブラウザでは表示されません。(左上の階層メニューは表示されるので問題はないと考えております)
 で、日本語を使ってますから見事に化けます。同じGeckoエンジンを使っているNetscape(日本語版)では問題なく動きます。

 そこで、この日本語の部分を escape() しておき、出力時に unescape() しようと考えました。IEとGeckoでは escape() で変換されるコードが異なるので、Geckoにのみ適用します。
 …うまくいきません。Geckoなのに、エンコードにIEの方式を使ってやがります。本来はIEコンポーネントブラウザであるSleipnirに組みこんでいるからでしょうか。(えー)
 当然、これをやるとNetscapeのGeckoが化けます。SleipnirのGeckoとNetscapeのGeckoを確実に見分けることはできませんから、escape() は使えないことが判明しました。

 そこで第2案。charCodeAt() を使ってLatin1に変換しておき、これを数値参照に加工してHTMLとして出力してやります。これはうまくいきました。
 でもこの方法はとても面倒くさく、フォームにテキストを入れてボタンを押せば一発変換!みたいなスクリプトを作らないとやってられません。
 …というわけで作ろうと思いましたが、同じようなことを考えてる人がすでにおられたようです。ありがたく使わせていただくことにしました。

 be Strict / 数値文字参照変換スクリプト

 これで得られた不可思議暗号を出力してやれば、ブラウザの方で勝手に解読してくれます。
 ただ、この場合はHTMLとして解釈させないといけないので、document.createTextNode() は使えません。不本意ですが innerHTML を使う必要があります。document.write() を使えればいいんですが、スクリプトの大部分は onload で実行される構造上、それは無理。
 この辺を解決できるメソッドってないのかなぁ…。document.createTextNode("&lt;") がちゃんと < になる方法。

追記:2005.5.28
俺の単純ミスでした。翌日の記事を参照。

思わぬ超バグ

2005.5.27.Fri

 昨日の問題で大変な事態が発覚しちまいました。
 「JavaScriptから日本語を出力すると化ける」は間違いでした。

 というのも、試しに document.write("文字"); とかやっても、普通に動いたんです。
 これはなんか変だと思い、原因を探るべくテストファイルを作って試したところ、どうやっても再現できない。ちゃんと正しく動くのです。おかしいです。これはどういうことでしょう。
 そうやってるうちに、この問題はvaccine.js から日本語を出力すると発生することが判明しました。vaccine.jsvirus.js からインポートされる外部ファイルで、siteCTSにあるJavaScriptの中枢です。

 それじゃあ、ということでテストファイルを作って最低限の実装で試してみたのですが、困ったことにどうやっても再現できない。ちゃんと正しく動くのです。おかしいです。これはどういうことでしょう。

 だとしたら、vaccine.js でなく virus.js に問題があるのです。

 そうして、スクリプトを最初から最後まで解析していくわけですが、始めてすぐに気がつきました。

■クラス "suzuna" に実装されたインポートメソッド

//   suzuna.include //
function Suzuna_include(f, i, r, t) {
  var e;
  if (i == 1) {
    if (brws.OP70)
      document.writeln("<script type='text/javascript' charset='EUC-JP' src='" + this.root + f + ".js'></script>");
    else {
      e = document.createElement("script");
      e.type = "text/javascript";
      e.charset = "EUC_JP";
      e.src = this.root + f + ".js";
      this.head.appendChild(e);
    }
  } else {
    e = document.createElement("link");
    e.href = this.root + f + ".css";
    e.type = "text/css";
    e.rel = r ? "alternate stylesheet" : "stylesheet";
    if (i) e.id = i;
    if (t) e.title = t;
    this.head.appendChild(e);
  }
}

 さて。どうしたもんでしょう。

e.charset = "EUC_JP";

 なんでしょうこれは。

 もう間抜けすぎてグゥの音も出ません。
 いや、ちょっと待ってよ。このメソッド実装させたのって2年前だよ? 今までずっと気づかなかったのかよ。
 そもそも、IEとかNetscapeとかOperaとかが適当に修正しちゃうから気がつかないんですよ。ちゃんと charset を解釈してよ。余計なことしないでよ。
 とゆーわけで、いちいち charCodeAt() なんか使わなくても普通に表示できるようになりました。

 あぁもう恥ずかしい。ひろちゃん顔真っ赤ですぅ。y=-( ゜д゜)・∵;; ターン

答え
× e.charset = "EUC_JP";
○ e.charset = "EUC-JP";

グッバイNetscape6

2005.5.28.Sat

 …。

 ……。

 ………。

 Netscape6.2が落ちる…。

 新しく登場するコンテンツの1ページを開くと、必ず落ちます。他のページは大丈夫なのに。テーブルが多いくらいで、他のページと同じなのに。
 で、テーブルをいくつか消してみると表示されます。なんなんでしょう。テーブルが多すぎるのが原因? そんな馬鹿な。多すぎるったって、10個かそこらですよ? なんの変哲もない普通の表ですよ? 俺紹介--のスペックテーブルの方がよっぽど巨大ですよ?
 他、CSSを少々いじくったのですが、JavaScriptからインポートしたやつが適用されなくなりました。全ページで適用されないならまだしも、2ページでだけ適用されます。かといって、この2ページと他のページでなにか差があるわけでもありません。

 いったいなんなんでしょう。たぶんブラウザのバグ絡みだと思いますが、八方手を尽くしても原因がさっぱり掴めません。表示が変とかなら「ごめんね!」ですむけど、必ず落ちるのはヤバすぎます。やばい。
 Netscapeの不可思議挙動で苛々すると、4.x時代に苦労してた頃の悪夢が蘇ってきて、余計にストレスがたまります。あーもー。
 7.1ではなんの問題もないんだけどなー。

 で、打ち出した対策がNetscape6.xのサポート終了なわけです。つーか、今どきいねーし。少なくともうちのサイトにはここ数ヵ月で1人か2人か来たくらいで、しかもソレ自分かもしれない…みたいな状況です。サポート終了ったって、IE4やIE5.0と同じ扱いになるだけで、別にいきなりプレーンテキストになるわけでもないし。

 siteCTSのJavaScriptは、熟成の甲斐あってモジュール化がわりと良くできてる(自画自賛)ので、こういう時でも簡単な修正だけですみます。

■Brwsクラス(ブラウザ判別係)の内部処理
Win版IE5.5以降 || Netscape7.0以降 || Opera7.0以降
if ((this.IE55 && this.win) || this.NN70 || this.OP70)
  this.check = 1; //checkフラグを入れる(コアスクリプトの実行対象)

 今まで this.NN62 だった部分を this.NN70 にするだけ。たったこれだけでNetscape6.xは go to 蚊帳の外。
 ただ、コアスクリプトの vaccine.js を読ませなきゃ落ちないし、CSSの実装はIEなんかより全然マシだから、これだけじゃもったいないってんで専用のスクリプトを作ってあげました。

■スクリプトの起動準備部分
if (brws.check) {
  //インスタンス生成・メソッド実装・vaccine.jsのインクルードなど
  //Netscape6.xはフラグが立たなくなったのでスルーされる

//Netscape6.x用に加えた処理
} else if (brws.NN60) {
  document.writeln("<link rel='stylesheet' type='text/css' href='/transient/sys/css/basic/transient.css' />");
  if (brws.win) { //ベースフォント(Windowsのみ)
    document.write(
      "<style type='text/css'>\n"+
        "BODY { font-family:Verdana, 'MS UI Gothic';}\n"+
        "DIV#navfrm { font-family:'Century Gothic', 'MS UI Gothic';}\n"+
      "</style>\n"
    );
  }
}

 今まで suzuna.include() から呼んでいたCSSを、単純な document.write() で出力する。
 これで「スクリプト機能は動かないけど表示は今までとまったく同じ」にできる、と。

今どきNetscapeもないか。いいかげんFirefox入れた方がええかのう。(Netscape7.xとの共存は可能なのかな?)


 今まで混沌としていたCSSを大整理しました。後づけで適当に足してたツケがまわってきました。見た目が変わってないのはいつものことだけど、ほとんど一からやり直してます。
 中で一番大きい変化は「PESコレクション」でしょーか。

■PESコレクション
<p class="クラス名">
  <em class="ttl">タイトル<br /></em>
  <span class="stc">本文</span>
</p>

 今までこんな方法を多用していて、基本スタイルを適用しつつ、対応ブラウザでのみ適用されるCSSで <em><span>display:block にして、デザインの自由度を高めようというものです。<br /><em> の中に入ってるのがポイントです。<P><Em><Span> を使うから「PES」です。
 それでいてCSSが効かない時は1段落にまとまり、かつタイトルは <em> で強調されます。

 見た目の上ではかなり上等なんですが、マークアップが汚くなるのが難点でして、本文が長くても段落を入られません。さすがにXHTMLやってると「1行空けたいから <br /> を2個入れよう!」なんて愚挙も犯せないし。
 だからといって <p><div> にしたら、CSSが効かない時に見にくくなります。そもそも、<div> は複数のグループをまとめるコンテナとして使うべきと考えます。
 ついでに、基本スタイルを上書きする形で追加スタイルが入るので、継承とかを考えるのも非常に面倒くさいです。

 …というわけで、これを改め、より汎用性の高いマークアップにしました。
 たとえば <ins>(追記)のマークアップは

<p class="ins">
  <ins datetime="2005-05-30T06:20:37+09:00">
    <em class="ttl">追記:2005.5.30<br /></em>
    <span class="stc">追記内容</span>
  </ins>
</p>

 …となってましたが、これを

<dl class="common ins">
  <dt>追記:2005.5.30</dt>
  <dd><ins datetime="2005-05-30T06:21:46+09:00">
    追記内容
  </ins></dd>
</dl>

 …にしました。common クラスに基本スタイル、ins クラスに個別スタイルを適用する方式。マークアップも綺麗だし、スタイル指定も楽です。CSSが効かなくても <dl> だからタイトル部分だけ目立つように表示されます。(音声ブラウザでどう表現されるのかは知りませんが)

 これらPESコレクションはサイトの至るところで使われています。特に追記が多くて、これだけで100ヵ所以上もあります。しかも大部分はCGIで管理されるScrawl Notesにあるので、ログデータの方を修正しないといけません。とてもじゃないけど手作業でやるには非現実的です。

 でも大丈夫。そんな時の正規表現。

■対象文字列
<p class="ins">\n\t<ins datetime="2005-05-30T06:20:37+09:00">\n\t\t<em class="ttl">追記:2005.5.30<br /></em>\n\t\t<span class="stc">追記内容</span>\t</ins>\n</p>

■検索文字列
<p class="ins">\n\t<ins( datetime="[0-9\-T\:\+]+")?>\n\t\t<em class="ttl">追記:([0-9.]*)[^\t]+\t\t<span class="stc">(.+)</span>\n\t</ins>\n</p>

■置換文字列
<dl class="common ins">\n\t<dt>追記:\2</dt>\n\t\t<dd><ins\1>\n\t\t\3\n\t</ins></dd>\n</dl>

 終わり。正規表現を組むのに3分、置換作業に数秒。まぁ変な書き方してて検索に漏れたのが数ヵ所あったから、実際には15分くらいかかったけど。手作業だと1日かかる作業が18分数秒で終わるわけですから、正規表現とはなんと素晴らしい技術なのでしょう。こんな便利な技術を開発してくれた功績に心から感謝したい所存です。

 で、もう少し具体的に感謝するために、その歴史について少し調べてみました。そのルーツは神経回路網を記述するための数学的な方法として開発されたものみたいですね。その一方でチョムスキー階層に由来するものだという説もあって、どっちがどうなんだか。
 どっちにしても、正規表現はUNIXに組みこまれ、文字の検索とかにちょうどいいからってテキストエディタに組みこまれたり、レポート作成言語をルーツとするPerlも採用したりで広まっていき、現在に至るとのことです。

 いやー、でも「grep」ってなんだろ?とずっと思ってたんですが、g/Regular Expression/p の略だったんですね。心のつかえがひとつ取れました。

 あと、CSSを改造するにあたって、すっかり恒例のIEの邪魔標準モードにならない云々)があるわけですが、ページのXML宣言を除去するという暴挙に出ました。バグが仕様を覆した痛快な一幕。その他のブラウザにはすまないことをしたと思っていますが、まぁ一時的なものなので許せ。IEの大軍勢は君たち少数精鋭では太刀打ちできぬのだ。

スタイルシートのためにHTMLをいじるのは感心できませんが、ページ数の少ない "Transient Edition" であるうちに熟成させておいて、"Valid Edition" でのリスクを最小限に抑えようという魂胆です。トップページでも「地下実験室的サイト」と明示しております。


グッバイ拡張子

2005.5.30.Mon

 今日はサイトをコンテントネゴシエーションに対応させました。いや、レンタルサーバーの方はデフォルトで対応してるんですが、ローカルサーバー(確認用)が非対応だったんで、今までそのままだったのです。

コンテントネゴシエーション

受け取るファイル(コンテント)を交渉(ネゴシエーション)すること。簡単に言えばHTTPサーバーが状況に応じて最適なファイルを選ぶこと。突っこんで言えばブラウザが「英語のファイルあったら優先的にちょうだい。なければ他のでもいいや」というような要求をして、サーバーが最適なファイルを選ぶこと。

たとえば index.ja.html(日本語)と index.en.html(英語)のふたつを用意しておき、index(拡張子抜き)にアクセスすると、日本語ブラウザには index.ja.html、英語ブラウザには index.en.html をサーバーが自動で選んでくれるような機能。
同様に、index.html と index.cgi を用意しておくと、状況に応じてサーバーが選んでくれる。

これによって、URLから拡張子がなくなる。/transient/scn/psn/0505.xht/transient/scn/psn/0505(拡張子がない)でアクセス可能になり、将来HTMLの拡張子が変わるようなことがあっても大丈夫!ってな具合。
進化の止まらないPC業界、.html のまま一生やっていけるわけがないし、恒久的なURLを提供するには拡張子は邪魔者でしかないんである。(現に当サイトはXHTML1.1へ移行した際に .xht へ変更しているわけだし)

ま、メリットばかりではないんだけど。

 例によってまたIEが邪魔してくれたりもしましたが、これで全ファイルから拡張子が消えました。(実際にはあるんだけど)
 めでたしめでたし。

驚くことに IE では text/html がありません。これではサーバが適切な文書を選んであげることもできないです。こんな基本的なところでもダメダメなのですね…。

XMLの利点を模索すべくコンテントネゴシエーションに悪戦苦闘 #4

画像ファイルに関しては従来通り拡張子付きのままです。IEちゃんはお馬鹿だから拡張子がないとBMPでしか保存できないのです。(当サイトの画像をローカルに保存したがる人がいるのかは別の話として)