-->

2010-04-26

javascript の IPアドレス の 計算


操作の説明
入力エリア

IPアドレス(ipv4, マスクを含む)を入力します。

submitボタン

出力エリアに結果を出力します。

resetボタン

入力エリア出力エリアを初期化します。

2回クリックすると入力エリアにサンプルの初期値が入ります。

sampleボタン

入力エリアにサンプルを入れます。

複数回クリックするとサンプルが切り替わります。

出力エリア

結果が出力されます。

出力エリアに複数の値を入力するにはresetボタンを押してからsampleボタンを押して改行で区切って入力してください。

入力した値はsubmitボタンを押すと出力結果で上書きされます。

一括入力のサンプルを表示するにはresetボタンを押してからsampleボタンを2回押してください。

出力エリアの説明
// Date:

開始時の日付です。

original:

入力内容です。

type:

入力内容のマスクのタイプです。

result(net):

結果です。

マスクはIPアドレスです。

result(bit):

結果です。

マスクは2進数の32桁目から右に連続する1の個数です。

連続する1が終わった後は0の連続が続きます。

表現できない場合はfalseを返します。

result(range):

結果です。

マスクは範囲(最後の値)を指定しています。

結果が期待した値にならない場合があります。

(例) 192.168.0.1-192.168.0.2
192.168.0.0-192.168.0.3 になります。
(2つのIPアドレスにはなりません)

また、グローバルなIPアドレスを付与されている場合のネットワークアドレス、ブロードキャストアドレスを表現している場合があります。

(例) 192.168.222.120/29
result(range) : 192.168.222.120-192.168.222.127
IPアドレスは8個で
ネットワークアドレスは     192.168.222.120
使えるIPアドレスは         192.168.222.121 ~ 192.168.222.126
ブロードキャストアドレスは 192.168.222.127

この場合classC:の内容に (network address) (broadcast address) が付くかどうかは無関係です。

num:

IPアドレスの数です。

IPアドレス/32 の場合に1として計算しています。

addr (bin):

mask (bin):とセットでresult(net):と同じ内容を2進数で表現しています。

mask (bin):

addr (bin):とセットでresult(net):と同じ内容を2進数で表現しています。

range(bin):

addr (bin):とセットでresult(range):と同じ内容を2進数で表現しています。

addr (hex):

mask (hex):とセットでresult(net):と同じ内容を16進数で表現しています。

mask (hex):

addr (hex):とセットでresult(net):と同じ内容を16進数で表現しています。

range(hex):

addr (hex):とセットでresult(range):と同じ内容を16進数で表現しています。

classA:

0.0.0.0-127.255.255.255にマッチした場合に表示されます。

10.0.0.0-10.255.255.255にマッチした場合 (private) を追加します。

9~32ビット目が0の場合 (network address) を追加します。

9~32ビット目が1の場合 (broadcast address) を追加します。

classB:

128.0.0.0-191.255.255.255にマッチした場合に表示されます。

172.16.0.0-172.31.255.255にマッチした場合 (private) を追加します。

17~32ビット目が0の場合 (network address) を追加します。

17~32ビット目が1の場合 (broadcast address) を追加します。

classC:

192.0.0.0-223.255.255.255にマッチした場合に表示されます。

192.168.0.0-192.168.255.255にマッチした場合 (private) を追加します。

25~32ビット目が0の場合 (network address) を追加します。

25~32ビット目が1の場合 (broadcast address) を追加します。

classD:

224.0.0.0-239.255.255.255にマッチした場合に表示されます。

IPv4 Multicast Address Space Registry

classE:

240.0.0.0-255.255.255.255にマッチした場合に表示されます。

localBroadcast:

255.255.255.255にマッチした場合に表示されます。

loopBack:

127.0.0.0/8にマッチした場合に表示されます。

--- separate ---

結果の区切り文字です。

// error

エラーです。

入力内容に数字が無い場合に表示されます。

2010-04-24

javascript の alert part2


操作の説明
1つ目のtextareaタグ
数値文字参照 文字実体参照 を入力して alertボタン または noalertボタン をクリックします。
2つ目のtextareaタグ と 1つ目のdivタグ に結果が出力されます。
2つ目のtextareaタグ
文字を入力して reverseボタン をクリックします。
1つ目のtextareaタグ に結果が出力されます。
1つ目のdivタグ
alertボタン または noalertボタン をクリックした際に結果が出力されます。
2つ目のdivタグ
ボタンをクリックした際に、ボタンの内容が出力されます。
3つ目のdivタグ
ボタンをクリックした際に、処理を終了した時刻が出力されます。
1つ目のbuttonタグ
alertボタン です。
クリックした際にポップアップを表示します。
2つ目のbuttonタグ
noalertボタン です。
ポップアップを表示しない以外は alertボタン と同じです。
3つ目のbuttonタグ
reverseボタン です。
4つ目のbuttonタグ
reverseボタン の設定を、逆送りに変更します。
5つ目のbuttonタグ
reverseボタン の現在の設定を表示します。
クリックした場合 reverseボタン の設定を、順送りに変更してから reverseボタン をクリックします。
6つ目のbuttonタグ
reverseボタン の設定を、順送りに変更します。
7つ目のbuttonタグ
resetボタン です。
1つ目のtextareaタグ と 2つ目のtextareaタグ と 1つ目のdivタグ と reverseボタン の設定を初期化します。
reverseボタン の設定の説明
default
可能な場合、文字実体参照を利用します。
数値文字参照の場合、10進数の数値文字参照の方が短い場合は、10進数の数値文字参照を利用します。
dec
10進数の数値文字参照を利用します。
hex
16進数の数値文字参照を利用します。
random
10進数の数値文字参照と、16進数の数値文字参照を、ランダムに利用します。
それぞれの数値文字参照の数値部分の手前に0~9文字のゼロを追加します。
50%の確率で数値文字参照のセミコロンを削除します。
def all
default の処理をする際に、半角スペースと改行を含めて処理します。
dec all
dec の処理をする際に、半角スペースと改行を含めて処理します。
hex all
hex の処理をする際に、半角スペースと改行を含めて処理します。
rand all
random の処理をする際に、半角スペースと改行を含めて処理します。

2010-04-23

javascript の alert


javascript の alert で html に記述されたエスケープされた文字を戻してから表示する例です。
セミコロンを省略すると各ブラウザで違った表示になる場合があります。
対応するフォントが無い場合は文字化けします。

http://www.w3.org/TR/REC-html40/sgml/entities.html
unhtmlspecialchars.min.2010-04-23.js.gz
<script type="text/javascript" src="unhtmlspecialchars.min.2010-04-23.js"></script>
<script type="text/javascript">
<!--
alert("&#12307; = " + unhtmlspecialchars("&#12307;"));
//-->
</script>

ブログ(blogspot.com)の機能で javascript の alert part2 と一緒に表示した時には動作しません。
javascript の alert part2

2010-04-22

javascript の String.fromCharCode

String.fromCharCode(0x2a6b2) が失敗します。

