メイン | 2007年01月 »

2006年12月 アーカイブ

2006年12月03日

JavaScriptによるMD5の実装

実力試しにJavaScriptでMD5の実装をしてみた。今後、色々なハッシュを組み込む事を考え、MessageDigestクラスのクラスメソッドとしている。
/*
================================================================================
    Name        :   MessageDigest
    In          :   [none]      
    Out         :   [none]      
    Note        :   メッセージダイジェスト
--------------------------------------------------------------------------------
    Version     :   Ver1.0.0    |   2006/11/18  |   新規作成
--------------------------------------------------------------------------------
    License     :   New BSD license
    URL         :   www.kanasansoft.com
================================================================================
*/

/*--------------------------------------------------------------------------------
    コンストラクタ
--------------------------------------------------------------------------------*/
function MessageDigest(){}

/*--------------------------------------------------------------------------------
    定数
--------------------------------------------------------------------------------*/
MessageDigest.BY_STRING     =   "string";
MessageDigest.BY_ARRAY      =   "array";

///*--------------------------------------------------------------------------------
//  バイト配列に変換する(内部関数)
//--------------------------------------------------------------------------------*/
//MessageDigest._changeParameterToByteArray
//= function(param){
//
//  var rtn;
//
//  switch(param.constructor){
//      case Number:
//          rtn =   MessageDigest._changeNumberToByteArray(param);
//          break;
//      case String:
//          rtn =   MessageDigest._changeStringToByteArray(param);
//          break;
//      default:
//          rtn =   null;
//          break;
//  }
//
//  return rtn;
//
//}

/*--------------------------------------------------------------------------------
    数値をバイト配列に変換する(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest._changeNumberToByteArray
=   function(num){

    //参照渡し破棄
    var numbuf          =   Number(num);

    var ary             =   [];
    //下位桁より配列化
    while(numbuf!=0){
        ary             .   push(numbuf & 0xFF);
        numbuf          =   numbuf >>> 8;
    }
    //バイトを反転
    ary                 .   reverse();

    return ary;

}

/*--------------------------------------------------------------------------------
    文字を文字コード配列に変換する(内部関数)
    (2バイト以上の場合は1バイト毎)
--------------------------------------------------------------------------------*/
MessageDigest._changeStringToByteArray
=   function(str){

    var bytearray           =   [];
    var strlen              =   str.length;

    //上位ワードより配列化
    for(var i=0;i<strlen;i++){
        bytearray           =   bytearray.concat(
                                    MessageDigest._changeNumberToByteArray(
                                        str.charCodeAt(i)
                                    )
                                );
    }

    return bytearray;

}

