JavaScriptでcookie取得 その2

IEcookieの挙動にハマったので書いておく。


まず下記のようにcookieを保存する。

document.cookie = 'hoge=piyo; path=/';
document.cookie = 'foo=; path=/';


cookie値を正規表現を使って取得

GetCookie('hoge');
GetCookie('foo');

function GetCookie(name) {
	var regexp = new RegExp('; ' + name + '=([^;]*);');
	var match  = ('; ' + document.cookie + ';').match(regexp);
	if (match) {
		alert(name + '=' + match[1]);
	}
	else {
		alert(name+'に一致するcookieが見つかりませんでした');
	}
}


実行結果は以下のようになる。
Firefox, Opera

hoge=piyo
foo=

IE

hoge=piyo
fooに一致するcookieが見つかりませんでした


IEはなぜ?? となったので調べてみたところ、どうもIEでは、cookie保存時に値を無しにすると
foo
とだけ保存される仕様らしい。


なので仕方なくIE対応版(正規表現のところを変更)

GetCookie('hoge');
GetCookie('foo');

function GetCookie(name) {
	var regexp = new RegExp('; ' + name + '(=([^;]*))?;'); // ←ここを変更
	var match  = ('; ' + document.cookie + ';').match(regexp);
	if (match) {
		alert(name + '=' + match[2]);
	}
	else {
		alert(name+'に一致するcookieが見つかりませんでした');
	}
}


これを実行すると、Firefox,Opera,IE共に

hoge=piyo
foo=

となり、対応可能。


Safariだとどうなんだろう…? その為だけにMac買うか…?

JavaScriptでcookie取得 その1

IEってcookieの保存場所がわかりにくいですよね。
ツールを使ったりしてたんですが、ちょっとした事の確認にもそいつを使うのはめんどくさすぎる…、と思っていたところ。

あ。


これって、URL入力欄に

javascript:alert(document.cookie);

てやればいいだけじゃないか!


ということにすんごい後で気づく。

自戒をこめて書いておく。

DxTex.exeが起動できない

DirectX SDKをインストールすると付属されてくる、αチャンネル付テクスチャ作成ソフト「DxTex.exe」。
一応の格納ディレクトリ↓
C:\Program Files\Microsoft DirectX SDK (August 2007)\Utilities\Bin\x86DxTex.exe


で、このソフトを起動しようとすると、

「Unable to create Direct3D Device. Please make sure your desktop color depth is 16 or 32bit, and that d3dref.dll is installed」

というエラーメッセージが出されて起動できませんでした。
デスクトップの色はデフォルトで32bitなので、問題なし。ただ、d3dref.dllが検索しても出てきません。
そこでとりあえず「d3dref.dll ダウンロード」でWeb検索しましたが、めぼしいものが出てこない為、めんどくさくなり。
そもそも、これ本来なら普通に起動できるものらしいので友人にこのソフト起動できるか試してもらったら、出来たし。
で、d3dref.dll検索してもらったら存在したので、その友人から頂きました。ただ、なんかいっぱいあった。
d3dref.dll, d3dref8.dll, d3dref9.dll
どれが必要なのか分からないので全部入れたら動きましたとさ。


ちなみに入れたディレクトリはここです↓
C:\WINDOWS\system32


以上。

JKL.ParseXMLの属性取得の使い方他

XMLJSONに変換してくれるAjax用ステキライブラリJKL.ParseXML
他のライブラリ(prototype.jsなど)に依存していないので、単体で動きます。ちなみに修正BSDライセンスだそーです。
JavaScriptは書けるけどAjax部分面倒くさい、という方にオススメ。凄く便利。
で、もちろん、JKL.ParseXMLのサイトに使い方がかなり詳しく書いてあるのですが、いかんせんアホなので、分からない部分が多い。
「こういう場合どうするんだ?」って悩んだ所を備忘録的に書いておきます。ではいきます。


使用するサンプルXMLは以下とします。ファイル名:sample.xml