𪚲 (&#x2A6B2;)

理屈は 65536 以上(17ビット以上。0,1が17文字以上)の場合、
unicode番号の最大値から 65536 を引くと最大で 20ビット(1が20個。9999のような桁が上がる直前)になるので、
65536 以上の数値を 65536 引いて 10ビットずつの2つに分けて、
0~65535 の内、使われていない範囲を指定する6ビットを、分けた2つのそれぞれの頭につける。
(1文字目の 6ビット) (1文字目の 10ビット) (2文字目の 6ビット) (2文字目の 10ビット)
String.fromCharCode の場合、それを String.fromCharCode(1文字目の16ビット, 2文字目の16ビット) として渡すと1文字を表現できます。
1文字目の10ビット、2文字目の10ビットが変化するので
2^10 * 2^10 = 1,048,576
ということで100万種類ぐらいになると思います。

0 ~ 65535 までは16ビット
0xFFFF (bin)1111111111111111
0xFFFF (dec)65535
0xFFFF (bin, length)16
65536 以上は17ビット以上
0x10000 (bin)10000000000000000
0x10000 (dec)65536
0x10000 (bin, length)17
unicode の文字数
0x10FFFF (bin)100001111111111111111
0x10FFFF (dec)1114111
0x10FFFF (bin, length)21
65536 を引くと20ビット
0x10FFFF - 0x10000 (bin)11111111111111111111
0x10FFFF - 0x10000 (dec)1048575
0x10FFFF - 0x10000 (bin, length)20
num = 0x2A6B2 の場合
String.fromCharCode(0x2a6b2)文字化け
num (bin)101010011010110010
num (dec)173746
num (bin, length)18
# num (hex)2a6b2
num - 65536 した場合
num - 0x10000 (bin)11010011010110010
num - 0x10000 (dec)108210
num - 0x10000 (bin, length)17
# num - 0x10000 high,low1101001,1010110010
サロゲートペア1文字目に移動させる値
0xD800 (bin)1101100000000000
0xD800 (dec)55296
0xD800 (bin, length)16
# 0xD800 high,low110110,0000000000
# 110110 00 00000000d800
# 110110 01 00000000d900
# 110110 10 00000000da00
# 110110 11 00000000db00
# 110110 11 11111111dbff
サロゲートペア2文字目に移動させる値
0xDC00 (bin)1101110000000000
0xDC00 (dec)56320
0xDC00 (bin, length)16
# 0xDC00 high,low110111,0000000000
# 110111 00 00000000dc00
# 110111 01 00000000dd00
# 110111 10 00000000de00
# 110111 11 00000000df00
# 110111 11 11111111dfff
サロゲートペア1文字目
high ((num - 0x10000) >> 10)1101001
high mask1101100000000000
high result1101100001101001
high result (dec)55401
サロゲートペア2文字目
parseInt("1111111111", 2).toString(16)3ff
low ((num - 0x10000) & 0x3FF)1010110010
low mask1101110000000000
low result1101111010110010
low result (dec)57010
String.fromCharCode に2つ指定
String.fromCharCode(55401, 57010)𪚲

この文字設定をサロゲートペアといいます。
http://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_D000-DFFF
http://ja.wikipedia.org/wiki/Unicode#.E3.82.B5.E3.83.AD.E3.82.B2.E3.83.BC.E3.83.88.E3.83.9A.E3.82.A2

2進数の 1xxxxxxxxxx >> 10 は 1になります。
// 例. 一桁目が消えて2桁目が1桁目になります。(2で割る。右シフト)
alert(parseInt("010", 2) >> 1); // 1 (01)
alert(parseInt("100", 2) >> 1); // 2 (10)
alert(parseInt("110", 2) >> 1); // 3 (11)
alert(parseInt("011", 2) >> 1); // 1 (01)
alert(parseInt("101", 2) >> 1); // 2 (10)
alert(parseInt("111", 2) >> 1); // 3 (11)

2進数の ??????????xxxxxxxxxx & 0x3FF(2進数で1が10個) は xxxxxxxxxx になります。
// 例. それぞれの桁で両方とも1の場合に、それぞれの桁が1になります。
alert(parseInt("010", 2) & parseInt("001", 2)); // 0 (000)
alert(parseInt("100", 2) & parseInt("001", 2)); // 0 (000)
alert(parseInt("110", 2) & parseInt("001", 2)); // 0 (000)
alert(parseInt("011", 2) & parseInt("001", 2)); // 1 (001)
alert(parseInt("101", 2) & parseInt("001", 2)); // 1 (001)
alert(parseInt("111", 2) & parseInt("001", 2)); // 1 (001)

high result, low result はオア演算です。
// 例. それぞれの桁でどちらかに1があれば、それぞれの桁を1にします。
alert(parseInt("010", 2) | parseInt("001", 2)); // 3 (011)
alert(parseInt("100", 2) | parseInt("001", 2)); // 5 (101)
alert(parseInt("110", 2) | parseInt("001", 2)); // 7 (111)
alert(parseInt("011", 2) | parseInt("001", 2)); // 3 (011)
alert(parseInt("101", 2) | parseInt("001", 2)); // 5 (101)
alert(parseInt("111", 2) | parseInt("001", 2)); // 7 (111)

ビット演算子は、他の言語でも同じように記述できる場合が多いです。
http://php.net/manual/ja/language.operators.bitwise.php

2進数、16進数は、理屈がわかっていれば暗算はできなくていいと思います。
buf = "";

buf += "### 0\n";
buf += parseInt("0",  2) + "\n"; // 0
buf += parseInt("0", 10) + "\n"; // 0
buf += parseInt("0", 16) + "\n"; // 0

buf += "### 1\n";
buf += parseInt("1",  2) + "\n"; // 1
buf += parseInt("1", 10) + "\n"; // 1
buf += parseInt("1", 16) + "\n"; // 1

//  2進数の10 = 0 1                             10
// 10進数の10 = 0 1 2 3 4 5 6 7 8 9             10
// 16進数の10 = 0 1 2 3 4 5 6 7 8 9 A B C D E F 10
buf += "### それぞれの 10\n";
buf += parseInt("10",  2) + "\n"; //  2 = ( 2 * 1)
buf += parseInt("10", 10) + "\n"; // 10 = (10 * 1)
buf += parseInt("10", 16) + "\n"; // 16 = (16 * 1)
buf += "### それぞれの 11\n";
buf += parseInt("11",  2) + "\n"; //  3 = ( 2 * 1) + 1
buf += parseInt("11", 10) + "\n"; // 11 = (10 * 1) + 1
buf += parseInt("11", 16) + "\n"; // 17 = (16 * 1) + 1

buf += "### 10進数と16進数の 99\n";
buf += parseInt(" 99", 10) + "\n"; //   99 =                 (10 * 9) + 9
buf += parseInt(" 99", 16) + "\n"; //  153 =                 (16 * 9) + 9
buf += "### 10進数と16進数の 999\n";
buf += parseInt("999", 10) + "\n"; //  999 = (10 * 10 * 9) + (10 * 9) + 9
buf += parseInt("999", 16) + "\n"; // 2457 = (16 * 16 * 9) + (16 * 9) + 9

buf += "### 16進数の F, FF\n";
buf += parseInt(" F", 16) + "\n";  //   15 =             15
buf += parseInt("FF", 16) + "\n";  //  255 = (16 * 15) + 15

buf += "### 2進数の 1111, 11111111\n";
buf += parseInt("    1111", 2) + "\n"; //   15 =                                 (2^3*1)+(2^2*1)+(2^1*1)+(2^0*1)
buf += parseInt("11111111", 2) + "\n"; //  255 = (2^7*1)+(2^6*1)+(2^5*1)+(2^4*1)+(2^3*1)+(2^2*1)+(2^1*1)+(2^0*1)

alert(buf);

他のやり方としては、割り算や、一旦文字列にする(非推薦)、などあります。
一旦文字列にして実行すると意味が理解しやすい場合があります。
ビット演算がやっていることが明確なのと早いそうなので良いです。
var nums, ret, i, num;
nums = new Array(0);
nums[0] = 0x2a6b2;
nums[1] = 0x296F0;
nums[2] = 12307;
nums[3] = -1;
nums[4] = 0;
nums[5] = 0x10FFFF;
nums[6] = 0x110000;
nums[7] = 65535;
nums[8] = 65536;
ret = "";
for (i = 0; i < nums.length; i++)
{
    if (nums[i] < 0)
    {
        ret = "Error";
    }
    else if (nums[i] <= 0xFFFF)
    {
        ret = "1: " + String.fromCharCode(nums[i]);
    }
    else if (nums[i] <= 0x10FFFF)
    {
        num = nums[i];
        num -= 0x10000;
        ret = "2: " + String.fromCharCode((num >> 10) | 0xD800, (num & 0x3FF) | 0xDC00);
    }
    else
    {
        ret = "Error";
    }
    alert("[" + i + "] " + nums[i] + " = " + ret);
}

0x110000 の場合 21桁目が1でその下が全部0の2進数になるので2文字とも 0xDC00 と同じになります。

正式名称は UTF-16BE でいいんだと思います。明確にするためにはBOM無しと記述すればいいのかもしれません。
http://ja.wikipedia.org/wiki/UTF-16

http://liosk.blog103.fc2.com/blog-entry-164.html?sess=83184307bdcf6494ab8d34a228efb8fe
http://en.wikipedia.org/wiki/UTF-16/UCS-2#Example_UTF-16_encoding_procedure
http://unicode.org/Public/UNIDATA/Blocks.txt

2010-04-20

javascript の new

javascript の new の例です。
保存して new1, new2 の順でクリックして下さい。
<html>
 <head>
 </head>
 <script type="text/javascript">
<!--

_str = "";
alert1 = function (str) {_str += str + "\n";};
alert2 = function () {alert(_str); _str="";};
new1q1 = "";
new1q2 = "";

function mynew(func)
{
    var ret = {}, key;
    if (typeof func == "function" &&
        func.prototype && typeof func.prototype == "object")
    {
        for (key in func.prototype)
        {
            ret[key] = func.prototype[key];
        }
        ret.__construct = func;
        ret.__construct();
        delete ret.__construct;
        ret.__test = "test";
    }
    return ret;
}

Base = {
    food: "毒キノコ",
    question: function()
    {
        return "あなたは" + this.food + "が好きですか?";
    },
    answer: function()
    {
        return "私は" + this.food + "が好きです。";
    }
};

function q1() {}

function q2()
{
    this.types = {
        like: "好き",
        hate: "嫌い",
        unknown: "わからない"
    };
    this.type = "unknown";
    this.answer = function()
    {
        return "私は" + this.food + "が" + this.types[this.type] + "です。";
    };
}

function new1()
{
    var obj1, obj2, key;

    alert1("q1 を new");
    q1.prototype = Base;
    obj1 = new q1();
    alert1(obj1.question());
    obj1.food = "ベニテングダケ";
    alert1(obj1.answer());
    alert1("typeof: " + typeof obj1);
    for (key in obj1)
    {
        alert1("key: " + key + ": " + typeof obj1[key]);
    }
    new1q1 = _str;
    alert2();

    alert1("q2 を new");
    q2.prototype = obj1;
    obj2 = new q2();
    obj2.type = "hate";
    alert1(obj2.question());
    obj2.food = "ニガクリタケ";
    alert1(obj2.answer());
    alert1("typeof: " + typeof obj2);
    for (key in obj2)
    {
        alert1("key: " + key + ": " + typeof obj2[key]);
    }
    new1q2 = _str;
    alert2();
}

function new2()
{
    var obj1, obj2, key;

    alert1(new1q1);
    alert1("q1 を mynew");
    q1.prototype = Base;
    obj1 = mynew(q1);
    alert1(obj1.question());
    obj1.food = "ベニテングダケ";
    alert1(obj1.answer());
    alert1("typeof: " + typeof obj1);
    for (key in obj1)
    {
        alert1("key: " + key + ": " + typeof obj1[key]);
    }
    alert2();

    alert1(new1q2);
    alert1("q2 を mynew");
    q2.prototype = obj1;
    obj2 = mynew(q2);
    obj2.type = "hate";
    alert1(obj2.question());
    obj2.food = "ニガクリタケ";
    alert1(obj2.answer());
    alert1("typeof: " + typeof obj2);
    for (key in obj2)
    {
        alert1("key: " + key + ": " + typeof obj2[key]);
    }
    alert2();
}

//-->
 </script>
 <body>
  <button onclick="javascript:new1();">new1</button>
  <button onclick="javascript:new2();">new2</button>
 </body>
</html>



for (key in obj) { ... } はオブジェクト用です。
for (i = 0; i < arr.length; i++) { ... } は配列用です。
obj = {0:"one", 1:"two"};
arr = Array("one", "two");
for (key in obj) {alert(obj[key]);}
for (i = 0; i < arr.length; i++) {alert(arr[i]);}

オブジェクトは 半角英字とアンダーバーとドル記号(2文字目から半角数字)だけの key の場合 obj.key と書けます。
__iterator__, __proto__ などがあるので __(アンダーバー2個)で始まる文字を使わないことをお勧めします。
また半角数字だけや、半角数字で始まる文字を使わないことをお勧めします。(obj.key と書けるため)
obj = {"0":"zero", "$":"one", "_":"two", "a":"a", "b001":"b001"};
//alert(obj.0);
alert(obj.$);
alert(obj._);
alert(obj.a);
alert(obj.b001);
alert(obj["0"]);
alert(obj["$"]);
alert(obj["_"]);
alert(obj["a"]);
alert(obj["b001"]);

obj1.food = "ベニテングダケ"; が obj2.food に引き継がれていますが、この動作が予想外であるという原因で不具合が発生するかもしれません。その場合 q2.prototype = Base; です。
function q2() の中で Base で定義される内容を拡張していますが、この動作が作法に則っているかどうかは不明です。
function mynew(func) の内容が new をある程度エミュレートしていますが、これは参考なので new を使ってください。
newを使う場合のお勧めはベースとなるオブジェクトを利用してnewしたオブジェクトは別のnewに使わないことです。ベースとなるオブジェクトが100回newされた結果の値だとしても、あなたの管轄ではなく、ソースを見ないですむほどのマニュアルがあれば問題ないです。よって問題が発生する場合があります。

http://www.glamenv-septzen.net/view/170

2010-04-19

jsparsenodetree.c part3

この記事は SpiderMonkey, jsparsenodetree, php の json_decode() を利用して jquery-1.4.2.min.js, prototype.js(1.6.1) の構造のjsonを php に読み込ませる際に修正した jsparsenodetree.c の差分の例です。

jsparsenodetree.c.2010-04-19.diff.gz

具体的には、特殊文字の一部のエスケープ、i++, ++i の向き、正規表現のオプション、関数の引数、関数内のローカル変数(関数)名、が修正対象です。

jsparsenodetree.c
jsparsenodetree.c part2

2010-04-18

javascript を短くしてみましょう 第5回

jquery-1.4.2.min.js を三度短くしてみましょう。

javascript を圧縮するツールはすでにあるので、そちらがお勧めです。
http://fnya.cocolog-nifty.com/blog/2007/08/yui_compressor__57c4.html
http://webos-goodies.jp/archives/51182388.html
http://dogmap.jp/2007/09/06/minify-javascript/
http://fnya.cocolog-nifty.com/blog/2009/10/microsoft-ajax-.html
http://aspnet.codeplex.com/releases/view/34488

jquery-1.4.2.min5.2010-04-18.js.gz (通常時: 約 70.2KB, 圧縮時: 約 22.7KB)

jquery-1.4.2.min.js と比べて 258 Byte 縮みました。
また簡単な動作テストしかしていないです。

今回の結論は、比較的に安全な動作変更を含むサイズ最適化の修正が、残されていることがわかりました。
例えば var x=parseFloat; とすると 167 Byte 前後縮むかもしれません。

「javascript を短くしてみましょう」としての結論は、ライブラリの場合、公式サイトで配布されているjsファイルをそのまま使うのが1番良い、ということです。圧縮する場合は javascript のコーディングと圧縮ツールの選定者が同一か、関連する他者すべての同意を得て、実績のあるツールを利用する、といった処理が必要な場合があります。
また、javascriptの文法を理解する勉強にはなります。書いてある内容の意味(どうしてそう書いてあるのか)の勉強には(少ししか)なりません。

今回の javascript を短くする方法は、前回に利用した方法を利用しています。
×の部分は反映されていませんので不可能であるかもしれません。
○ ライセンスを除くコメントの削除。
○ ローカルな変数、関数名の変更。
○ スペース(文法的なワーニングを含む、動作の変更を含まない)の削除。
○ セミコロン(文法的なワーニングを含む、動作の変更を含まない)の削除。
× グローバルな変数、関数名を沢山利用されている場合にローカル変数に代入して置き換える。
× var a; var b; を var a, b; にする。(連続するvar宣言をまとめる) 追記: function(){for(var i=0;i<xxx;i++){}var j=0;for(var k in xxx){}} → function(){var i,j,k; ...}
× if(xxx){yyy} を if(xxx)yyy; にする。({...}の中の処理が単独な場合{}を消す)
× function xxx(){...}; のセミコロンを消す。(処理が無いセミコロンを消す)
× 到達することが無いifブロックなどを消すということをする機能がツールによってはあるそうです。

また jquery を使う場合 click, getWH, k, i という名前はグローバルな変数、関数名に使わない方がいいかもしれません。
click, k
d.attachEvent("onclick",function k(){ ... d.detachEvent("onclick",k)});

getWH, i
function i(){e=b=== ... ==0?i():c.swap(a,ob,i);
追記: 変数(関数名)のスコープの判定が間違っていました。下記の場合にxxx()がグローバルな関数ではないことが明確であることを判断できていないです。
function () { ... if (...) { ... function xxx(){ ... } ... xxx() ... } ... }
function () { ... if (...) { ... function xxx(){ ... xxx() ... } ... } ... }

「javascript を短くしてみましょう」で利用したツールは下記になります。
javascript パーサ
SpiderMonkey jsparsenodetree 他には ECMAScript パーサ で探すと良いのがあるかもしれません。
全体的な処理
php コマンド (5.2.13-pl0-gentoo) パース結果のツリー構造のデータの処理や、検証のための別のアプローチの処理(例:pcreの正規表現)で利用します。 好きな言語であれば何でも良いはずです。
圧縮
gzip コマンド (1.4) phpのgzip関数より結果が良いため利用しています。

「javascript を短くしてみましょう」のツールのphp部分の作成の流れは下記になります。
(1)
javascriptの完全に把握している1KB~5KBの短いファイルを用意します。
(2)
(1)のツリー構造のデータを元に、コメントやスペースを削除するだけの処理を作成します。
(3)
1文字ずつ読むか、正規表現か、得意な方で(2)と同じ処理を作成します。ツリー構造のデータを扱うのに慣れている場合で、かつ、ECMAScript系に詳しい場合は任意です。
(4)
(2),(3)が同じ内容になるまで、修正します。動くことを確認します。
(5)
変数を短くしたりする機能を(2)に追加します。その際(3)と同じ出力をできる機能を残します。(3)自体に機能を追加するのは大変なので諦めます。
(6)
(1)のファイルを別のファイルにして(2)~(5)を繰り返します。 別ファイルの候補は、ある程度長く、いろいろな書き方で記述されているファイルを2つ以上です。
(7)
一通りできたところでjavascriptを出力する直前のデータがあると思うので、変数名を入れ替える処理を追加して圧縮率を上げます。 ...A... より ...B... の方が圧縮した際に短くなる場合がある、という処理の連続です。この処理は任意です。例えばjqueryの場合1.5~2時間かかります。gzipのアルゴリズムの知識のある場合、もっと早く、小さくできるのかもしれません。

追記: ...(3)自体に機能を追加するのは大変なので諦めます。...の部分の例です。
// (a), (b), (c), (d) は b と名乗る関数の場所
// (1), (2), (3), (4) は外側の b ではない b を定義した場合

str = "";
function b(){str = "" + str + 1;}                         //     (a)
function a(c) {
    b();                                                  //     (b)
    //if (c==1){var b = function (){str = "" + str + 2;}} // (1) (c)
    //if (c==1){function b(){str = "" + str + 3;}}        // (2) (c)
    //{function b(){str = "" + str + 4;}}                 // (3) (c)
    //function b(){str = "" + str + 5;}                   // (4) (c)
    b();                                                  //     (d)
}
a(1);
a(2);
alert(str);

// 無し: 1111                 (a,b,c,d) 全て同じ名前で(a)に合わせる。
// (1) : b is not a function
// (2) : 1311                 (a,b,c,d) 全て同じ名前で(a)に合わせる。
// (3) : 1414                 (a,b) (c,d) がそれぞれ同じ名前
// (4) : 5555                 (b,c,d) が同じ名前で (a)は無関係
結局のところ、ツリー構造か、似たデータを作成するようになるかもしれないので、であれば、最初からツリー構造のデータが欲しいと思うかもしれません。
jquery-1.4.2.min5.2010-04-18.js.gz の場合(4)以外の例ではbをすべてグローバル変数として扱っています。よって、変数名の縮小の対象外になっています。しかし該当する部分が k,i と元から短いです。
よって function b(){} を function の中の function 以外の {...} の中に書く場合は function a(c) { var b; ... } とすると、変数(関数)のスコープが明確です。

javascript を短くしてみましょう 第1回
javascript を短くしてみましょう 第2回
javascript を短くしてみましょう 第3回
javascript を短くしてみましょう 第4回

javascript を短くしてみましょう 第4回

prototype.js を再び短くしてみましょう。

javascript を圧縮するツールはすでにあるので、そちらがお勧めです。
http://fnya.cocolog-nifty.com/blog/2007/08/yui_compressor__57c4.html
http://webos-goodies.jp/archives/51182388.html
http://dogmap.jp/2007/09/06/minify-javascript/
http://fnya.cocolog-nifty.com/blog/2009/10/microsoft-ajax-.html
http://aspnet.codeplex.com/releases/view/34488

prototype.min.js.2010-04-18.gz (通常時: 約 78KB, 圧縮時: 約 22KB)

前回と比べて 19 Byte, 圧縮時に 989 Byte 縮みました。
また簡単な動作テストしかしていないです。

今回の結論は、前回 ajaxmin に負けていたので、もう一度やってみましたが、圧縮時のサイズのみ勝ちました。変数以外の内容も変更しないと非圧縮時は勝てないようです。

今回の javascript を短くする方法は、下記になります。
  • 以前の修正を行う。
  • 変数名が2文字の部分を1文字にする。
詳細は、変数 a, b, c, が ...処理... の中で利用されていない時
変数 xx, yy, zz は a, b, c に名前を変えても動作に問題ない、といった内容です。
(function (){
var a, b, c;
var d = function (){var xx, yy, zz; ...処理... }
})()

さらに縮めるには
先頭のコメントを消す、
var a; var b; を var a, b; にする、
if(xxx){yyy} を if(xxx)yyy; にする、
function (){...}; のセミコロンを消す、
などがありますがコメント以外は、
動作変更を含まないソースのサイズ削減ではなく、
動作変更を含むソースのサイズ削減になるかもしれないので、
別問題だと思うので、対象外です。
先頭のコメントもライセンス表示なので消すべきではないです。

javascript を短くしてみましょう 第1回
javascript を短くしてみましょう 第2回
javascript を短くしてみましょう 第3回
javascript を短くしてみましょう 第5回

gentoo のマウスとキーボードの設定

x11-base/xorg-server を 1.7.6 に更新したところ、マウスとキーボードが動かなくなりました。

更新する前に x11-drivers/ati-drivers は使えませんと出たので /etc/make.conf の VIDEO_CARDS から fglrx を削除しました。
# fglrx を削除
VIDEO_CARDS="radeon"

エラーが出て X Window が起動しないので /etc/X11/xorg.conf から fglrx を削除しました。
...
-       Driver      "fglrx"
+       #Driver      "fglrx"
...

その後、起動はしますが、マウスとキーボードが動かないので AllowEmptyInput を追加したところ /var/log/Xorg.0.log に変化は有りましたが、動かないままでした。(よってコメントアウトしました)
...
+#Section "ServerFlags"
+#      Option "AllowEmptyInput" "off"
+#EndSection
...

hal を設定するそうなので設定しました。
/etc/make.conf の INPUT_DEVICES に evdev を追加し、システムの更新を行いました。
# evdevを追加
INPUT_DEVICES="evdev keyboard mouse"

$ emerge --update --deep --newuse --verbose --tree --with-bdeps=y -a world など

/usr/share/doc/hal-0.5.13-r2/use-multiple-layouts-with-kbd.fdi.bz2 を
/etc/hal/fdi/policy/10-xinput-configuration.fdi.out にコピーして
pc105 を pc106 に変更し en,fr を jp に変更し
haldを再起動しました。

startx を実行したところ、マウスとキーボードが動きました。

http://blog.fujitoko.com/2009/04/archives/article252.html
http://www.gentoo.org/proj/en/desktop/x/x11/xorg-server-1.5-upgrade-guide.xml

2010-04-14

javascript を短くしてみましょう 第3回

prototype.js を短くしてみましょう。

今回の結論は、修正による不具合の有無を考慮しない場合は、良い結果になりました。
また javascript を圧縮するツールはすでにあるので、そちらがお勧めです。
http://fnya.cocolog-nifty.com/blog/2007/08/yui_compressor__57c4.html
http://webos-goodies.jp/archives/51182388.html
http://dogmap.jp/2007/09/06/minify-javascript/
http://fnya.cocolog-nifty.com/blog/2009/10/microsoft-ajax-.html
http://aspnet.codeplex.com/releases/view/34488

prototype.min.js.2010-04-13.gz

original→ prototype.js
change  → prototype.min.js.2010-04-13.gz
yuicomp → yuicompressor-2.4.2 (java -jar yuicompressor-2.4.2.jar -o prototype.yuicompressor-2.4.2.js prototype.js)
ajaxmin → Microsoft Ajax Minifier 1.1 (ajaxmin.exe /H prototype.js -o prototype.ajaxmin.js)

 (1) original(2) change (3) yuicomp (4) ajaxmin
未圧縮 136.58 KB78.03 KB79.56 KB77.67 KB
圧縮 30.66 KB22.99 KB23.88 KB22.73 KB

 (1) - (2)(1) - (3)(1) - (4)(2) - (3)(2) - (4)
未圧縮 58.54 KB57.02 KB58.91 KB-1.53 KB0.37 KB
圧縮 7.67 KB6.78 KB7.93 KB-0.89 KB0.26 KB

結果としては javascript(prototype.js) が使えて、ファイル圧縮(gzip, deflate) が使えないというブラウザはほとんど無いことと、一度 ステータスコード 200 (ファイルデータ有り) で取得したファイルは、変更しない場合で If-Modified-Since をブラウザが送ってくる限り ステータスコード 304 (ファイルデータ無し) で返すことを合わせて考えるのであれば、大規模なサイトであっても、あまり意味は無いということがわかりました。
しかし始めて訪れたサイトで javascript が少しでも早く実行されるというのは良いと思います。
それぞれ動作確認していません。それぞれオプションを吟味していません。ajaxmin は /H を付けていますので、内容が変化しています。change が善戦している理由には、警告や注意やエラーになる場合のある変更があるため、というのもあると思います。

読み込んでエラーにならないことを確認したブラウザは Firefox 3.6.3 です。
先頭のコメントは残しています。

今回の javascript を短くする方法は、下記になります。

javascript を短くしてみましょう 第1回
javascript を短くしてみましょう 第2回
javascript を短くしてみましょう 第4回
javascript を短くしてみましょう 第5回

追記: jslint で prototype.js を通すテスト結果。
//============================
// Warning. 括弧が無い場合
var a=1; if(a==1) a=2;
// Warning. セミコロンの無い場合
var a=1; if(a==1) {a=2}
// OK
var a=1; if(a==1) {a=2;}

//============================
// Error. tryでセミコロンが無い場合
var a=1; switch(a){case 1:try{break}catch(e){}}
// Warning. if文を使える場合
var a=1; switch(a){case 1:try{break;}catch(e){}}

//============================
// Warning.
var a = new Date;
var a = new Array;
var a = new Array();
// OK.
var a = new Array(0);
var a = new Date();

//============================
// __iterator__, __proto__
// Error.
if (a.__proto__){}
// Warning.
if (a["__proto__"]){}

//============================
// Error.
return a=1;
// OK.
return (a=1);

//============================
// var では無いカンマ区切り
// Error.
a=1, b=2;
// OK. 意味同じはず。
a=1; b=2;

//============================
// Error.
a = void 0;
// OK.
a = undefined;

// OKでは無い。変更可能。
undefined = null;
a = undefined;

結果としては、無理でした。

2010-04-12

javascript を短くしてみましょう 第2回

追記: javascript を圧縮するツールがお勧めです。
http://fnya.cocolog-nifty.com/blog/2007/08/yui_compressor__57c4.html
http://webos-goodies.jp/archives/51182388.html
http://dogmap.jp/2007/09/06/minify-javascript/



jquery-1.4.2.min.js を再び短くしてみましょう。

今回の結論は、すでに短くなっている javascript は、変数名もすでに短いので、そのまま利用するのがお勧めです。

jquery-1.4.2.min3.js.2010-04-13.gz

無圧縮時 151バイト(0.2%) 短くなりました。
圧縮時 441バイト(1.8%) 短くなりました。

確認したブラウザは Firefox 3.6.3 です。
先頭のコメントは残しています。

今回の javascript を短くする方法は、下記になります。
  • 前回の修正を行う。
  • 変数名を修正する。
    _(アンダーバーで1文字)を変数名に追加したので4バイト短くなりましたが、わざと使っていないように見えるので不具合が発生する場合があります。

javascript を短くしてみましょう 第1回
javascript を短くしてみましょう 第3回
javascript を短くしてみましょう 第4回
javascript を短くしてみましょう 第5回

2010-04-11

php で標準入力を取得する例

function getStdin()
{
    $ret = "";
    $fp = fopen("php://stdin", "r");
    if (is_resource($fp))
    {
        $cnt = 0;
        stream_set_blocking($fp, 0);
        do
        {
            $buf = fread($fp, 512);
            if ($buf === "")
            {
                $cnt++;
                if ($cnt > 15)
                {
                    break;
                }
            }
            else
            {
                $cnt = 0;
                $ret .= $buf;
            }
        }
        while (1);
        fclose($fp);
    }
    return $ret;
}

手前のコマンドがゆっくり出力される場合、失敗します。
$ for i in $(seq 1 3); do echo $i; sleep 1; done
1
2
3
$ for i in $(seq 1 3); do echo $i; sleep 1; done | php wagnek.php
  2| Basic Latin    |   1| 1
  1| Control Code...|   1| \n

必ず標準入力が有る場合 stream_set_blocking($fp, 0); をコメントアウトすると良いです。

emacs の php-mode の設定の例

この例では pear の標準コーディング規約にある設定をベースにします。
http://pear.php.net/manual/ja/standards.indenting.php (空白 4 つのインデントを使用します。タブは使いません。)

(require 'php-mode)
(add-hook
 'php-mode-hook
 (defun php-mode-hook ()
   ;; c-mode のスタイル (コメントアウトした場合 "gnu")
   (c-set-style "bsd")

   ;; 連続する空白の一括削除 (必要なければコメントアウトする)
   (c-toggle-hungry-state t)

   ;; コメント行のインデント (必要なければコメントアウトする)
   (setq c-comment-only-line-offset 0)

   ;; コメントのスタイル (必要なければコメントアウトする)
   (setq comment-start "// "
         comment-end   ""
         comment-start-skip "// *")

   ;; 勝手に改行モード (必要なければコメントアウトする)
   (c-toggle-auto-hungry-state t)
   (setq c-hanging-braces-alist
         '(
           (class-open nil)
           (class-close nil)
           (defun-open before after)
           (defun-close nil)
           (inline-open nil)
           (inline-close nil)
           (brace-list-open nil)
           (brace-list-close nil)
           (block-open nil)
           (block-close nil)
           (substatement-open before after)
           (statement-case-open before after)
           (extern-lang-open nil)
           (extern-lang-close nil)
           ))

   ;; PEAR のスタイル (indent-tabs-mode nil を pear だけに反映させる場合はこちら)
   (setq tab-width 4
         c-basic-offset 4
         c-hanging-comment-ender-p nil
         indent-tabs-mode nil)
   ))
(setq
 auto-mode-alist
 (append
  '(
    ("/\\(PEAR\\|pear\\)/" . php-mode)
    ("\.php$" . php-mode)
    )auto-mode-alist))

c-set-style は慎重に選ぶことを、お勧めします。
M-x c-set-style
Possible completions are:
awk            bsd            ellemtel       gnu            java
k&r            linux          python         stroustrup     whitesmith

例えば、サンプルとなるソース (多次元配列, オブジェクト, クラス, 関数, if, else, for, while, switch, case などが書かれたインデント無しの状態の phpファイル) を用意して C-x h C-M-\ をスタイルごとに実行します。その都度 emacs を再起動した方が良いかもしれません。

blogger: レイアウト: HTML の編集: タイトルの修正 part2

javascript の無名関数(ラムダ関数)の例です。


<div id='header-wrapper'>
...
</div>

<script defer='defer' type='text/javascript'>
// <![CDATA[<!--

try {(function() {
    var h1 = document.getElementsByTagName("h1");
    if (h1 && h1[0] && h1[0].className.match(/(?:^|\s)title(?:$|\s)/i))
    {
        var a = h1[0].getElementsByTagName("a");
        if (a && a[0])
        {
            var title = document.getElementsByTagName("title");
            if (title && title[0])
            {
                a[0].href = document.URL;
                a[0].innerHTML = title[0].innerHTML.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            }
        }
    }
})();} catch(e) {}

//-->]]>
</script>

<div id='content-wrapper'>
...
</div> <!-- end content-wrapper -->

try{ ...処理... } catch(e) { ...エラー処理... }
処理が失敗した場合、エラー処理を実行します。
例えば alert(e) と書くと、エラー時に、なんらしかの文字が表示されます。

(function() { ...処理... })();
処理を実行します。
この場合の利用する理由は変数や関数の名前が被って欲しくないためです。

1個目の h1タグ のクラス名に title があることを確認し、その中に少なくとも1個 aタグ があることを確認し、1個目の titleタグ の中身を aタグの中身に書き込み、現在の url を aタグ の href に書き込みます。

追記: 元々の管理画面の機能を使った方が楽です。
レイアウト →
ページ要素 →
ガジェットを追加(サイドバー) →
HTML/JavaScript →
<script>~</script> をコピー&ペースト →
HTML/JavaScript をサイドバーの一番下へ移動

2010-04-10

Google Analytics の設定

この記事は下記のURLの設定の例です。
http://groups.google.com/group/japan-blogger-users/browse_thread/thread/474a05d5daa307cb?pli=1
http://www.hoge256.net/2007/09/70.html

読み替える部分です。
Google Analytics で設定した値 no_report
設置したサイトのURL           http://www.example.com/

Google Analytics で設定します。
(1) Analytics 設定 - Google Analytics のフィルタマネージャをクリックします。
フィルタ マネージャ»

(2) 編集をクリックします。(新規の場合 「+ フィルタを追加」をクリックします)
1.  自分のアクセスを除外  除外  編集  削除

(3) 入力します。
フィルタ名: [自分のアクセスを除外]
フィルタの種類: ( ) 既定のフィルタ (●)カスタム フィルタ
(●) 除外
( ) 一致
( ) 小文字
( ) 大文字
( ) 検索と置換
( ) アドバンス
フィルタ フィールド [ユーザー定義]
フィルタ パターン   [no_report]
大文字と小文字の区別 ( ) はい (●) いいえ

使用できるプロファイル           選択したプロファイル
                                www.example.com

html (xxx.html) を修正します。
残す場合はBasic認証などをかける、終わったら消すなどの対処をします。
xxx.html に記述します。
  <script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script>
  <a onclick="javascript:alert(__utmSetVar('no_report'))">Cookieをセット</a>
  <a href="http://www.example.com/">top</a>

ブラウザで http://www.example.com/xxx/xxx.html に移動します。
Cookieをセット をクリックします。
top をクリックします。

cookie を確認します。(Firefox の場合)
ツール => オプション => プライバシー => Cookie を個別に削除 =>
www.example.com を入力 =>
  内容: 多数の半角数字.no_report
該当する cookie があり、その後、1日以上待って、自分のアクセスがカウントされていなければ成功です。

array.patterns.Blocks.UTF-8.php part2

array.patterns.Blocks.UTF-8.php は http://unicode.org/Public/UNIDATA/Blocks.txt を UTF-8 と php と pcre で表現した場合の結果です。

前回と比べて変化は無いです。
利用するためのサンプルが、一度にマッチする量が多い場合に、セグメンテーション違反になっていたので修正しました。
wagnek.2010-04-10.tar.gz

(例 1) UTF-8 のファイルの、ファイル名を渡す。
$ date | nkf -w > date.txt
$ php wagnek.php date.txt
  2| Basic Latin    |   4| 2010
113| CJK Unified ...|   3| 年
  2| Basic Latin    |   3|   4
113| CJK Unified ...|   3| 月
  2| Basic Latin    |   3|  10
113| CJK Unified ...|   3| 日
  2| Basic Latin    |   1|
113| CJK Unified ...|   9| 土曜日
  2| Basic Latin    |  13|  19:48:55 JST
  1| Control Code...|   1| \n

(例 2) UTF-8 ではないテキストの内容を UTF-8 にして渡す。
$ cat ../wagahaiwa_nekodearu.txt | nkf -w | php wagnek.php | head -n10
113| CJK Unified ...|   6| 吾輩
101| Hiragana       |   3| は
113| CJK Unified ...|   3| 猫
101| Hiragana       |   9| である
  1| Control Code...|   2| \r\n
113| CJK Unified ...|  12| 夏目漱石
  1| Control Code...|   4| \r\n\r\n
  2| Basic Latin    |  55| -------------------------------------------------------
  1| Control Code...|   2| \r\n
100| CJK Symbols ...|   3| 【

文字を分割したい場合は chasen, kakasi, mecab がお勧めです。
"199| unknown" という文字が表示される場合、違う文字コードを渡している可能性が高いです。

(例 3) EUC-JP の文字を渡すと "199| unknown" が発生する。
$ date | nkf -e | php wagnek.php
  2| Basic Latin    |   4| 2010
  5| Latin Extend...|   2| ǯ
  2| Basic Latin    |   3|   4
199| unknown        |   2| \xB7\xEE
  2| Basic Latin    |   3|  10
199| unknown        |   2| \xC6\xFC
  2| Basic Latin    |   1|
199| unknown        |   6| \xC5\xDA\xCD\xCB\xC6\xFC
  2| Basic Latin    |  13|  19:55:51 JST
  1| Control Code...|   1| \n

unknown の場合1バイトずつ 16進数 で表現しています。
$ php -r 'var_export(mb_convert_encoding("\xB7\xEE", "UTF-8", "EUC-JP")); print "\n";';
'月'
$ php -r 'var_export(mb_convert_encoding("\xC6\xFC", "UTF-8", "EUC-JP")); print "\n";';
'日'
$ php -r 'var_export(mb_convert_encoding("\xC5\xDA\xCD\xCB\xC6\xFC", "UTF-8", "EUC-JP")); print "\n";';
'土曜日'

ǯ (&#x1ef;) の部分は UTF-8 の別の文字コードにマッチしています。
$ php -r 'var_export(unpack("N", mb_convert_encoding(mb_convert_encoding("年", "EUC-JP", "UTF-8"), "UCS-4", "UTF-8"))); print "\n";';
array (
1 => 495,
)
$ php -r 'print dechex(495) . "\n";'
1ef
http://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF の 横 01E0 - 縦 F の部分です。

例えば、データベースに binary(blob)型で UTF-8 に似ているデータが入っている場合のサルベージや、サルベージするかどうかの検討に利用します。

php の json_decode のエラー

json_decode でエラーが出るので調べたところ、下記のように記述すると解決しました。
バックスペース(\x08): \b
水平タブ      (\x09): \t
改ページ      (\x0C): \f

以下は詳細です。

json_last_error() は php-5.3 で使えるので、無い場合、適当な場所にインストールします。
$ ./configure --prefix=/usr/local/stow/php-5.3.2 --disable-cgi --disable-all --enable-json
$ make
$ sudo make install

エラーの出る(json_decode() が NULL を返す)プログラムに json_last_error() を追加します。
$ lv /tmp/test.php
...
var_dump(json_decode($buf));
var_dump(json_last_error());
$arr = get_defined_constants(true);
var_dump($arr["json"]);
...

json_last_error() が JSON_ERROR_CTRL_CHAR でした。
$ /usr/local/stow/php-5.3.2/bin/php /tmp/test.php | lv
...
NULL
int(3)
array(10) {
  ["JSON_HEX_TAG"]=>
  int(1)
  ["JSON_HEX_AMP"]=>
  int(2)
  ["JSON_HEX_APOS"]=>
  int(4)
  ["JSON_HEX_QUOT"]=>
  int(8)
  ["JSON_FORCE_OBJECT"]=>
  int(16)
  ["JSON_ERROR_NONE"]=>
  int(0)
  ["JSON_ERROR_DEPTH"]=>
  int(1)
  ["JSON_ERROR_STATE_MISMATCH"]=>
  int(2)
  ["JSON_ERROR_CTRL_CHAR"]=>
  int(3)
  ["JSON_ERROR_SYNTAX"]=>
  int(4)
}

$buf を最小限にしてテストしたところ、バックスペース、水平タブ、改ページ、が、そのまま書かれていることが原因でした。

2010-04-09

javascript を短くしてみましょう 第1回

追記: javascript を圧縮するツールがお勧めです。
http://fnya.cocolog-nifty.com/blog/2007/08/yui_compressor__57c4.html
http://webos-goodies.jp/archives/51182388.html
http://dogmap.jp/2007/09/06/minify-javascript/



jquery-1.4.2.min.js を短くしてみましょう。

149箇所修正しました。
jquery-1.4.2.min2.2010-04-09.js.gz

無圧縮時 147バイト短くなりました。
gzip -9 で圧縮時 210バイト短くなりました。(ファイル名に半角で12文字追加時)

確認したブラウザは Firefox 3.6.3 です。
先頭のコメントは残しています。
今回の結論は、すでに短くなっている javascript は変更による動作の不具合のリスクが高いので、そのまま利用するのが、お勧めです。

今回の javascript を短くする方法は、下記になります。
  • 無くても動作するセミコロンを削除する。
    (throw など。推薦されないが元のソースが利用しているので同じ危険性にしかならないことを期待する)
  • 改行を削除できる場合は削除する。
    (改行が空白1個として機能しているかどうかに注意する)
  • 削除できるスペースを削除する。
    (case文など)
  • 数字の表現を変更して短くできる場合は短くする。
    (-10000 → -1e4 など)

javascript を短くしてみましょう 第2回
javascript を短くしてみましょう 第3回
javascript を短くしてみましょう 第4回
javascript を短くしてみましょう 第5回

2010-04-07

blogger: レイアウト: HTML の編集: タイトルの修正

blogger でタイトルの部分を消しても問題が無い場合で、トップページだけに表示されて欲しい場合の修正の例です。

div#header
{
display:none
}
]]></b:skin>