/*--------------------------------------------------------------------------------
    ビットを回転する(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest._rotateBit
=   function(bit,max,num){
    var mask    =   Math.pow(2,max)-1;
    var buf     =   bit & mask;
    if(false){
    }else if(num>0){
        return (( (buf <<  ( num)) | (buf >>> (max-num)) ) & mask);
    }else if(num<0){
        return (( (buf >>> (-num)) | (buf <<  (max+num)) ) & mask);
    }else if(num==0){
        return buf;
    }
}

/*--------------------------------------------------------------------------------
    MD5本体
--------------------------------------------------------------------------------*/
MessageDigest.MD5
=   function(param,by){

    //MessageDigest参照オブジェクトの作成
    var MD          =   MessageDigest;

    //バイト配列変換
    var charArray   =   MD._changeStringToByteArray(param);

    //バイト配列長と拡張バイト長の取得
    var charLen     =   charArray.length;
    var paddingLen  =   512-((charLen*8+64)%512);

    //拡張バイト補填
    charArray       =   charArray.concat(
                                [   0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ].slice(0,paddingLen/8)
                        );

    //バイト長補填
    var lenArray    =   MD._changeNumberToByteArray(charLen*8);
    lenArray        .   reverse();
    lenArray        =   lenArray.concat([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]).slice(0,8);
    charArray       =   charArray.concat(lenArray);

    //レジスタ(32ビット)の初期化
    var wordA       =   0x67452301;
    var wordB       =   0xefcdab89;
    var wordC       =   0x98badcfe;
    var wordD       =   0x10325476;

    //[T]テーブルの作成
    var table       =   MD.MD5._makeTable();

    //バイト配列を64バイト単位で処理
    var charLen64   =   charArray.length;
    for(var i=0;i<charLen64;i+=64){

        //64バイトのデータを4バイト単位で格納
        //(4バイトは下位より格納)
        var numX    =   [];
        for(var j=0;j<16;j++){
            numX[j] =   0;
            for(var k=0;k<4;k++){
                numX[j] +=  charArray[i+j*4+k]*Math.pow(2,k*8);
            }
        }

        //データ退避
        var wordAA      =   wordA;
        var wordBB      =   wordB;
        var wordCC      =   wordC;
        var wordDD      =   wordD;

        //ハッシュ生成(Round 1)
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[0] , 7,table[ 1]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[1] ,12,table[ 2]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[2] ,17,table[ 3]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[3] ,22,table[ 4]);
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[4] , 7,table[ 5]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[5] ,12,table[ 6]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[6] ,17,table[ 7]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[7] ,22,table[ 8]);
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[8] , 7,table[ 9]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[9] ,12,table[10]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[10],17,table[11]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[11],22,table[12]);
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[12], 7,table[13]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[13],12,table[14]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[14],17,table[15]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[15],22,table[16]);

        //ハッシュ生成(Round 2)
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[1] , 5,table[17]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[6] , 9,table[18]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[11],14,table[19]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[0] ,20,table[20]);
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[5] , 5,table[21]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[10], 9,table[22]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[15],14,table[23]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[4] ,20,table[24]);
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[9] , 5,table[25]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[14], 9,table[26]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[3] ,14,table[27]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[8] ,20,table[28]);
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[13], 5,table[29]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[2] , 9,table[30]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[7] ,14,table[31]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[12],20,table[32]);

        //ハッシュ生成(Round 3)
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[5] , 4,table[33]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[8] ,11,table[34]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[11],16,table[35]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[14],23,table[36]);
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[1] , 4,table[37]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[4] ,11,table[38]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[7] ,16,table[39]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[10],23,table[40]);
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[13], 4,table[41]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[0] ,11,table[42]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[3] ,16,table[43]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[6] ,23,table[44]);
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[9] , 4,table[45]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[12],11,table[46]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[15],16,table[47]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[2] ,23,table[48]);

        //ハッシュ生成(Round 4)
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[0] , 6,table[49]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[7] ,10,table[50]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[14],15,table[51]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[5] ,21,table[52]);
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[12], 6,table[53]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[3] ,10,table[54]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[10],15,table[55]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[1] ,21,table[56]);
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[8] , 6,table[57]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[15],10,table[58]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[6] ,15,table[59]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[13],21,table[60]);
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[4] , 6,table[61]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[11],10,table[62]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[2] ,15,table[63]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[9] ,21,table[64]);

        //退避していたデータを加算
        wordA   +=  wordAA;
        wordB   +=  wordBB;
        wordC   +=  wordCC;
        wordD   +=  wordDD;

        //32ビットへの丸め込み(レジスタのサイズ)
        wordA   =   wordA & 0xffffffff;
        wordB   =   wordB & 0xffffffff;
        wordC   =   wordC & 0xffffffff;
        wordD   =   wordD & 0xffffffff;

    }

    //各々を下位バイト順にした後(反転した後)、順に連結
    var rtnArray    =   [];
    var rtnA    =   MD._changeNumberToByteArray(wordA);
    var rtnB    =   MD._changeNumberToByteArray(wordB);
    var rtnC    =   MD._changeNumberToByteArray(wordC);
    var rtnD    =   MD._changeNumberToByteArray(wordD);
    rtnA.reverse();
    rtnB.reverse();
    rtnC.reverse();
    rtnD.reverse();
    rtnArray        =   rtnArray.concat(rtnA);
    rtnArray        =   rtnArray.concat(rtnB);
    rtnArray        =   rtnArray.concat(rtnC);
    rtnArray        =   rtnArray.concat(rtnD);

    //取得指定方法毎に戻り値処理
    switch(by){
        case MD.BY_ARRAY:
            return rtnArray;
            break;
        case MD.BY_STRING:
        default:
            var rtnArrayLen =   rtnArray.length;
            var rtnString   =   "";
            for(var i=0;i<rtnArrayLen;i++){
                rtnString   +=  ("00"+rtnArray[i].toString(16)).slice(-2);
            }
            return rtnString;
            break;
    }

}

