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("");
};
}