...

<div id='header-wrapper'>
...
</div>

<script defer='defer' type='text/javascript'>
//<![CDATA[<!--
if (document.getElementById && (window.location.pathname.match(/^(?:\/(?:index(?:\.[a-z]+)?)?)?$/i)))
{
    document.getElementById("header").style.display="block";
}
//-->]]>
</script>

<div id='content-wrapper'>
...
</div> <!-- end content-wrapper -->

2010-04-03

php の ob_start の利用例 (preg_replace の正規表現の例)

html を出力する前にスペースを取り除く例です。
<?php
function callback($buffer)
{
    $buf = $buffer;
    $pattern = array();
    $replace = array();

    // 改行を \n に統一する。
    $pattern[] = "/(?:\r\n)|[\r\n]/";
    $replace[] = "\n";

    // \n を除く制御文字を削除する。
    $pattern[] = "/[\\x00-\\x09\\x0b-\\x1f]/";
    $replace[] = " ";

    // 反映させる。
    $buf = preg_replace($pattern , $replace, $buf);

    // 修正しない部分を保管して代替文字に入れ替える。
    $tmpName = "__TMP__";
    $i = 0;
    do
    {
        if (!isset($GLOBALS[$tmpName . $i]))
        {
            $tmpName .= $i;
            break;
        }
        if ($i > 10)
        {
            $tmpName .= md5(mt_rand()) . md5(mt_rand());
            break;
        }
        $i++;
    }
    while (true);
    $GLOBALS[$tmpName] = array();
    $buf = preg_replace_callback(
        "/<(pre|s(?:cript|tyle)|xmp)(?:\s*|(?:\s+[^>]+))>(.*?)<\/\\1\s*>/is",
        create_function('$matches',
                        '$tmp =& $GLOBALS["' . $tmpName . '"];' .
                        'if ($matches[2] === "") { return $matches[0]; }' .
                        '$tmp[] = $matches[0];' .
                        'return "<\\x00," . count($tmp) . ",\\x01>";'),
        $buf);

    $pattern = array();
    $replace = array();

    // 最初と最後の空白を削除する。
    $pattern[] = "/^\s+|\s+$/";
    $replace[] = "";

    // コメントを削除する。
    $pattern[] = "/<\!\-\-.*?(?<=\-\-)>/s";
    $replace[] = "";

    // 行ごとの最初と最後の半角スペースを削除する。
    $pattern[] = "/^ +| +$/m";
    $replace[] = "";

    // 改行を減らす。
    $pattern[] = "/\\n{3,}/";
    $replace[] = "\n\n";

    // 半角スペースを減らす。
    $pattern[] = "/ {2,}/";
    $replace[] = " ";

    // <, > に接触する空白を削除する。
    $pattern[] = "/\s*([<>])\s*/";
    $replace[] = "$1";

    // </head>までのタグに改行を追加する。
    $pattern[] = "/^(.*?)(<\/head>)/eis";
    $replace[] = "rtrim(preg_replace(\"/><(?!\\/)/\", \">\\n<\", \"$1\")) . \"\\n$2\\n\"";

    // ブロック要素の終わりに改行を追加する。
    $pattern[] = "/<\/(?:address|blockquote|c(?:aption|ol(?:group)?)|d(?:iv|[dlt])|" .
        "f(?:ieldset|orm)|h[1-6r]|l(?:egend|i)|noscript|ol|p(?:re)?|" .
        "t(?:able|body|foot|head|[dhr])|ul)>/i";
    $replace[] = "$0\n";

    // brタグの終わりに改行を追加する。
    $pattern[] = "/<br((?:\s*)|(?:\s+[^>]+?))\/?" . ">/ei";
    $replace[] = "\"<br\" . preg_replace(\"/^\\s+|\\s+$/\", \" \", \" $1 \") . \"/>\\n\"";

    // 反映させる。
    $buf = preg_replace($pattern , $replace, $buf);

    // 代替文字を元に戻す。
    $buf = preg_replace_callback(
        "/<\\x00,(\d+),\\x01>/",
        create_function(
            '$matches',
            '$tmp =& $GLOBALS["' . $tmpName . '"];' .
            'return $tmp[$matches[1] - 1];'
            ),
        $buf);

    return $buf;
}
ob_start("callback");