<?xml version="1.0" encoding="Shift_JIS" ?>
<CS_DATA param="GAME">
  <CS_DATA1 param="MAKER_CD" code="01" name="SEGA">
    <CS_DATA1 param="HARD_CD" code="01_001" name="セガサターン"/>
    <CS_DATA1 param="HARD_CD" code="01_002" name="ドリームキャスト"/>
  </CS_DATA1>
  <CS_DATA1 param="MAKER_CD" code="02" name="SONY">
    <CS_DATA1 param="HARD_CD" code="02_001" name="プレイステーション"/>
  </CS_DATA1>
</CS_DATA>


まず属性の取得方法。

var url = "sample.xml";
var http = new JKL.ParseXML( url );
var data = http.parse();

// 全体属性(GAME)
document.write(data["CS_DATA"]["param"] + "<br><br>");
// セガ
document.write(data["CS_DATA"]["CS_DATA1"][0]["param"] + ":" 
             + data["CS_DATA"]["CS_DATA1"][0]["name"] + "<br>");
document.write(data["CS_DATA"]["CS_DATA1"][0]["CS_DATA1"][0]["param"] + ":" 
             + data["CS_DATA"]["CS_DATA1"][0]["CS_DATA1"][0]["name"] + "<br><br>");
// ソニー
document.write(data["CS_DATA"]["CS_DATA1"][1]["param"] + ":" 
             + data["CS_DATA"]["CS_DATA1"][1]["name"] + "<br>");
document.write(data["CS_DATA"]["CS_DATA1"][1]["CS_DATA1"]["param"] + ":" 
             + data["CS_DATA"]["CS_DATA1"][1]["CS_DATA1"]["name"]);

出力結果:

GAME

MAKER_CD:SEGA
HARD_CD:セガサターン

MAKER_CD:SONY
HARD_CD:プレイステーション 


よく見るとソニーだけ少し書き方が違ったりします。
セガで「セガサターン」を取得する所が、

data["CS_DATA"]["CS_DATA1"][0]["CS_DATA1"][0]["name"]

に対し、ソニーの「プレイステーション」を取得する場合は、

data["CS_DATA"]["CS_DATA1"][2]["CS_DATA1"]["name"]

になります。

どうも下階層が1つしか存在しない場合に配列展開が微妙に変わるようです。この場合は、

<CS_DATA1 param="MAKER_CD" code="02" name="SONY">
    <CS_DATA1 param="HARD_CD" code="02_001" name="プレイステーション"/>
</CS_DATA1>

と、プレイステーションの部分が1階層しかない為にこうなります。自分、ここで思いっきりハマりましたよ…。



こういった階層による微妙な違いを意識したくない場合は、全ての要素を配列展開するsetOutputArrayAll()メソッドがあります。
以下のように使います。データ構成は冗長になりますが…。

var url = "sample.xml";
var http = new JKL.ParseXML( url );
http.setOutputArrayAll(); // setOutputArrayAll()メソッド、全ての要素を配列展開
var data = http.parse();

// 全体属性(GAME)
document.write(data["CS_DATA"][0]["param"] + "<br><br>");
// セガ
document.write(data["CS_DATA"][0]["CS_DATA1"][0]["param"] + ":" 
             + data["CS_DATA"][0]["CS_DATA1"][0]["name"] + "<br>");
document.write(data["CS_DATA"][0]["CS_DATA1"][0]["CS_DATA1"][0]["param"] + ":"
             + data["CS_DATA"][0]["CS_DATA1"][0]["CS_DATA1"][0]["name"] + "<br><br>");
// ソニー
document.write(data["CS_DATA"][0]["CS_DATA1"][1]["param"] + ":" 
             + data["CS_DATA"][0]["CS_DATA1"][1]["name"] + "<br>");
document.write(data["CS_DATA"][0]["CS_DATA1"][1]["CS_DATA1"][0]["param"] + ":"
             + data["CS_DATA"][0]["CS_DATA1"][1]["CS_DATA1"][0]["name"]);