/*--------------------------------------------------------------------------------
    補助関数(内側)(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest.MD5._rogicF       =   function(x,y,z){return ( ( x) & ( y) ) | ( (~x) & ( z) ) };
MessageDigest.MD5._rogicG       =   function(x,y,z){return ( ( x) & ( z) ) | ( ( y) & (~z) ) };
MessageDigest.MD5._rogicH       =   function(x,y,z){return ( x) ^   ( y) ^ ( z)   };
MessageDigest.MD5._rogicI       =   function(x,y,z){return ( y) ^ ( ( x) | (~z) ) };

/*--------------------------------------------------------------------------------
    補助関数(外側)(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest.MD5._rogicRound1  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicF(b,c,d)+K+I),32,s)};
MessageDigest.MD5._rogicRound2  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicG(b,c,d)+K+I),32,s)};
MessageDigest.MD5._rogicRound3  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicH(b,c,d)+K+I),32,s)};
MessageDigest.MD5._rogicRound4  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicI(b,c,d)+K+I),32,s)};

/*--------------------------------------------------------------------------------
    テーブル作成(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest.MD5._makeTable
=   function(){
    var table                   =   [];
    for(var i=0;i<=64;i++){
        table[i]                =   Math.floor(0xffffffff*Math.abs(Math.sin(i)));
    }
    return table;
}

文字列の操作を行うjavaScript Class

これまで作ってきた文字列操作系の関数で使えそうなものを、StringUtilityとしてひとつにまとめた。(まとめようと考えてから、かれこれ一年以上たってた。)
/*
================================================================================
    Name        :   StringUtility
    In          :   [none]      
    Out         :   [none]      
    Note        :   文字列用ユーティリティ群
--------------------------------------------------------------------------------
    Version     :   Ver1.0.0    |   2006/01/16  |   新規作成
--------------------------------------------------------------------------------
    License     :   New BSD license
    URL         :   www.kanasansoft.com
================================================================================
*/

/*--------------------------------------------------------------------------------
    コンストラクタ
--------------------------------------------------------------------------------*/
function StringUtility(){
}

StringUtility.Encode
=   function(){
}

StringUtility.Decode
=   function(){
}

StringUtility.Convert
=   function(){
}