主な用途はタグに隣接するスペースの削除です。
<tag>\n
  <tag>\n
      This is an example.\n
  </tag>\n
</tag>\n

htmlソースの見易さのために、ブロック要素の終わりに改行を追加するため、スタイルシートなどでブロック要素を display: inline; にしている場合、余分なスペースが発生します。

以下は詳細です。
http://jp.php.net/manual/ja/reference.pcre.pattern.syntax.php

    // 改行を \n に統一する。
    $pattern[] = "/(?:\r\n)|[\r\n]/";
    $replace[] = "\n";
\r\n を優先して検索して、あれば \n に変更し、無ければ \r を \n に変更します。(\n を \n に変換する意味の無い処理も含まれます)

    // \n を除く制御文字を削除する。
    $pattern[] = "/[\\x00-\\x09\\x0b-\\x1f]/";
    $replace[] = " ";
\n(10番)を除く 0 ~ 31番の文字を 32番(半角スペース)へ変更します。
http://ja.wikipedia.org/wiki/ASCII#ASCII.E5.88.B6.E5.BE.A1.E6.96.87.E5.AD.97

UCS-4 などの該当部分が1バイトではない文字コードは破壊されます。
表現は16進数です。 (\x0A → \10, \x1F → \31, \x20 → \32)
[ ... ] で括った場合で 文字-文字 となっている場合、範囲を指定しています。
例えば半角スペースを \x20 と表現した場合の利点は、 \x00-\x20 と書いた場合見やすいことや、スペースを無視する正規表現 ( /pattern/x ) の際に半角スペースを指定する、などです。

    // 修正しない部分を保管して代替文字に入れ替える。
    $tmpName = "__TMP__";
    ...
    $GLOBALS[$tmpName] = array();
    $buf = preg_replace_callback(
        "/<(pre|s(?:cript|tyle)|xmp)(?:\s*|(?:\s+[^>]+))>(.*?)<\/\\1\s*>/is",
        create_function('$matches',
                        '$tmp =& $GLOBALS["' . $tmpName . '"];' .
                        'if ($matches[2] === "") { return $matches[0]; }' .
                        '$tmp[] = $matches[0];' .
                        'return "<\\x00," . count($tmp) . ",\\x01>";'),
        $buf);