出力結果:

GAME

MAKER_CD:SEGA
HARD_CD:セガサターン

MAKER_CD:SONY
HARD_CD:プレイステーション 

これで全て同じようにかけるようになりました。
ちなみに全属性を取得する場合(入れ子過ぎますね…):

var url = "sample.xml";
var http = new JKL.ParseXML( url );
http.setOutputArrayAll(); // setOutputArrayAll()メソッド、全ての要素を配列展開
var data = http.parse();

for ( var i = 0; i < data["CS_DATA"].length; i++ ) {
    // 全体属性GAME
    document.write(data["CS_DATA"][i]["param"] + "<br><br>");

    for ( var j = 0; j < data["CS_DATA"][i]["CS_DATA1"].length; j++ ) {
        // メーカー
        document.write(data["CS_DATA"][i]["CS_DATA1"][j]["param"] + ":"
                     + data["CS_DATA"][i]["CS_DATA1"][j]["name"] + "<br>");

	for ( var k = 0; k < data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"].length; k++ ) {
            // ハード
            document.write(data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"][k]["param"] + ":"
                         + data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"][k]["name"] + "<br>");

	}
	document.write("<br>");
    }
}

全属性取得の出力結果:

GAME

MAKER_CD:SEGA
HARD_CD:セガサターン
HARD_CD:ドリームキャスト

MAKER_CD:SONY
HARD_CD:プレイステーション

ちなみにsetOutputArrayAll()メソッドの部分も他のメソッドに変えると他の挙動をします。
公式サイトの説明見れば分かると思うので特に書きませんが…。



あと、凄い単純なのにアホなので全く気づかなかった事を書いて終わりにします。
今まで書いたやつは全て同期モードですが、非同期モードの書き方です。サイトの説明通りに書くと、

var url = "sample.xml";
var http = new JKL.ParseXML( url );
var func = function(data) { // 呼出先関数定義
    for ( var i = 0; i < data["CS_DATA"].length; i++ ) {
        // 全体属性GAME
	document.write(data["CS_DATA"][i]["param"] + "<br><br>");

	for ( var j = 0; j < data["CS_DATA"][i]["CS_DATA1"].length; j++ ) {
            // メーカー
	    document.write(data["CS_DATA"][i]["CS_DATA1"][j]["param"] + ":"
                         + data["CS_DATA"][i]["CS_DATA1"][j]["name"] + "<br>");

	    for ( var k = 0; k < data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"].length; k++ ) {
                // ハード
		document.write(data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"][k]["param"] + ":"
                             + data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"][k]["name"] + "<br>");

	    }
	    document.write("<br>");
	}
    }
}
http.async( func ); // 呼出先関数指定
http.setOutputArrayAll(); // setOutputArrayAll()メソッド、全ての要素を配列展開
http.parse();

こんな感じになります。出力結果は、上の「全属性取得の出力結果」と同じね。
ただ、この書き方だと、呼出先関数定義の関数がやたら長くなった場合分かりにくい。
どうしようもないのかなぁ、と思っていたら、こう書けば良かったんですね…。

var url = "sample.xml";
var http = new JKL.ParseXML( url );
// 呼出先関数定義&指定
http.async(
    function(data) {
	for ( var i = 0; i < data["CS_DATA"].length; i++ ) {
            // 全体属性GAME
	    document.write(data["CS_DATA"][i]["param"] + "<br><br>");

	    for ( var j = 0; j < data["CS_DATA"][i]["CS_DATA1"].length; j++ ) {
                // メーカー
		document.write(data["CS_DATA"][i]["CS_DATA1"][j]["param"] + ":"
                             + data["CS_DATA"][i]["CS_DATA1"][j]["name"] + "<br>");

		for ( var k = 0; k < data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"].length; k++ ) {
                    // ハード
		    document.write(data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"][k]["param"] + ":"
                                 + data["CS_DATA"][i]["CS_DATA1"][j]["CS_DATA1"][k]["name"] + "<br>");

		}
		document.write("<br>");
	    }
	}
    }
)
http.setOutputArrayAll(); // setOutputArrayAll()メソッド、全ての要素を配列展開
http.parse();

こうすると、どの関数を実行するのかも分かりやすくなると思います。
関数定義してから指定だと、ちょっと面倒な気もしますし。


説明は以上です。ではでは。

getElementByIdのバグ検証

JavaScriptのgetElementByIdを使うと、ブラウザによってはバグが出て思う通りの挙動をしない事があります。
自分が遭遇したのはIEだけだったのですが、他のブラウザではどうなのかと思って検証してみました。


検証ブラウザ:FireFox 2、Internet Explorer 6Opera 9.2、WindowsSafari 3.1


では、いってみましょう。
以下のようなHTMLソースがあるとします。

<html>
  <head></head>
  <body>
    <form action="test.cgi" method="post">
      <input type="checkbox" name="bug_search" />チェックボックス
    </form>
    <div id="bug_search">getElementByIdバグチェック</div>
  </body>
</html>

これに対して、以下のJavaScriptコードを実行します。

document.getElementById("bug_search").innerHTML = "書き換わりました";

上記を実行すると、"getElementByIdバグチェック" という文字列が "書き換わりました" という文字列に書き換わるはずです。


実際に各ブラウザで実行してみると、
FireFoxWindowsSafariではちゃんと "書き換わりました" という表示がされますが、IEOperaでは何の反応も起きませんでした。
では、実際に何の要素を取ってきているのかとチェックしてみると、

FireFoxWindowsSafariでは、div要素(これが想定通り)、つまり↓

<div id="bug_search">getElementByIdバグチェック</div>

IEOperaでは、input要素(これは想定外)、つまり↓

<input type="checkbox" name="bug_search" />

を取ってきています。
どうも、IEOperaでは、input要素のname属性と何らかの要素のid属性が同じ名前だと勘違いするようです。まぁ確実にバグですね。
てゆうか、IEは分かってましたが、Operaもなるとは…。驚きです。そして早く直してください。


ちなみに、このバグを避けるには、input要素のname属性とid属性が同じ名前にならないようにするのが良いと思います。
が、どうしてもそれだとダメだと言う時は、以下のようにすればなんとかなります。

var divElements = document.getElementsByTagName("DIV");
for ( var i = 0; i < divElements.length; i++ ) {
    if( divElements[i].id == "bug_search" ) {
	divElements[i].innerHTML="書き換わりました";
	break;
    }
}

全てのdiv要素とってくれば何とかなるよ!って方法ですね。もっと良い方法はあるんでしょうか?
まぁ、こんな所です。

※ところで、Internet Explorer 7で、このバグは直っているんだろうか?

1秒以下の時間計測

Perlで処理時間を計測する用がありました。
で、1秒単位はtimeで出来る。が、1秒以下は? って事で悩んだのでメモ。
Time::HiResモジュールのgettimeofdayを使えば出来る事が判明!
以下のようにすると、秒とマイクロ秒が取得できる。

use Time::HiRes qw(gettimeofday);

my ($sec, $microsec) = gettimeofday;
printf "%d %d", $sec, $microsec;

一応、実行結果↓*1

1207140957 140838


で、かなり無理やりな感が否めない処理時間計測は下記。

use Time::HiRes qw(gettimeofday);

my ($sec, $microsec) = gettimeofday;
$microsec = sprintf("%06d", $microsec);
my $time1 = $sec . '.' . $microsec;

なんか処理

($sec, $microsec) = gettimeofday;
$microsec = sprintf("%06d", $microsec);
my $time2 = $sec . '.' . $microsec;
my $syoritime = $time2 - $time1;
print $syoritime;

これで何とか、マイクロ秒までの処理時間が計測できます。
しかし、恐ろしく微妙なソースな気がする今日この頃…。

*1:当たり前だけど、実行結果は毎回変わりますよ