webAi.Tools - ソースコード
説明
「Web AI」は、クロノス・クラウンの柳井政和が開発しているJavaScirptのライブラリです。「Web AI」を使うと、Web上からデータを取得して構築するWebページを、とても簡単に構築することができます。
「Web AI」は、Webからの検索結果やフィードの取得、短縮URLへのURLの変換、日本語文章からのキーワードや文章抽出、日本語文章のマルコフ連鎖、クエリーの管理などの機能を備えています。
このページでは、この「Web AI」のファイルの1つである「crocro.webAi.Tools.js」のソースコードを掲載します。
「crocro.webAi.Tools.js」
「crocro.webAi.Tools.js」には、URLのクエリーを便利に操作する機能がまとまっています。また、その他の雑多な機能も、このファイルには収録されています。「crocro.webAi.Tools.js」では主に、以下の2つの機能をサポートします。
*URLクエリーの解析と構築。 *URLクエリーの圧縮と解凍。
「crocro.webAi.Tools.js」は、その他のファイルとは違い、複数のオブジェクトが収録されています。収録されているオブジェクトは以下になります。
*crocro.webAi.Tools *crocro.webAi.Query *crocro.webAi.Cmprs *crocro.webAi.Base64 *crocro.webAi.Lzw
この内、ユーザーが実際に使用するのは、「crocro.webAi.Tools」「crocro.webAi.Query」の2つになります。その他は内部処理用のオブジェクトです。
「crocro.webAi.Query」を利用すれば、HTMLのクエリーを簡単に操作することができます。
使い方は、「Web AI」の各サンプルを参考にしてください。
ソースコード
以下、「crocro.webAi.Tools.js」のソースコードです。
/* Web AI <http://crocro.com/write/web_ai/wiki.cgi> is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> in a Japanese translation <http://sourceforge.jp/projects/opensource/wiki/licenses%2FMIT_license> Copyright (c) 2010 Masakazu Yanai / (c) 2010 Cronus Crown <webmaster@crocro.com> */ // crocro.webAiの初期化 if (! crocro) var crocro = {}; if (! crocro.webAi) crocro.webAi = {}; /** * @variable crocro.webAi.Tools * @title ツール群 * @description * * プログラム記述用のツール群。 * newで初期化せずにそのまま利用する。 */ crocro.webAi.Tools = new function() { // 内部利用変数 var majorVersion = "1"; var minorVersion = "1"; /* *-------------------------------------------------- */ /** * @variable Tools.getVer() * @title バージョン取得 * @description * * バージョン情報を取得する。 * * @return バージョン情報。 */ this.getVer = function() { return majorVersion + "." + minorVersion; } /** * @variable Tools.getMajorVer() * @title メジャー・バージョン取得 * @description * * メジャー・バージョン情報を取得する。 * * @return メジャー・バージョン。 */ this.getMajorVer = function() { return majorVersion; } /** * @variable Tools.getMinorVer() * @title マイナー・バージョン取得 * @description * * マイナー・バージョン情報を取得する。 * * @return マイナー・バージョン。 */ this.getMinorVer = function() { return minorVersion; } /** * @variable Tools.isLocal() * @title ローカルの判定 * @description * * ローカルか否かを戻す。 * * @return ローカルか否か(true/false)。 */ this.isLocal = function() { if (location.protocol.indexOf("file") != 0) return false; // ネット上 return true; // ローカル } /** * @variable Tools.getObjInf(obj, eleId) * @title オブジェクト情報の取得 * @description * * オブジェクトのプロパティと内容(ハッシュのキーと値)を取得/挿入する。 * * @param obj 表示するオブジェクト。 * @param eleId 要素を挿入するエレメントID。指定がある場合は、その場所に整形済みHTMLを挿入する。 * @return 作成した文字列。 */ this.getObjInf = function(obj, eleId) { var resArr = []; if (! obj) return ""; for (var key in obj) { resArr.push("key : " + key + " / " + typeof(obj[key]) + " / " + obj[key]); } if (eleId) { document.getElementById(eleId).innerHTML = "<ul><li>" + resArr.join("</li><li>") + "</li></ul>"; } // 戻り値を戻して終了 var resStr = resArr.join("\n"); return resStr; } /** * @variable Tools.shffl(srcArr) * @title 配列シャッフル * @description * * シャッフルした配列を新たに作成して戻す。 * * @param srcArr 元配列。 * @return シャッフルした配列。 */ this.shffl = function(srcArr) { var resArr = srcArr.concat(); var i = resArr.length; while (--i) { var j = Math.floor(Math.random() * (i + 1)); if (i == j) continue; var k = resArr[i]; resArr[i] = resArr[j]; resArr[j] = k; } return resArr; } /** * @variable Tools.unq(srcArr) * @title 配列ユニーク * @description * * ユニークにした配列を新たに作成して戻す。 * * @param srcArr 元配列。 * @return ユニークにした配列。 */ this.unq = function(srcArr) { var resArr = []; var hash = {} for (var i = 0; i < srcArr.length; i ++) { if (! srcArr[i] || srcArr[i] == 0) continue; hash[srcArr[i]] = 1; } for (var key in hash) { resArr.push(key); } return resArr; } } /** * @variable crocro.webAi.Query(arg) * @title クエリー * @description * * クエリーを抽象化するためのオブジェクト。Webページの「?」以降の値を、連想配列に格納する。 * 「?」以降がない場合は「#」以降を対象にする。 * newで初期化してから利用する。 */ crocro.webAi.Query = function(arg) { // 内部利用変数 var hash = {}; var urlBdy = null; /** * @variable Query.cmprsKe * @title 圧縮クエリー用キー * @description * 圧縮クエリー使用時の、クエリーのキー。 * * デフォルトは「_cmp」。 * 圧縮クエリー使用時には、「_cmp=~」といったクエリーになる。 */ this.cmprsKey = "_cmp"; /* *-------------------------------------------------- */ /** * @variable Query.set(key, prm) * @title データの書き込み * @description * * 「hash」にデータを書き込む。 * * @param key キー。 * @param prm 値。 * @return なし。 */ this.set = function(key, prm) { hash[key] = prm; } /** * @variable Query.get(key) * @title データの読み込み * @description * * 「hash」からデータを読み込む。 * * @param key キー。 * @return 値。 */ this.get = function(key) { if (! (key in hash)) return ""; var resStr = hash[key] .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); return resStr; } /** * @variable Query.getRaw(key) * @title 生データの読み込み * @description * * 「hash」からデータをエスケープなしで読み込む。 * 通常はセキュリティのために「get」の方を利用する。 * * @param key キー。 * @return 値。 */ this.getRaw = function(key) { if (! (key in hash)) return ""; return hash[key]; } /** * @variable Query.isKey(key) * @title キーの存在確認 * @description * * 「hash」内に、指定の「key」があるか確認する。 * * @param key キー * @return キー存在の有無(true/false) */ this.isKey = function(key) { return (key in hash); } /* *-------------------------------------------------- */ /** * @variable Query.prsFrmLocation(arg) * @title locationからのパース * @description * * locationからパースして、クエリーのハッシュを作成する。 * * Webページの「?」以降の値を、連想配列に格納する。 * 指定によっては、「?」以降がない場合は「#」以降を対象にする。 * * @param arg.useQ クエリーの利用(true/false)デフォルトはyes。 * @param arg.useH ハッシュの利用(true/false)デフォルトはno。ローカルのデバッグ用。 * @return なし。 */ this.prsFrmLocation = function(arg) { if (! arg) arg = {}; // 引数なしなら初期化 arg.url = location.href; this.prsFrmUrl(arg); }; /** * @variable Query.prsFrmUrl(arg) * @title URLからのパース * @description * * URLからパースして、クエリーのハッシュを作成する。 * * Webページの「?」以降の値を、連想配列に格納する。 * 指定によっては、「?」以降がない場合は「#」以降を対象にする。 * * @param arg.url URL文字列。 * @param arg.useQ クエリーの利用(true/false)デフォルトはyes。 * @param arg.useH ハッシュの利用(true/false)デフォルトはno。ローカルのデバッグ用。 * @return URL本体を戻す。 */ this.prsFrmUrl = function(arg) { // ユーザー変数 var url = ""; // URL var useQ = true; // クエリーの利用(true/false)デフォルトはyes。 var useH = false; // ハッシュの利用(true/false)デフォルトはno。ローカルのデバッグ用。 // ユーザー変数の設定 if (arg) { if ("url" in arg) url = arg.url; if ("useQ" in arg) useQ = arg.useQ; if ("useH" in arg) useH = arg.useH; } if (! url || url == "") return ""; // クエリーの取り出し var b = "" + (url.match(/^.+(?:[\?#])/) || url); // URL本体の取得 var q = "" + (url.match(/\?.+(?:#|$)/) || ""); // クエリーの取得 var h = "" + (url.match(/#[^#].+$/) || ""); // ハッシュの取得 if (useQ && q != "") { q = q.replace(/^\?/, ""); // 先頭の?の除去 } else if (useH && h != ""){ q = h.replace(/^#/, ""); // 先頭の#の除去 } // URLの格納 b = b.replace(/[\?#].*$/g, ""); urlBdy = b; //alert("url " + url + "\nb " + b + "\nq " + q + "\nh " + h); // 文字列からのパース this.prsFrmStr(q); // URL本体を戻す return b; } /** * @variable Query.prsFrmStr(q) * @title 文字列からのパース * @description * * クエリー部分を文字列からパースして、ハッシュを作成する。 * * @param q 元の文字列。 * @return なし。 */ this.prsFrmStr = function(q) { // 圧縮の確認 if (q.indexOf(this.cmprsKey) == 0) { // 圧縮データがあるので解凍 var cmpStr = q.substr(this.cmprsKey.length + 1); q = crocro.webAi.Cmprs.dcmprs(cmpStr); // ハッシュの作成 var qArr = q.split("&"); // 配列化 for (var i = 0; i < qArr.length; i ++) { var tmpArr = qArr[i].split("="); if (tmpArr.length != 2) continue; // 値の格納 var key = tmpArr[0]; var prm = tmpArr[1]; // エスケープの戻し key = key .replace(/%3D/g, "=") .replace(/%26/g, "&"); prm = prm .replace(/%3D/g, "=") .replace(/%26/g, "&"); hash[key] = prm; // 圧縮時にエンコードしていないのでデコードは不要 } return; } // ハッシュの作成 var qArr = q.split("&"); // 配列化 for (var i = 0; i < qArr.length; i ++) { var tmpArr = qArr[i].split("="); if (tmpArr.length != 2) continue; // 値の格納 var key = tmpArr[0]; hash[key] = decodeURIComponent(tmpArr[1]); } } /* *-------------------------------------------------- */ /** * @variable Query.getPrms() * @title 値リストの取得 * @description * * 値のリストを配列で取得する。 * * @return 値。 */ this.getPrms = function() { var resArr = []; for (var key in hash) { var prm = '{"' + key.replace(/"/g, '\"') + '" : "' + hash[key].replace(/"/g, '\"') + '"}'; resArr.push(prm); } return resArr; }; /** * @variable Query.getQuer() * @title クエリー形式で値を取得 * @description * * 値のリストをクエリー形式で取得する。 * * @return 値リストのクエリー。 */ this.getQuery = function() { var resArr = []; for (var key in hash) { var prm = encodeURIComponent(hash[key]); key = encodeURIComponent(key); resArr.push(key + '=' + prm); } return resArr.join("&"); } /** * @variable Query.getCmprsQuery() * @title 圧縮クエリー形式で値を取得 * @description * * 値のリストを圧縮したクエリー形式にして取得する。 * * @return 値リストを圧縮したクエリー。 */ this.getCmprsQuery = function() { var resArr = []; for (var key in hash) { if (! hash[key]) continue; var prm = hash[key] .replace(/=/g, "%3D") .replace(/&/g, "%26"); key = key .replace(/=/g, "%3D") .replace(/&/g, "%26"); resArr.push(key + '=' + prm); } var qStr = resArr.join("&"); var cmpStr = crocro.webAi.Cmprs.cmprs(qStr); var cmbStr = this.cmprsKey + "=" + cmpStr; return cmbStr; } /* *-------------------------------------------------- */ /** * @variable Query.getUrl() * @title URL取得 * @description * * クエリーを付加したURLを取得する。 * * @return URL文字列。 */ this.getUrl = function() { return urlBdy + "?" + this.getQuery(); } /** * @variable Query.getCmprsUrl() * @title 圧縮URL取得 * @description * * クエリーを付加したURLを取得する。 * 値のリストは圧縮したクエリー形式にして取得する。 * * @return URL文字列。 */ this.getCmprsUrl = function() { return urlBdy + "?" + this.getCmprsQuery(); } }; /* *-------------------------------------------------- */ /** * @variable crocro.webAi.Cmprs * @title 【圧縮解凍】 * @description * * クエリーを圧縮するためのオブジェクト。 * newせずにそのまま利用する。 */ crocro.webAi.Cmprs = new function() { /* *-------------------------------------------------- */ /** * @variable Cmprs.cmprs(uStr) * @title 圧縮 * @description * * ユニコードの文字列をクエリーに利用できる形で圧縮する。 * * @param uStr ユニコードの文字列。 * @return 圧縮した文字列。 */ this.cmprs = function(uStr){ // 文字列を圧縮してBase64化 var bStr = crocro.webAi.Base64.u2B(uStr); // バイト化した文字列に変換 var cStr = crocro.webAi.Lzw.cmprs(bStr); // 圧縮 var b64s = crocro.webAi.Base64.toBs64(cStr); // バイトの数値配列をBase64化 return b64s; }; /** * @variable Cmprs.dcmprs(b64s) * @title 解凍 * @description * * クエリーに利用できる形で圧縮した文字列を、ユニコードの文字列に解凍する。 * * @param b64s クエリーに利用できる形で圧縮した文字列。 * @return ユニコードの文字列。 */ this.dcmprs = function(b64s){ // Base64を解凍して文字列に var bStr = crocro.webAi.Base64.frmBs64(b64s); // Base64をバイト化した文字列に var dStr = crocro.webAi.Lzw.dcmprs(bStr); // 解凍 var uStr = crocro.webAi.Base64.b2U(dStr); // 解凍したデータをユニコード文字列に return uStr; }; } /* *-------------------------------------------------- */ /** * @variable crocro.webAi.Base64 * @title Base64 * @description * * クエリーを圧縮するためのオブジェクト。通常は「Cmprs」内で利用する。 * newせずにそのまま利用する。 * Base64のコード、デコード、ユニコード文字列のバイト範囲化を行う。 */ crocro.webAi.Base64 = new function() { // ユニコード→バイト用変数 var reChrNoAscii = /[^\x00-\xFF]/g; var repChrNoAscii = function(m){ var n = m.charCodeAt(0); return n < 0x800 ? String.fromCharCode(0xc0 | (n >>> 6)) + String.fromCharCode(0x80 | (n & 0x3f)) : String.fromCharCode(0xe0 | ((n >>> 12) & 0x0f)) + String.fromCharCode(0x80 | ((n >>> 6) & 0x3f)) + String.fromCharCode(0x80 | (n & 0x3f)); }; /** * @variable Base64.u2B(uStr) * @title ユニコード→バイト * @description * * ユニコードの文字列をバイト処理用の文字列にして取得する。 * * @param uStr ユニコードの文字列。 * @return バイト化した文字列。 */ this.u2B = function(uStr){ return uStr.replace(reChrNoAscii, repChrNoAscii); }; /* *-------------------------------------------------- */ // バイト→ユニコード用変数 var reBytNoAscii = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g; var repBytNoAscii = function(m){ var c0 = m.charCodeAt(0); var c1 = m.charCodeAt(1); if(c0 < 0xe0){ return String.fromCharCode(((c0 & 0x1f) << 6) | (c1 & 0x3f)); }else{ var c2 = m.charCodeAt(2); return String.fromCharCode( ((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f) ); } }; /** * @variable Base64.b2U(bSt) * @title バイト→ユニコード * @description * * バイト処理用の文字列をユニコードの文字列にして取得する。 * * @param bStr バイト化した文字列。 * @return ユニコードの文字列。 */ this.b2U = function(bStr){ return bStr.replace(reBytNoAscii, repBytNoAscii); }; /* *-------------------------------------------------- */ // Base64用変数 var bs64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; var bs64tab = function(bin){ var t = {}; for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i; return t; }(bs64chars); // バイト→Base64用変数 var repToBs64 = function(m){ var n = (m.charCodeAt(0) << 16) | (m.charCodeAt(1) << 8) | (m.charCodeAt(2) ); return bs64chars.charAt( n >>> 18) + bs64chars.charAt((n >>> 12) & 63) + bs64chars.charAt((n >>> 6) & 63) + bs64chars.charAt( n & 63); }; /** * @variable Base64.toBs64(bin) * @title バイト→Base64 * @description * * バイト処理用の文字列をBase64の文字列にして取得する。 * * @param bin バイト化した文字列。 * @return Base64の文字列。 */ this.toBs64 = function(bin) { if (bin.match(/[^\x00-\xFF]/)) throw 'err'; var padlen = 0; while(bin.length % 3) { bin += '${ld:body}'; padlen ++; }; var b64 = bin.replace(/[\x00-\xFF]{3}/g, repToBs64); if (! padlen) return b64; b64 = b64.substr(0, b64.length - padlen); while(padlen --) b64 += '_'; return b64; }; /* *-------------------------------------------------- */ /* *-------------------------------------------------- */ // Base64→バイト用変数 var repfrmBs64 = function(m){ var n = (bs64tab[ m.charAt(0) ] << 18) | (bs64tab[ m.charAt(1) ] << 12) | (bs64tab[ m.charAt(2) ] << 6) | (bs64tab[ m.charAt(3) ]); return String.fromCharCode( n >> 16) + String.fromCharCode((n >> 8) & 0xff) + String.fromCharCode( n & 0xff); }; /** * @variable Base64.frmBs64(b64) * @title Base64→バイト * @description * * Base64の文字列をバイト処理用の文字列にして取得する。 * * @param b64 Base64の文字列。 * @return バイト処理用の文字列。 */ this.frmBs64 = function(b64) { b64 = b64.replace(/[^A-Za-z0-9\+\/]/g, ''); var padlen = 0; while(b64.length % 4){ b64 += 'A'; padlen ++; } var bin = b64.replace(/[A-Za-z0-9\+\/]{4}/g, repfrmBs64); bin = bin.substr(0, bin.length - padlen); return bin; }; } /* *-------------------------------------------------- */ /** * @variable crocro.webAi.LZW * @title LZW * @description * * クエリーを圧縮するためのオブジェクト。通常は「Cmprs」内で利用する。 * newせずにそのまま利用する。 * 圧縮後のデータの上位4バイトは、オフセット・サイズを記録するために使用。 */ crocro.webAi.Lzw = new function() { // ユーザー用変数 /** * @variable LZW.tmOutMSec * @title タイムアウト用ミリ秒 * @description * 不正な文字列が与えられた際に、無限ループに至るのを回避するための設定。 * * デフォルトは「1000」。 */ this.tmOutMSec = 1000; // タイムアウト用ミリ秒 /* *-------------------------------------------------- */ // データ function Data(arg0, arg1) { this.byts = arg0 || []; this.ofst = 0; this.cnt = arg1; this.write = function(v, n) { for(var i = 0; i < n; ++i) { this.byts[this.ofst >>> 3] |= ((v >>> i) & 1) << (this.ofst & 7); this.ofst ++; } } this.read = function(n) { if((this.ofst + n) > this.cnt) return null; var v = 0; for(var i = 0; i < n; ++ i) { var tmp = this.byts[this.ofst >>> 3] >> (this.ofst & 7); this.ofst ++; v |= (tmp & 1) << i; } return v; } } function getLen(n) { return Math.ceil(Math.log(n) / Math.LN2); } // 辞書 function Dic(isCmprs) { this.isCmprs = isCmprs; this.hash = {}; this.sz = 256; this.len = this.isCmprs ? function() {return getLen(this.sz + 1);} : 9; for(var i = 0; i < this.sz; i ++) { this.isCmprs ? (this.hash[String.fromCharCode(i)] = i) : (this.hash[i] = String.fromCharCode(i)); } this.is = function(v) {return v in this.hash;}; this.get = function(v) {return this.hash[v];}; this.set = function(v) { if (this.isCmprs) { var n = this.len(); this.hash[v] = this.sz ++; return n; } else { this.hash[this.sz ++] = v; return this.len = getLen(this.sz + 2); } }; } /* *-------------------------------------------------- */ /** * @variable LZW.cmprs(str) * @title 圧縮 * @description * * バイト文字列を圧縮する。 * * @param str バイト文字列。 * @return 圧縮した配列。 */ this.cmprs = function(str) { // 変数の初期化 var len = str.length; var dat = new Data(); if(len > 0) { var dic = new Dic(true); var n = dic.len(); var w = ""; // 圧縮 for(var i = 0; i < len; i++) { var c = str.charAt(i); if(dic.is(w + c)) { w += c; } else { n = dic.set(w + c); dat.write(dic.get(w), n); w = c; } } dat.write(dic.get(w), n); } // オフセットを頭に記録 var ofst = dat.ofst; var ofstArr = [ ofst >>> 24, ofst >>> 16, ofst >>> 8, ofst & 255 ]; // バイト化した文字列を作成して戻す var resArr = ofstArr.concat(dat.byts); for (var i = 0; i < resArr.length; i ++) { resArr[i] = String.fromCharCode(resArr[i]); } return resArr.join(""); }; /** * @variable LZW.dcmprs(str) * @title 解凍 * @description * * バイト化した文字列を解凍する。 * * @param str バイト化した文字列。 * @return 解凍した文字列。 */ this.dcmprs = function(str) { // 配列の作成 var byts = []; for (var i = 0; i < str.length; i ++) { byts.push(str.charCodeAt(i)); } // オフセットの取り出し var ofst = (byts.shift() << 24) + (byts.shift() << 16) + (byts.shift() << 8) + byts.shift(); if (ofst == 0) return ""; // 変数の初期化 var dat = new Data(byts, ofst); var dic = new Dic(false); var n = dic.len; var k = dat.read(n); var w = String.fromCharCode(k); var resArr = [w]; var entry = ""; // タイムアウトの初期化 var tmStrt = (new Date()).getTime(); var tmOutCnt = 0; // 解凍 while ((k = dat.read(n)) != null) { // 不正なデータ時のタイムアウト処理 if (tmOutCnt ++ >= 1000) { if ((new Date()).getTime() - tmStrt >= this.tmOutMSec) break; } // 通常の解凍処理 entry = (k < dic.sz) ? dic.get(k) : (w + w.charAt(0)); resArr.push(entry); n = dic.set(w + entry.charAt(0)); w = entry; } return resArr.join(""); }; }