pre, script, style, xmp タグを保管場所に移動します。

利用していないグローバル変数を検索して空の配列とし、文字列の保管場所にします。
    $tmpName = "__TMP__";
    ...
    $GLOBALS[$tmpName] = array();

タグの中身が空の場合には移動をしません。
http://jp.php.net/manual/ja/function.preg-replace-callback.php
'if ($matches[2] === "") { return $matches[0]; }' .

始まりのタグ
<(pre|s(?:cript|tyle)|xmp)(?:\s*|(?:\s+[^>]+))>

大文字小文字を無視します。( /pattern/i )

中身
(.*?)

改行を含む任意の文字にマッチします。( /pattern/s )
中身に </script> などがあれば失敗し、期待した動作になりません。
? をつけない場合、最後の </script> にマッチします。
(量指定子の後に疑問符を付けると、貪欲さは消え、できるだけ少ない回数だけマッチします付近の説明)

終わりのタグ
<\/\\1\s*>

\\1 は後方参照で始まりのタグで指定した pre, script, style, xmp の、どれかが入ります。(括弧の始まりの後ろに ?: と付かない (...) で囲まれた部分です)
phpの正規表現は文字列に正規表現を入れるので \\1 は 動作する際には \1 になっています。
例えば \\n, \n は \n という文字か、改行(line feed)自体の文字かの違いになりますので変化は無いです。
スペースを無視する正規表現( /pattern/x ) では、その違いは重要です。

    // 最初と最後の空白を削除する。
    $pattern[] = "/^\s+|\s+$/";
    $replace[] = "";