/*--------------------------------------------------------------------------------
    HTMLエンコードを行なう
--------------------------------------------------------------------------------*/
StringUtility.Encode.HTML
=   function(str){
    return          str                                         .
                    replace(    /&/ig   ,   "&amp;"     )       .
                    replace(    /</ig   ,   "&lt;"      )       .
                    replace(    />/ig   ,   "&gt;"      )       .
                    replace(    /"/ig   ,   "&quot;"    )       .
                    replace(    / /ig   ,   "&nbsp;"    )       ;
}

/*--------------------------------------------------------------------------------
    HTMLデコードを行なう
--------------------------------------------------------------------------------*/
StringUtility.Decode.HTML
=   function(str){
    return          str                                         .
                    replace(    /&nbsp;/ig  ,   " "     )       .
                    replace(    /&quot;/ig  ,   "\""    )       .
                    replace(    /&gt;/ig    ,   ">"     )       .
                    replace(    /&lt;/ig    ,   "<"     )       .
                    replace(    /&amp;/ig   ,   "&"     )       ;
}

/*--------------------------------------------------------------------------------
    HTMLエンコードを行なう(Bookmarklet用)
--------------------------------------------------------------------------------*/
StringUtility.Encode.HTMLforBookmarklet
=   function(str){
    return          str                                         .
                    replace(    /&/ig   ,   "&amp;"     )       .
                    replace(    /</ig   ,   "&lt;"      )       .
                    replace(    />/ig   ,   "&gt;"      )       .
                    replace(    /"/ig   ,   "&quot;"    )       ;
}

/*--------------------------------------------------------------------------------
    JavaScriptエンコードを行なう
--------------------------------------------------------------------------------*/
StringUtility.Encode.JavaScript
=   function(str){
    return          str                                         .
                    replace(    /\\/ig  ,   "\\\\"      )       .
/*                  replace(    /\b/ig  ,   "\\b"       )       .*/
                    replace(    /\f/ig  ,   "\\f"       )       .
                    replace(    /\n/ig  ,   "\\n"       )       .
                    replace(    /\r/ig  ,   "\\r"       )       .
                    replace(    /\t/ig  ,   "\\t"       )       .
                    replace(    /'/ig   ,   "\\'"       )       .
                    replace(    /"/ig   ,   "\\\""      )       ;
}

/*--------------------------------------------------------------------------------
    tabをspaceに変換する
--------------------------------------------------------------------------------*/
StringUtility.Convert.TabToSpace
=   function(str,tabNumber){

    var linesRN                         =   str.split("\r\n");
    for(var lineCntRN=0;lineCntRN<linesRN.length;lineCntRN++){
        var linesN                      =   linesRN[lineCntRN].split("\n");
        for(var lineCntN=0;lineCntN<linesN.length;lineCntN++){
            var linesR                  =   linesN[lineCntN].split("\r");
            for(var lineCntR=0;lineCntR<linesR.length;lineCntR++){
                var wordsT              =   linesR[lineCntR].split("\t");
                for(var wordsCntT=0;wordsCntT<wordsT.length-1;wordsCntT++){
                    wordsT[wordsCntT]   +=  StringUtility.getRepeatString(
                                                " "                                                                     ,
                                                tabNumber-(StringUtility.getLengthByBite(wordsT[wordsCntT])%tabNumber)  )
                }
                linesR[lineCntR]        =   wordsT.join("");
            }
            linesN[lineCntN]            =   linesR.join("\r");
        }
        linesRN[lineCntRN]              =   linesN.join("\n");
    }
    var rtn                             =   linesRN.join("\r\n");

    return rtn;

}

/*--------------------------------------------------------------------------------
    改行コードを"<br />"に変換する
--------------------------------------------------------------------------------*/
StringUtility.Convert.NewLineCodeToTag
=   function(str){
    return          str                                             .
                    replace(    /\r\n/ig    ,   "<br />"    )       .
                    replace(    /\r/ig      ,   "<br />"    )       .
                    replace(    /\n/ig      ,   "<br />"    )       ;
}

/*--------------------------------------------------------------------------------
    文字バイト長取得
--------------------------------------------------------------------------------*/
StringUtility.getLengthByBite
=   function(str){
    var count   =   0;
    for(var i=0;i<str.length;i++){
        var code    =   str.charCodeAt(i);
        while(code!=0){
            count++;
            code>>>=8;
        }
    }
    return count;
}

/*--------------------------------------------------------------------------------
    繰り返し文字取得
--------------------------------------------------------------------------------*/
StringUtility.getRepeatString
=   function(str,num){
    var rtn =   "";
    for(var i=0;i<num;i++){
        rtn +=  str;
    }
    return rtn;
}
追記(2007/07/07)
下記リンク先でバージョンアップしたものを公開中。

javascriptでJSON<=>Object変換

2009/11/24 追記
このJSONの変換処理は時代遅れとなっています。
最新の動向を知りたい方は、以下のキーワードで検索する事をお勧めします。
「JSON.stringify」「JSON.parse」「json2.js」
追記終わり
JavaScriptのObjectをJSONに変換するクラス。JSONの規格外のオブジェクト(Functionオブジェクト等)はnullとして処理するので注意。Collection & Copy - JSON入門に詳しい日本語訳があるので興味のある方は参照してほしい。逆の処理をするevalもついでにラッピング。こちらはFunctionも認識してしまう。
/*
================================================================================
    Name        :   JSON
    In          :   [none]      
    Out         :   [none]      
    Note        :   JSONユーティリティ群
--------------------------------------------------------------------------------
    Version     :   Ver1.0.0    |   2006/12/04  |   新規作成
--------------------------------------------------------------------------------
    License     :   New BSD license
    URL         :   www.kanasansoft.com
================================================================================
*/

/*--------------------------------------------------------------------------------
    コンストラクタ
--------------------------------------------------------------------------------*/
function JSON(){
}

/*--------------------------------------------------------------------------------
    JSONエンコードを行なう
--------------------------------------------------------------------------------*/
JSON.Encode
=   function(obj){

    var rtn;

    if(obj==null){

        rtn = "null";

    }else{

        switch(obj.constructor){

            case Boolean:
                rtn = obj?"true":"false";
                break;

            case Number:
                rtn = isNaN(obj)||!isFinite(obj)?"null":obj.toString(10);
                break;

            case String:
                rtn = "\""+StringUtility.Encode.JavaScript(obj)+"\"";
                break;

            case Array:
                var buf = [];
                for(var i=0;i<obj.length;i++){
                    //再帰呼出
                    buf.push(arguments.callee(obj[i]));
                }
                rtn = "["+buf.join(",")+"]";
                break;

            case Object:
                var buf = [];
                for(var key in obj){
                    //Object汚染回避判定有
                    if(obj.hasOwnProperty(key)){
                        //再帰呼出
                        buf[buf.length] = arguments.callee(key)+":"+arguments.callee(obj[key]);
                    }
                }
                rtn = "{"+buf.join(",")+"}";
                break;

            default:
                rtn = "null";
                break;

        }

    }

    return rtn;
}

/*--------------------------------------------------------------------------------
    JSONデコードを行なう
--------------------------------------------------------------------------------*/
JSON.Decode
=   function(str){

    var rtn;

    eval("rtn="+str);

    return rtn;
}
追記(2007/07/07)
バージョンアップしたものが下記リンク先で公開済。

2006年12月14日

Kanasansoft BlogEditor公開

開発の動機
blogでプログラムの解説が面倒に思ったことはないか。私はめんどくさいと思う。
例えば文章とコードが混在するような内容だと
JavaScriptのfor分は次のように書く。
<div class="code">
for(var&nbsp;i=0;i&lt;ary.length;i++){<br />
&nbsp;&nbsp;&nbsp;&nbsp;alert(ary[i]);<br />
}
</div>
のようになり、encodeが必要な部分と必要でない部分が入りまじってしまって書くのに集中できない。blogの設定で、htmlをencodeするのかしないのか、改行は自動で行うのかの指定はエントリー毎にできるけど、「ここからここまではencode、ここはencodeなし」というような柔軟性はない。レイアウト上の理由でdivタグの出力は行いたいが、その中身、つまりコンテンツ部分はencodeしたい。色々、実現方法を考えているうちに思いついたのが「文章・コード・bookmarklet等を、div単位で記述するエリア」として、「このエリアをブロックみたいに積み重ねていく」構成である。これを実現したのが今回公開する「Kanasansoft BlogEditor」となる。この文章はまさにBlogEditorを使って書いている。

Kanasansoft BlogEditor設定方法(その1)

BlogEditorはBookmarkletで構成されている。
(詳しい仕組みはそのうち解説するかもしれないが、興味のある人はコードを見てほしい。)
設定が少々面倒ではあるが我慢して頂きたい。まずはダウンロードと解凍を行なって欲しい。
設置場所
まず、設置場所を決める。制約は「ブラウザからアクセスできる場所」、どこかのサイトでもいいし、自分のPCにApacheなんかが入っているならそこでもいい。ただ、あまりパスが長いとIEの「お気に入り」の文字数制限に引っかかるから気をつけて頂きたい。
(もしかするとWindowsなら「c:¥」と書けばローカルにアクセスできるかもしれないが、試していないため何とも言えない。)
ここでは「http://xxx/utility/」に置くことにする。
settingInitial.js
Kanasansoft->Bookmarklet->BlogEditor->settingInitial.js

定数DYNAMIC_LOAD_FILE_PATHのlocalhostとなっているところを自分の環境に書き換えよう。
var DYNAMIC_LOAD_FILE_PATH  =   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/DynamicLoad.js";
DynamicLoad.js
Kanasansoft->Bookmarklet->BlogEditor->DynamicLoad.js

定数KANASANSOFT_FOLDER_PATHをさっきと同じように書き換える。
var KANASANSOFT_FOLDER_PATH =   "http://xxx/utility/Kanasansoft/";
BookmarkletInitial.js
Kanasansoft->Bookmarklet->BlogEditor->BookmarkletInitial.js

ここはちょっと多めになる。テンプレート(別エントリーで説明)の数だけ書き換える必要がある。
var TEMPLATE_FILE_PATHS     =   [];
TEMPLATE_FILE_PATHS         .   push(   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/Template/Kanasansoft/PlainText.js"       );
TEMPLATE_FILE_PATHS         .   push(   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/Template/Kanasansoft/Source.js"          );
TEMPLATE_FILE_PATHS         .   push(   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/Template/Kanasansoft/Bookmarklet.js"     );
TEMPLATE_FILE_PATHS         .   push(   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/Template/Kanasansoft/LinkForPage.js"     );
TEMPLATE_FILE_PATHS         .   push(   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/Template/Kanasansoft/LinkForFile.js"     );
TEMPLATE_FILE_PATHS         .   push(   "http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/Template/Kanasansoft/Image.js"           );
JavaScriptで書かれているテンプレートをBookmarkletで動的に読み込んでいるのだが、「誰か他の人が書いたテンプレートに直接アクセスする」ような需要をみこしてこのようになっている。本来はこのサイト上に置けばいいのだが、レンタルサーバのためにあまりアクセスを集中させたくない。また、このサイトが仮に閉じてしまったことを考えてのことなのでご了承願いたい。

更に、TARGET_TEXTAREA_IDを書き換えなくてはならない。BlogEditorを使用するblogのエントリー入力画面にあるテキストエリアのIDが必要だ。
(少なくとも昨今のblogは入力欄にID位は指定しているだろうという考え。)
例えば、あなたが「KanasanBlog」を使っていてテキストエリアのIDが「entry_body」だったとしたら下のようになる。
(Movable Typeは絶対使わないのであれば、その行は削除してもいい。)
var TARGET_TEXTAREA_ID      =   {};
TARGET_TEXTAREA_ID["Movable Type"]  =   "text";
TARGET_TEXTAREA_ID["KanasanBlog"]   =   "entry_body";

Kanasansoft BlogEditor設定方法(その2)

BlogEditorの設置
「http://xxx/utility/」に設置場所を決めたはずなので、Kanasansoftフォルダをここにアップロードしよう。
(下の階層に同名のフォルダがあるが間違えないで頂きたい。深すぎる為、間違えににくいと思うが念のため。)
Bookmarkletの登録
ここでも「http://xxx/utility/」に設置したと仮定する。「http://xxx/utility/Kanasansoft/Bookmarklet/BlogEditor/setting.htm」にアクセスしてみよう。タイトルが「Bookmarklet Kanasansoft BlogEditor」になっているページが開く。「Kanasansoft BlogEditor」というリンクが表示されているはずだ。表示されていないなら「文字のエンコード」をチェックしよう。BlogEditorは文字コードが「UTF-8」で書かれているが、一部の環境ではブラウザが正常に認識しないため、表示されないことがある。
(誤認識を起こしているのに表示される。)
(誤認識を避ける方法があるなら教えてほしい...。)
表示されているリンクを右クリック(Macでシングルボタンのマウスの場合はcontrol+クリック)してブックマーク(IEではお気に入り)に登録しよう。
blogの設定
htmlのエンコードや改行処理はBlogEditor側で行う。blog側のこれらの設定が有効になっていると重複した処理になるため、無効にする。
(各blogのヘルプを参照のこと。)
BlogEditorの起動テスト
BlogEditorは別ウィンドウで起動する。ウィルス対策ソフト等のポップアップブロックが有効になっていると動かないので無効にする。ポップアップブロックの有効無効はサイト別に指定できるはずなので、自分のblogのあるサイト(可能ならディレクトリ)のみ無効にしよう。blogの新規エントリーを開いてほしい。表示されたら、bookmark(またはお気に入り)から「Kanasansoft BlogEditor」を選択する。
別ウィンドウが表示されるはずだ。
(タブブラウザなら新規タブに表示される。)
新規ウィンドウに何も表示されないなら、また「文字コード」を疑ってみよう。方法は先程と同じ。
(起動時にライブラリ等を読み込むので表示までに数秒かかることがあるので注意。)
左側に何かの一覧が表示されたらBlogEditorの起動テストは完了となる。