^ は文字列の始まりです。
$ は文字列の終わりです。
| は、or、もしくは、の意味です。
\s は改行を含む空白です。

    // コメントを削除する。
    $pattern[] = "/<\!\-\-.*?(?<=\-\-)>/s";
    $replace[] = "";
<!-- という文字を探し、あった場合、後ろに続く処理を行います。
.*? は改行を含む任意の文字にマッチしますが、常に最短の短さでマッチしようとしますので > の有る無しを常に探します。
> があった場合 > の手前の文字が -- であるかどうかをチェックします。
> の手前の文字が -- であった場合(<!--> など)マッチしたことになります。

    // 行ごとの最初と最後の半角スペースを削除する。
    $pattern[] = "/^ +| +$/m";
    $replace[] = "";
行ごとの最初と最後にマッチします。( /pattern/m )

// "/^ +| +$/m" の場合
  aaa  
  bbb  
  ccc  

// "/^ +| +$/" の場合
  aaa  
  bbb  
  ccc  

    // 改行を減らす。
    $pattern[] = "/\\n{3,}/";
    $replace[] = "\n\n";
\n が 3回以上続く場合 \n\n に変換します。

    // 半角スペースを減らす。
    $pattern[] = "/ {2,}/";
    $replace[] = " ";
半角スペースが2個以上続く場合、半角スペース1個に変換します。

繰り返しの例です。
a が0~1の時にマッチします。
a?
a{0,1}

a が0文字以上の時にマッチします。
a*
a{0,}

a が1文字以上の時にマッチします。
a+
a{1,}

文字の指定の後の ? と、文字の連続する回数の指定の後の ? が意味が違います。
後者は最小限の長さでマッチしてください、という意味です。

aab? に bが含まれます。
$ php -r '$buf = "aabbcc"; if (preg_match("/(aab?)(b*cc)/", $buf, $m)) { var_dump($m); }'
array(3) {
  [0]=>
  string(6) "aabbcc"
  [1]=>
  string(3) "aab"
  [2]=>
  string(3) "bcc"
}

aab?? に bが含まれません。
$ php -r '$buf = "aabbcc"; if (preg_match("/(aab??)(b*cc)/", $buf, $m)) { var_dump($m); }'
array(3) {
  [0]=>
  string(6) "aabbcc"
  [1]=>
  string(2) "aa"
  [2]=>
  string(4) "bbcc"
}

aab?? に bが含まれます。bcc の部分の b の長さが固定なためです。
$ php -r '$buf = "aabbcc"; if (preg_match("/(aab??)(bcc)/", $buf, $m)) { var_dump($m); }'
array(3) {
  [0]=>
  string(6) "aabbcc"
  [1]=>
  string(3) "aab"
  [2]=>
  string(3) "bcc"
}

    // <, > に接触する空白を削除する。
    $pattern[] = "/\s*([<>])\s*/";
    $replace[] = "$1";
$1 は ( ... ) で指定した <, > のどちらかが入ります。

    // </head>までのタグに改行を追加する。
    $pattern[] = "/^(.*?)(<\/head>)/eis";
    $replace[] = "rtrim(preg_replace(\"/><(?!\\/)/\", \">\\n<\", \"$1\")) . \"\\n$2\\n\"";
head の部分の大文字小文字を無視します。 ( /pattern/i )
.* の部分の任意の文字に改行を含めます。 ( /pattern/s )
( /pattern/e ) は $replace[] の部分を phpのコードとして解釈します。

php コード自体で書いた場合下記のような意味です。($matches に配列でマッチした文字が入っている場合)
rtrim(preg_replace("/><(?!\/)/", ">\n<", "{$matches[1]}")) . "\n{$matches[2]}\n";

"/><(?!\/)/"
>< にマッチしますが < の後ろに / が有る場合、マッチしません。(マッチする内容に / を含みません)

>< を >(改行)< に変換後に、文字列の後ろのスペースを取り除き(rtrim) (改行)</head>(改行) を追加します。

    // ブロック要素の終わりに改行を追加する。
    $pattern[] = "/<\/(?:address|blockquote|c(?:aption|ol(?:group)?)|d(?:iv|[dlt])|" .
        "f(?:ieldset|orm)|h[1-6r]|l(?:egend|i)|noscript|ol|p(?:re)?|" .
        "t(?:able|body|foot|head|[dhr])|ul)>/i";
    $replace[] = "$0\n";
</tagname> を </tagname>(改行) へ変更します。
指定しているタグ名は、ブロック要素です。
http://ja.wikipedia.org/wiki/HTML%E8%A6%81%E7%B4%A0#.E3.83.96.E3.83.AD.E3.83.83.E3.82.AF.E3.83.AC.E3.83.99.E3.83.AB.E8.A6.81.E7.B4.A0

括弧が多いのは、マッチにかかる時間を減らすためです。
aa|ab|ac → aa か ab か ac を探す。
a[abc]   → a を探してから a, b, c を探す。

p(?:re)?      → p, pre を探す。
d(?:iv|[dlt]) → div, dd, dl, dt を探す。

    // brタグの終わりに改行を追加する。
    $pattern[] = "/<br((?:\s*)|(?:\s+[^>]+?))\/?" . ">/ei";
    $replace[] = "\"<br\" . preg_replace(\"/^\\s+|\\s+$/\", \" \", \" $1 \") . \"/>\\n\"";
<br で始まり
スペースが0文字以上、もしくはスペース1文字以上で > では無い文字が1文字以上あり
/ が0~1文字あり
> で終わる
場合の文字の後ろに改行を追加します。

[^ ... ] の場合 ... を含まないという意味になります。
1文字(1バイト)にしか効果が有りません。

文字列の場合(preg_xxx で利用されている pcreライブラリは perl 互換です)
http://www.din.or.jp/~ohzaki/regex.htm#Without

スペースが0文字以上 の場合
<br />

スペースが1文字以上で > では無い文字が1文字以上 の場合
<br class="className" />
例えば<br class="<className>" />となっていた場合、失敗します。(htmlとしてはおかしいですが " の数が合っていればブラウザは正常に解釈する場合が多いです)

    // 代替文字を元に戻す。
    $buf = preg_replace_callback(
        "/<\\x00,(\d+),\\x01>/",
        create_function(
            '$matches',
            '$tmp =& $GLOBALS["' . $tmpName . '"];' .
            'return $tmp[$matches[1] - 1];'
            ),
        $buf);
<\\x00,(\d+),\\x01> の \x00, \x01 の部分は実際にはブラウザで見ると文字化けを起こすような文字が指定されています。
<\\x00,(\d+),\\x01> を埋め込む前に// \n を除く制御文字を削除する。で 元々ある \x00, \x01 は削除されています。

<\\x00,(\d+),\\x01> の 数字の部分は配列の個数で入っているので
'return $tmp[$matches[1] - 1];' で -1 で番号を指定します。
$tmp[] = $matches[0]; のように空の配列に追加した場合、配列のキーは 0, 1, 2 ... の順番で追加されます。
配列が1個 → $tmp = array(0 => "xxx");
配列が2個 → $tmp = array(0 => "xxx", 1 => "yyy");

この処理(function callback($buffer))は、htmlのタグのオプションの値の <, > が &lt;, &gt; のようにエスケープされていることや
key="a   b   c" が key="a b c" のようにスペースが短くなっても問題ないことや
(例えば <a onclick="javascript: ... code ...">click</a> などで問題が出る場合がある)
<script> ... </script> の中に var a="</script>" のような指定が無いこと
などの前提が必要です。
よって html の処理には tidy などがお勧めです。

2010-04-01

jslint part2

http://d.hatena.ne.jp/cooldaemon/20090119/1232344701
http://www.jslint.com/lint.html
http://blog.8-p.info/2010/15-spidermonkey-jslint
http://insnvlovn.blogspot.com/2010/03/jslint.html

上記のurlの内容の設定例です。

一番上のリンクからテキストをコピーして修正します。
js-1.8.0-rc1.tar.gz を /usr/local/src/spidermonkey/js/src/Linux_All_DBG.OBJ/js にインストールします。
$ diff jslint.js.orig jslint.js
1c1
< load(environment.HOME + '/bin/js/fulljslint.js');
---
> load('/usr/local/bin/jslintLib/fulljslint.js');
32c32,41
< })(arguments[0], arguments[1]);
---
> })(arguments[0], slurp());
>
> function slurp() {
>     var ln;
>     var result = '';
>     while ((ln = readline()) !== null) {
>         result += ln + '\n';
>     }
>     return result;
> }

$ diff jslint.sh.orig jslint
8c8,10
< js ~/bin/js/jslint.js $1 "`cat $1`"
---
> cat $1 | /usr/local/src/spidermonkey/js/src/Linux_All_DBG.OBJ/js \
>     /usr/local/stow/jslint-2010-04-01/bin/jslintLib/jslint.js $1
>

サイズの大きいjsファイルで動くことを確認します。
$ ./jslint prototype.js | head -n10
prototype.js:22:7:Missing semicolon.
prototype.js:34:10:Expected '{' and instead saw 'return'.
prototype.js:40:16:['__proto__'] is better written in dot notation.
prototype.js:40:37:['__proto__'] is better written in dot notation.
prototype.js:40:59:['__proto__'] is better written in dot notation.
prototype.js:54:29:Missing semicolon.
prototype.js:58:4:Expected '{' and instead saw 'Prototype'.
prototype.js:83:26:Unnecessary semicolon.
prototype.js:87:8:Expected '{' and instead saw 'parent'.
prototype.js:99:38:Missing '()' invoking a constructor.

emacsのhtml-modeのインデントの幅

(add-hook
 'sgml-mode-hook
 (lambda ()
   (setq
    sgml-basic-offset 1
    )))