/** 
@fileoverview ブラウザ用JavaScriptコア
@author nossie
@version 1.0
*/

// << EcmaScriptユーティリティ >> //
// グローバルオブジェクト
var global = this;
// 名前空間の管理 //
var jsutil_namespace="jsutil";
/** プロパティの単純コピーを行う.
@param {Object} dist コピー先 
@param {Object} source コピー元 */
function copy(dist,source) {
	for(var i in source) dist[i] = source[i];
	/*@cc_on 
	if(source.hasOwnProperty("toString"))
		dist.toString = source.toString;
	if(source.hasOwnProperty("valueOf"))
		dist.valueOf = source.valueOf;@*/
	return dist;
}

/** オーバライドが必要な関数のデフォルト値 */
function notOverrided() { throw Error("Not overrided."); }
/** 何もしない関数 */
function nothingTodo() {};

/** JavaScript 用ユーティリティ 
@name jsutil
@namespace */
var jsutil = {};
void function() {
	var $ = (new Package).get;
/** プロトタイプ継承を行う.
@param {Function} DerivedConstructor 派生クラスのコンストラクタ
@param {Function} BaseConstructor 基底クラスのコンストラクタ */
jsutil.chainPrototype = function(DerivedConstructor,BaseConstructor) {
	// 副作用無しでインスタンスを取得
	DerivedConstructor.prototype = jsutil.newEmptyInstance(BaseConstructor);
	// コンストラクタを復元
	DerivedConstructor.prototype.constructor = DerivedConstructor;
};
/** コンストラクタを起動せずにインスタンスを作成する.
<p> 主にプロトタイプチェーンの構築、コンストラクタの分割のために使用できます。</p>
@param {Function} Constructor コンストラクタ */
jsutil.newEmptyInstance = function(Constructor) {
	// 副作用の無い空のコンストラクタを生成
	var Empty = function(){};
	// プロトタイプを統一。これでinstanceof演算子は両者を同一とみなす。
	Empty.prototype = Constructor.prototype;
	// 派生側のプロトタイプを指定。
	return new Empty;
};
/** 配列の要素を引数にしてnew演算子を呼び出す.
@param {Function} Constructor コンストラクタ
@param {Object[]} argumentsArray 引数に使う配列
@example
// Point クラスのメソッド Point.prorotype.add
// コンストラクタと同じ引数を受け取る
Point.prototype.add = function(/* ... *&#x2F;) {
	var p = newApply(Point,arguments);
	return new Point(this.getX()+p.getX(),this.getY()+p.getY());
}; */
jsutil.newApply = function(Constructor,argumentsArray) {
	// コンストラクタを起動せずにインスタンスを作成
	var instance = jsutil.newEmptyInstance(Constructor);
	// applyを呼び出しnew演算子をエミュレートする。
	Constructor.apply(instance,argumentsArray);
	return instance;
}
/** 残余引数を配列で取得する.
@param {Arguments} callerArguments 呼び出し元の <code>arguments</code> オブジェクト */
jsutil.getRestArguments = function(callerArguments) {
	var	expectedCount = callerArguments.callee.length;
	var actualCount = callerArguments.length;
	var restArguments = new Array(actualCount - expectedCount);
	for(var i=0;i<restArguments.length;i++)
		restArguments[i] = callerArguments[expectedCount+i];
	return restArguments;
};
/** 引数エラーを表す.
@name ArgError
@class
@augments Error
@param {String} msg メッセージ*/
jsutil.ArgError = ArgError;
jsutil.chainPrototype(ArgError, Error);
function ArgError(msg) { 
	if(!this) return new ArgError(msg);
	Error.call(this,msg);
}
/** 新しいプロパティを追加できるかどうかを取得します。
@param obj {Object} 検査するオブジェクト */
jsutil.enableNewProperty = function(obj) {
	// instanceof Object を使うと window (IE, FireFox) や
	// DOM 要素 (IE限定) で false になる。
	switch(typeof obj) {
		case "object": return obj !== null;
		case "function": return true;
		default: return false;
	}
};
/** プロパティの予約を行います.
<p> <code>obj[name]</code> が存在すればその値を戻し、
存在しなければ <code>value</code> で初期化します。</p>
@param obj {Object} プロパティを設定するオブジェクト
@param name {String} プロパティの名前
@param value {Object} プロパティの値 */
jsutil.reserve = function(obj, name, value) {
	if(!jsutil.enableNewProperty(obj)) throw ArgError();
	if(typeof name !== "string") throw ArgError();
	return obj[name] || (obj[name] = value);
};
/** プロパティの予約を行います.
<p> <code>obj[name]</code> が存在すればその値を戻し、
存在しなければ <code>valueCreator</code> で作成し初期化します。</p>
@param obj {Object} プロパティを設定するオブジェクト
@param name {String} プロパティの名前
@param valueCreator {Function} プロパティの値を作成する関数 */
jsutil.reserveDo = function(obj, name, valueCreator) {
	if(!jsutil.enableNewProperty(obj)) throw ArgError();
	if(typeof name !== "string") throw ArgError();
	if(typeof valueCreator !== "function") throw ArgError();
	return obj[name] || (obj[name] = valueCreator());
};
/** 名前空間オブジェクトを取得します.
<p> <code>obj["ns." + namespace]</code> を返します。
それが無ければ、<code>{}</code> で初期化します。</p>
<p> <code>obj</code> は新しいプロパティを追加出来る必要があります。
そのため、EcmaScript 5 th エディションで導入された <code>seal</code> や
<code>freeze</code> の対象になったオブジェクトや、IE で <code>new 
ActiveXObject</code> から作成したオブジェクトは使用できません。</p>
@param {Object} obj 名前空間を追加するオブジェクト
@param {String} namespace 名前空間名 */
jsutil.getNamespace = function(obj, namespace) {
	if(typeof namespace !== "string") throw ArgError();
	return jsutil.reserve(obj, "ns."+namespace, {});
};
/** 擬似パッケージを表す.
<p> 関数スコープを利用して各オブジェクトに
その中でのみ参照可能なフィールドを追加します。</p>
<p> <strong> この機能はデバッグ用にのみ提供されています。</strong>
(EcmaScript 3rd Edition では public メソッドが書き換え可能なため
完全なパッケージングは不可能です。) </p> 
@class
@example 
// 名前空間にパッケージレベルフィールドを追加する例。
var mynamespace = {};
void function() {
    var $ = (new jsutil.Package).get;
    $(mynamespace).packField1 = {};
    $(mynamespace).packField2 = {};
}
@example
// クラスのインスタンスにパッケージレベルフィールドを追加する例。
void function() {
    var $ = (new jsutil.Package).get;
    function MyClass() {
        $(this).packField1 = {};
        $(this).packField2 = {};
    }
} */
jsutil.Package = Package;
function Package() {
	/** パッケージフィールドを管理するオブジェクトを取得します.
	@param {Object} obj パッケージフィールドを取得するオブジェクト*/
// IF DEBUG
	var This = this; 
	this.get = function(obj) {
		return jsutil.reserveDo(obj, "$getPackValues", 
				function() { return makeValueKeeper(This, {});}
			)(This);
	};
	function makeValueKeeper(key, value) {
		return function(key2) {
			if(key !== key2) throw Error("access error");
			return value;
		};
	}
// END IF */
/* IF NOT DEBUG
	this.get = function(obj) {
		return jsutil.reserve(obj, "$packValues", {});
	};
// END IF */
}
/** URI を表します.
<p> RFC2396で定義されるURI参照の操作を補助します。</p>
@class */
jsutil.URI = function() { }; 
/** @lends jsutil.URI */ copy(jsutil.URI, {
	/** 文字列から構築します.
	@param {String} str URI を表す文字列
	@throws {Error} URI の解析に失敗した場合
	@returns {jsutil.URI} 構築した URI */
	fromString: function(str) {
		var parseResult = _uriRegExp.exec(str);
		if(parseResult === null) throw ArgError("URI parse error.");
		/*/ 以下の冗長なコードはIEバグ回避用。
		対応する文字列が無い場合、undefined でなく空文字になる。/*/
		return jsutil.URI.fromComponents(
			parseResult[1]?parseResult[2]:null,
			parseResult[3]?parseResult[4]:null,
			parseResult[5],
			parseResult[6]?parseResult[7]:null,
			parseResult[8]?parseResult[9]:null);
	},
	/** 各種コンポーネント文字列から構築します.
	@param {String} scheme スキーマ 
	@param {String} authority オーソリティ 
	@param {String} path パス
	@param {String} query クエリ
	@param {String} fragment フラグメント
	@returns {jsutil.URI} 構築した URI */
	fromComponents: function(scheme, authority, path, query, fragment) {
		return copy(jsutil.newEmptyInstance(jsutil.URI), {
			scheme: scheme, authority: authority, path: path,
			query: query, fragment: fragment
		});
	}
});
/** @lends jsutil.URI.prototype */ copy(jsutil.URI.prototype, {
	/** 絶対 URI かどうかを取得します.
	@returns {Boolean} 絶対 URI かどうか */
	isAbsolute: function() { return !!scheme; },
	/** 現在の URI をベースに URI 解決を行います.
	<p> 注:異状データは処理できません。</p>
	@param {jsutil.URI} uri 解決する URI */
	resolve: function(uri) {
		if(!(uri instanceof jsutil.URI))throw ArgError();
		/*/ 変換が不要なパターン
		[uri="" or "#fragment" or "scheme:..."] /*/
		if(!(uri.scheme || uri.authority || uri.path || uri.query) || uri.scheme)
			return uri;
		// 変換が必要なパターン
		var targetUri = jsutil.URI.fromComponents(
			this.scheme, uri.authority, uri.path, uri.query, uri.fragment
		);
		do {
			// uri="//authority..."
			if(uri.authority)
				break;
			targetUri.authority = this.authority;
			// uri="/path..."
			if(uri.path.charAt(0) === '/')
				break;
			// 基底URIのパスとURI参照のパス(相対)を結合
			targetUri.path = _mergePath(this.path, uri.path);
		} while(false)
		return targetUri;
	},
	/** URI 文字列に変換します.
	@returns {String} URI 文字列 */
	toString: function() {
		with(this) return [
			scheme!==null?(scheme+":"):"", authority!==null?("//"+authority):"",
			path, query!==null?("?"+query):"", fragment!==null?("#"+fragment):"",
		].join("");
	}
});
// URI の正規表現
var _uriRegExp = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
// パスの結合
function _mergePath(basePath, relativePath) {
	// スタックによる実装を使う
	var stack = basePath.split("/"), result=[];
	// ファイル部分を削除
	stack.pop();
	// 基底側のパスと相対側のパスの連結
	stack.push.apply(stack, relativePath.split("/"));
	// "."と".."を可能な限り除去
	while(stack.length > 0) {
		var segment = stack.shift();
		switch(segment) {
		case "..":
			result.pop();
		case ".":
			if(stack.length === 0)
				result.push("");
			break;
		default:
			result.push(segment);
		}
	}
	return result.join("/");
}
/** マップ
@class */
jsutil.Map = function() {
	if(arguments.length > 0) throw ArgError();
	var _this = $(this);
	// ハッシュによりget,setの高速化。
	_this.hash = {};
	// リストによる走査の提供。
	_this.list = new jsutil.List;
};
/** @lends jsutil.Map.prototype */ copy(jsutil.Map.prototype, {
	/** キーに対応する値を取得します.
	@param {Object} key キー
	@returns {Object} キーに対応する値*/
	get : function(key) {
		with(new _EntryList($(this).hash,key))
			return search()?get().value:undefined;
	},
	/** キーに対応する値を設定します.
	<p> 実装に {@link jsutil.getHashCode} を使用しているため、
	<code>key</code> の値には若干の制約があります。</p>
	<p> <code>value</code> が未指定の場合は <code>key</code> を使用します。</p>
	@param {Object} key キー
	@param {Object} [value] キーに対応する値 */
	set : function(key, value) { with($(this)) {
		var isNewEntry,entry;
		with(new _EntryList(hash,key)) {
			isNewEntry = make();
			entry = get();
			entry.value = (value!==undefined)?value:key;
		}
		// リストに対する処理
		if(!isNewEntry)return;
		$(entry).listEntry = list.getEnd().addPrev(entry);
	} },
	/** キーの設定を削除します.
	@key {Object} key キー */
	remove : function(key) { with($(this)) {
		var _entry;
		with(new _EntryList(hash,key)) {
			if(!search())return;
			_entry = $(get());
			remove();
		}
		// リストに対する処理
		_entry.listEntry.remove();
	} },
	/** 管理するキーと値のペアの数
	@returns {Integer} キーと値のペアの数 */
	getLength : function() {
		return $(this).list.getLength();
	},
	/** イテレータを取得します.
	<p> 一番最初の MapEntry を返すだけです。 </p>
	@returns {jsutil.MapEntry} イテレータ */
	iterator : function() {
		var listFirst = $(this).list.getFirst();
		return listFirst?listFirst.value:null;
	}
});
// エントリリストの管理用ヘルパ
function _EntryList(hash,key) {
	this.hash = hash;
	this.key = key;
	this.hashCode = jsutil.getHashCode(key);
	this.entryList = hash[this.hashCode];
	this.i=0;
}
copy(_EntryList.prototype, {
	search : function() { with(this) {
		if(entryList === undefined) return false;
		for(i=0;i<entryList.length;i++)
			if($(entryList[i]).key === key) return true;
		return false;
	} },
	make : function(value) { with(this) {
		if(entryList === undefined)
			entryList = hash[hashCode] = [];
		for(i=0;i<entryList.length;i++)
			if($(entryList[i]).key === key) return false;
		entryList.push(new jsutil.MapEntry(key,null));
		return true;
	} },
	remove : function() { with(this) {
		entryList.splice(i,1);
		if(entryList.length === 0)
			delete hash[hashCode];
	} },
	get : function() { with(this) {
		return entryList[i];
	} }
});
/** マップのエントリ.
{@link jsutil.Map} のエントリです。
@class
@param {Object} key キー
@param {Object} value キーに対応する値
*/
jsutil.MapEntry = function(key,value) {
	var _this = $(this);
	/** キーに対応する値
	@type Object */
	this.value = value;
	_this.key = key;
	_this.listEntry = null;
};
copy(jsutil.MapEntry.prototype, {
	/** キーを取得します.
	@returns {Object} キー */
	getKey : function() { return $(this).key; },
	/** マップ中の次のエントリを取得します.
	@returns {jsutil.MapEntry} マップ中の次のエントリ */
	getNext : function() {
		var nextListEntry = $(this).listEntry.getNext();
		return nextListEntry?nextListEntry.value:null;
	}
});
/** リスト.
@class */
jsutil.List = function() {
	var _this = $(this);
	_this.start = new jsutil.ListEntry(null,this);
	_this.end = new jsutil.ListEntry(null,this);
	$(_this.start).next = _this.end;
	$(_this.end).prev = _this.start;
	_this.count = 0;
};
/** @lends jsutil.List.prototype */ copy(jsutil.List.prototype, {
	/** 含まれるエントリ数を取得します.
	@returns {Integer} 含まれるエントリ数 */
	getLength: function() { return $(this).count; },
	/** 最初のエントリを取得します.
	@returns {jsutil.ListEntry} 最初のエントリ */
	getFirst: function() { return $(this).start.getNext(); },
	/** 最後のエントリを取得します.
	@returns {jsutil.ListEntry} 最後のエントリ */
	getLast: function() { return $(this).end.getPrev(); },
	/** 開始位置を表すダミーエントリを取得します.
	@returns {jsutil.ListEntry} 開始位置を表すダミーエントリ */
	getStart: function() { return $(this).start; },
	/** 終了位置を表すダミーエントリを取得します.
	@returns {jsutil.ListEntry} 終了位置を表すダミーエントリ */
	getEnd: function() { return $(this).end; }
});
/** リストのエントリ
@class */
jsutil.ListEntry = function(value,list) {
	var _this = $(this);
	/** エントリに含まれる値
	@type {Object} */
	this.value = value;
	_this.prev = _this.next = null;
	_this.list = list;
};
copy(jsutil.ListEntry.prototype, {
	/** このエントリの一つ前のエントリを取得します.
	@returns {jsutil.Entry} 一つ前のエントリ */
	getPrev : function() { with($(this)) {
		return prev!==$(list).start ? prev : null;
	} },
	/** このエントリの一つ後のエントリを取得します.
	@returns {jsutil.Entry} 一つ後のエントリ */
	getNext : function() { with($(this)) {
		return next!==$(list).end ? next : null;
	} },
	/** このエントリの一つ後にエントリを追加します.
	@param {Object} 追加するエントリに格納する値
	@returns {jsutil.Entry} 追加したエントリ */
	addNext : function(value) { 
		if($(this).list.getEnd() === this)throw ArgError();
		return $(this).next.addPrev(value);
	},
	/** このエントリの一つ前にエントリを追加します.
	@param {Object} 追加するエントリに格納する値
	@returns {jsutil.Entry} 追加したエントリ */
	addPrev : function(value) { with($(this)) {
		if($(this).list.getStart() === this)throw ArgError();
		var entry = new jsutil.ListEntry(value,list);
		$(entry).prev = prev;
		$(entry).next = this;
		$(list).count++;
		return prev = $(prev).next = entry;
	} },
	/** このエントリをリストから取り除きます. */
	remove : function() { with($(this)) {
		if(!prev || !next)throw ArgError();
		$(prev).next = next;
		$(next).prev = prev;
		next = prev = null;
		$(list).count--;
	} }
});
/** 関数の集合.
@class */
jsutil.FuncSet = function() {
	var This = this;
	$(this).funcMap = new jsutil.Map;
	/** <code>this</code> や引数を指定できない場面での正常な呼び出しを行う関数.*/
	this.thisCall = function(){ This.exec.call(This,this,arguments); };
};
copy(jsutil.FuncSet.prototype, {
	/** キーに関数を設定します.
	<p> 関数が未指定の場合キー自体を関数として扱います。</p> 
	<p> <code>f</code> が未指定の場合は <code>key</code> を使用します。</p>
	@name set
	@methodOf jsutil.FuncSet#
	@param {Object} key キー
	@param {Function} [f] 関数 */
	set : function(key,f) {
		$(this).funcMap.set(key,f);
	},
	/** 指定されたキーのエントリを削除します.
	@name remove
	@methodOf jsutil.FuncSet#
	@param {Object} key キー */
	remove : function(key) {
		$(this).funcMap.remove(key);
	},
	/** エントリが空かどうかを取得します.
	@name empty
	@returns {Boolean} エントリが空かどうか */
	empty : function() {
		return $(this).funcMap.getLength()===0;
	},
	/** 全ての関数を実行します.
	@name exec
	@methodOf jsutil.FuncSet#
	@param {Object} [thisArg] 関数の呼び出しに使う <code>this</code> オブジェクト
	@param {Object[]} [argumentsArg] 関数の呼び出しに使う引数を表す配列 */
	exec : function(thisArg,argumentsArg) {
		// 実行中の新たな追加や削除に備え現在のリストをコピー
		var funcMap = $(this).funcMap;
		functions = [];
		for(var i=funcMap.iterator();i!==null;i=i.getNext())
			functions.push(i.value);
		// 実行
		this.preExec.apply(thisArg,argumentsArg);
		for(var i=0;i<functions.length;i++)
			functions[i].apply(thisArg,argumentsArg);
		this.afterExec.apply(thisArg,argumentsArg);
	},
	/** {@link #exec} の前に実行する関数です.
	@name preExec
	@methodOf jsutil.FuncSet#
	@function */
	preExec : nothingTodo,
	/** {@link #exec} の後に実行する関数です.
	@name afterExec
	@methodOf jsutil.FuncSet#
	@function */
	afterExec : nothingTodo 
});
/** 値やオブジェクトのハッシュ値を得ます.
<p> <code>target</code> はプリミティブ型または新しいプロパティを追加出来る
オブジェクトです。そのため、EcmaScript 5 th エディションで導入された 
<code>seal</code> や <code>freeze</code> の対象になったオブジェクトや、
IE で <code>new ActiveXObject</code> から作成したオブジェクトは使用できません。
@param {Object} target ハッシュ値を得る対象 */
jsutil.getHashCode = function(target) {
	var hashCode = target;
	// (target instanceof Object) は時間を食いすぎる。
	if(typeof target === "object" && target !== null) {
		var _target = $(target);
		hashCode = _target.hashCode = _target.hashCode || Math.random();
	}
	return typeof target + ":" + hashCode;
};
}();

/** ブラウザラッパー
@namespace */
var bw = {};
void function() {
	var $ = (new jsutil.Package).get;
	/** 環境情報
	@namespace */
	bw.env = {};
	/** DOM Level 2 完全準拠の場合のみ真
	@name dom
	@fieldOf bw.env */
	/** Gecko の場合のみ真
	@name gk
	@fieldOf bw.env */
	/** Opera の場合のみ真
	@name op
	@fieldOf bw.env */
	/** IE の場合のみ真
	@name ie
	@fieldOf bw.env */
	/** WebKit の場合のみ真
	@name wk
	@fieldOf bw.env */
	function _setDOMInfo() {
		var dom2 = ["Core","XML","HTML","Views","StyleSheets","CSS","CSS2","Events",
			"UIEvents","MouseEvents","MutationEvents","HTMLEvents","Range","Traversal"];
		for(var i=0,I=dom2.length;i<I;i++)
		if(!document.implementation.hasFeature(dom2[i], "2.0"))
			return;
		bw.env.dom = true;
	}
	function _setGeckoInfo() {
		bw.env.gk = false;
		// 大昔のGeckoで使われていたマイルストーン形式は古すぎるので考慮しない。
		var productToken = navigator.userAgent.match(/Gecko\/(\d{8})/);
		var releaseVersion = navigator.userAgent.match(/rv:([^;,\s\(\)]*)/);
		if(productToken === null || releaseVersion === null) return;
		bw.env.gk = true;
		bw.env.buildId = productToken[1];
		bw.env.cmpVer = (new BetaVerComparator(releaseVersion[1])).compare;
	}
	function _setOperaInfo() {
		bw.env.op = false;
		if(window.opera === undefined || window.opera.version === undefined) return;
		bw.env.op = true;
		bw.env.cmpVer = (new VerComparator(window.opera.version())).compare;
	}
	function _setIEInfo() {
		bw.env.ie = false;
		var productToken = navigator.userAgent.match(/MSIE (\d(?:\.\d+)*)/);
		if(productToken === null) return;
		/*@cc_on bw.env.ie = true;
		bw.env.cmpVer = (new VerComparator(productToken[1])).compare;
		bw.env.css1 = !(document.compatMode === "BackCompat"); @*/
	}
	function _setWebKitInfo() {
		bw.env.wk = false;
		var productToken = navigator.userAgent.match(/AppleWebKit\/(\d(?:\.\d+)*)/);
		if(productToken === null) return;
		bw.env.wk = true;
		bw.env.cmpVer = (new VerComparator(productToken[1]));
	}
	/** バージョン文字列の比較用です.
	@class
	@param {String} baseVer ベースとなるバージョン文字列 */
	bw.VerComparator = VerComparator;
	function VerComparator(baseVer) {
		var This = this;
		/** バージョンを比較します.
		<p> バージョン文字列はドット区切りで行われ、各サブバージョンの
		比較は {@link bw.VerComparator#compareSub} で行われます。</p>
		@name compare
		@param {String} compVer 比較するバージョン
		@memberOf bw.VerComparator
		@returns {Boolean} ベースと同じならば 0、大きければ正の値、小さければ負の値。 */
		this.compare = function(compVer) {
			var baseSubs = baseVer.split('.'), compSubs = compVer.split('.');
			for(var i=0;i<Math.min(baseSubs.length,compSubs.length);i++) {
				var result = This.compareSub(i,baseSubs[i],compSubs[i]);
				if(result !== 0)return result;
			}
			return baseSubs.length - compSubs.length;
		};
	}
	/** サブバージョンを比較します.
	<p>デフォルトの実装には以下の規則があります。</p>
	<ul>
	<li>比較先が未知のバージョンの場合はエラー</li>
	<li>ベースが未知のバージョンの場合はそちらが大きい</li>
	<li>メジャー番号は整数としてマイナー番号は先頭に "0." を付けた小数として比較 </li>
	</ul>
	@name bw.VerComparator.compareSub
	@memberOf bw.VerComparator#
	@function
	@param {String} compVer 比較するバージョン
	@returns {Boolean} ベースと同じならば 0、大きければ正の値、小さければ負の値。
	@throws 比較先が未知のバージョンの場合 */
	VerComparator.prototype.compareSub = function(index,baseVer,compVer) {
		if(!/^\d*$/.test(compVer)) throw jsutil.ArgError(); 
		if(!/^\d*$/.test(baseVer)) return +1; 
		return (index===0?compAsInt:compAsFloat)(baseVer,compVer);
	};
	/** ベータバージョンの可能性のあるバージョン文字列を比較します.
	@augments bw.VerComparator
	@class
	@param {String} baseVer ベースとなるバージョン文字列 */
	bw.BetaVerComparator = BetaVerComparator;
	jsutil.chainPrototype(BetaVerComparator,VerComparator);
	function BetaVerComparator(baseVer) {
		VerComparator.call(this,baseVer);
	}
	/** サブバージョンを比較します.
	基底クラスの比較方法で同じ場合、最後に "b" が付いた側が小さくなります。
	@name bw.BetaVerComparator.compareSub
	@memberOf bw.BetaVerComparator#
	@function
	@param {String} compVer 比較するバージョン
	@returns {Boolean} ベースと同じならば 0、大きければ正の値、小さければ負の値。
	@throws 比較先が未知のバージョンの場合 */
	BetaVerComparator.prototype.compareSub = function(context,baseVer,compVer) {
		var baseParam = baseVer.match(/^(\d*)(b?)$/);
		var compParam = compVer.match(/^(\d*)(b?)$/);
		if(compParam === null) throw jsutil.ArgError();
		if(baseParam === null) return +1;
		return VerComparator.prototype.compareSub(context,baseParam[1],compParam[1]) ||
			verPostfixCompare(baseParam[2],compParam[2]);
	};
	function compAsInt(baseVer,compVer) { return parseInt(baseVer)-parseInt(compVer); };
	function compAsFloat(baseVer,compVer) { return parseFloat("0."+baseVer)-parseFloat("0."+compVer); };
	function verPostfixCompare(baseVer,compVer) { return compVer.length - baseVer.length; }
	// 判別と環境情報の設定(確実な順から設定)
	bw.env.unknown = !(_setOperaInfo(),bw.env.op
		|| _setIEInfo(),bw.env.ie
		|| _setGeckoInfo(),bw.env.gk
		|| _setDOMInfo(),bw.env.dom);

	// << ブラウザ依存部吸収用メソッド等 >> //
	// 文書の表示オブジェクトを取得
	bw.defaultView = !bw.env.ie?
		function(document) {return document.defaultView;}:
		function(document) {return document.parentWindow;};
	// OBJECT要素内の文書を取得
	bw.contentDocument =!bw.env.ie?
		function(obj) {return obj.contentDocument;}:
		function(obj) {return obj.object;};
	// 関連するオブジェクトを削除する。
	bw.deleteRelatedObject = function(htmlElement) {
		// SCRIPT要素でFOR属性を持つ場合
		var scripts = document.getElementsByTagName("SCRIPT");
		for(var i=0;i<scripts.length;i++) {
			var script = scripts[i];
			if(script.htmlFor === htmlElement.id)
				script.parentNode.removeChild(script);
		}
	};
	/*/ 最終的なスタイルを取得。
	返値の暗黙的なプロパティに短縮名でアクセスする事でスタイルを取得可能。
	DOMでは返値のgetPropertyValueメソッドにスタイルの完全名を渡しても良い。
	注意:borderWidth等はgeckoでは計算されたピクセルに、IEではmidium等の値。*/
	bw.getComputedStyle = !bw.env.ie?
		function(element) {return document.defaultView.getComputedStyle(element,null);}:
		function(element) {return element.currentStyle;};
	// テキストの選択を解除する。
	bw.selectionRemove
		= _isDefineRmoveAllRanges()? function(){window.getSelection().removeAllRanges();}
		: _isDefineSelectionEmpty()? function(){document.selection.empty();}
		: function(){}; // Opera8は未対応
	function _isDefineRmoveAllRanges() { // gecko等
		return window.getSelection !== undefined
			&& window.getSelection().removeAllRanges !== undefined;
	};
	function _isDefineSelectionEmpty() { // IE等
		return document.selection !== undefined
			&& document.selection.empty !== undefined
	};
	// ブラウザ固有のドラッグ動作を抑止
	bw.preventDrag = !bw.env.ie?function(element) {
			bw.addEventListener(element,"mousedown",function(event){event.preventDefault();});
		}:function(element) {
			bw.addEventListener(element,"drag",function(){window.event.returnValue = false;});
		};
	// イベントオブジェクトの生成
	bw.createEvent = function(eventType) {
		var event = !bw.env.ie?
			document.createEvent(eventType):
			document.createEventObject();
		switch(eventType) {
		case "MouseEvents": return new bw.MouseEvent(event);
		default: return new bw.Event(event);
		}
	};
	// イベントオブジェクトの発火
	bw.dispatchEvent = !bw.env.ie?
		function(htmlElement,event) {
			var event = $(event).event;
			return htmlElement.dispatchEvent(event);
		}:
		function(htmlElement,event) {
			event.preDispatch(htmlElement);
			var event = $(event).event;
			return htmlElement.fireEvent("on"+event.type,event);
		};
	// イベントの取得
	bw.getEvent = function(event) {
		var _event = event || window.event;
		switch(_event.type) {
			case "click": case "mousedown": case "mouseup":
			case "mouseover": case "mousemove": case "mouseout":
				return new bw.MouseEvent(_event);
			default:
				return new bw.Event(_event);
		}
	};
	// 基本イベント
	bw.Event = function(event) {
		$(this).event = event;
	};
	copy(bw.Event.prototype, {
		getType : function() { return $(this).event.type; },
		getTarget : function() { return $(this).event.target; },
		getCurrentTarget : function() { return $(this).event.currentTarget; },
		stopPropagation : function() { $(this).event.stopPropagation(); },
		preventDefault : function() { $(this).event.preventDefault(); },
		// バブルやキャンセル可能性については変更不可能なIEに合わせる。
		initEvent : function(type) {
			with(_eventDefaults[type])
				$(this).event.initEvent(type,canBubble,cancelable);
		}
	});
	if(bw.env.ie) copy(bw.Event.prototype, {
		getTarget : function() { return $(this).event.srcElement; },
		stopPropagation : function() { $(this).event.cancelBubble = true; },
		preventDefault : function() { $(this).event.returnValue = false; },
		// 発火時の参照用:type等はfireEventで無視される。
		// preDispatchはマウスイベントで発火前の前処理用。
		initEvent : function(type) { 
			$(this).event.type = type;
			this.preDispatch = function(htmlElement) { delete this.preDispatch; };
		}
	});
	// マウスイベント
	bw.MouseEvent = function(event) {
		bw.Event.call(this,event);
	};
	jsutil.chainPrototype(bw.MouseEvent, bw.Event);
	copy(bw.MouseEvent.prototype, {
		// relatedTargetはAREA要素付近の挙動が統一出来ていないので注意！
		getScreenX : function() { return $(this).event.screenX; },
		getScreenY : function() { return $(this).event.screenY; },
		getClientX : function() { return $(this).event.clientX; },
		getClientY : function() { return $(this).event.clientY; },
		getCtrlKey : function() { return $(this).event.ctrlKey; },
		getShiftKey : function() { return $(this).event.shiftKey; },
		getAltKey : function() { return $(this).event.altKey; },
		getRelatedTarget : function() { return $(this).event.relatedTarget; },
		getButton : function() { return $(this).event.button; },
		// バブルやキャンセル可能性については変更不可能なIEに合わせる。
		// view,metaKeyはIEに該当するプロパティが存在しません。
		initMouseEvent : function(type,detail,screenX,screenY,clientX,clientY,
				ctrlKey,altKey,shiftKey,button,relatedTarget) {
			$(this).event.initMouseEvent(
				type,_eventDefaults[type].canBubble,_eventDefaults[type].cancelable,
				window,detail,screenX,screenY,clientX,clientY,
				ctrlKey,altKey,shiftKey,false,button,relatedTarget);
		}
	});
	if(bw.env.ie) copy(bw.MouseEvent.prototype, {
		getRelatedTarget : function() {
			switch($(this).event.type) {
			case "mouseover":return $(this).event.fromElement;
			case "mouseout":return $(this).event.toElement;
			default:return null;
			}
		},
		getButton : function() {
			switch($(this).event.type) {
			case "click":case "mousedown":case "mouseup":
			if($(this).event.button & 0x2)return 2;// 右
			if($(this).event.button & 0x1)return 0;// 左
			if($(this).event.button & 0x4)return 1;// 中央
			default:return new Number;
			}
		},
		initMouseEvent : function(
				type,detail,screenX,screenY,clientX,clientY,
				ctrlKey,altKey,shiftKey,button,relatedTarget) {
			var event = $(this).event;
			event.type = type; // 発火時の参照用
			event.screenX = screenX;
			event.screenY = screenY;
			event.clientX = clientX;
			event.clientY = clientY;
			event.ctrlKey = ctrlKey;
			event.altKey = altKey;
			event.shiftKey = shiftKey;
			event.button = (button===0)?1:(button===1)?4:(button===2)?2:new Number;
			event.fromElement = event.toElement = null;
			// fromElementとtoElementの設定(逆方向はdispatchEventで対象が提供されるまで保留)
			if(type === "mouseover")event.fromElement = relatedTarget;
			if(type === "mouseout")event.toElement = relatedTarget;
			this.preDispatch = function(htmlElement) {
				delete this.preDispatch;
				if(type === "mouseover")event.toElement = htmlElement;
				if(type === "mouseout")event.fromElement = htmlElement;
			};
		}
	});
	// IEでイベントリスナの撤去用
	var _toRemove;
	if(bw.env.ie && bw.env.cmpVer("7.0") < 0) {
		_toRemove = new jsutil.Map;
		window.attachEvent("onunload",_cleanupAll);
	}
	// イベントリスナの登録
	bw.addEventListener = 
		// [IE Bug KB890900]対策
		(bw.env.ie && bw.env.cmpVer("7.0") < 0) ? function(element,eventName,listener) {
			var isFirst = _addEventForIE(element,eventName,listener);
			if(!isFirst) return;
			if(element !== window) _toRemove.set(element.uniqueID, element);
		}:
		// IE7以上
		bw.env.ie ? function(element,eventName,listener) {
			_addEventForIE(element,eventName,listener);
		}:
		// [Bugzilla Bug 174320],[Bugzilla Bug 241518]対策
		(bw.env.gk && bw.env.cmpVer("1.8.1") < 0) ? function(element,eventName,listener,capture) {
			var funcSet = _reserveFuncSet(element,eventName);
			if(funcSet.empty())
				element.addEventListener(eventName,_listenerForGeckoWithBug,capture);
			funcSet.set(listener);
		}:
		// DOM標準
		function(element,eventName,listener,capture) {
			element.addEventListener(eventName,listener,capture);
		};
	// イベントリスナの削除
	bw.removeEventListener = 
		(bw.env.ie && bw.env.cmpVer("7.0") < 0) ? function(element,eventName,listener) {
			var isLast = _removeEventForIE(element,eventName,listener);
			if(!isLast)return;
			if(element !== window) _toRemove.remove(element.uniqueID);
		}: 
		bw.env.ie ? function(element,eventName,listener) {
			_removeEventForIE(element,eventName,listener);
		}:
		(bw.env.gk && bw.env.cmpVer("1.8.1") < 0) ? function(element,eventName,listener,capture) {
			var funcSet = _getFuncSet(element,eventName);
			if(funcSet === undefined)return;
			funcSet.remove(listener);
			if(funcSet.empty())
				element.removeEventListener(eventName,_listenerForGeckoWithBug,capture);
		}:
		function(element,eventName,listener,capture) {
			element.removeEventListener(eventName,listener,capture);
		};
	function _reserveFuncSet(element,eventName,capture) {
		var _element = $(element);
		jsutil.reserve(_element, "bubbles", {});
		jsutil.reserve(_element, "captures", {});
		var listeners = capture?_element.captures:_element.bubbles;
		return jsutil.reserveDo(listeners, eventName, function(){ return new jsutil.FuncSet; });
	}
	function _getFuncSet(element,eventName,capture) {
		var _element = $(element);
		var listeners = capture?_element.captures:_element.bubbles;
		if(!listeners) return undefined;
		return listeners[eventName];
	}
	function _listenerForGeckoWithBug(event) {
		with(event) {
			var funcSet = _getFuncSet(currentTarget,type);
			funcSet.exec(currentTarget,arguments);
		}
	}
	function _addEventForIE(element,eventName,listener) {
		var funcSet = _reserveFuncSet(element,eventName);
		var isEmpty = funcSet.empty();
		if(isEmpty) {
			element.attachEvent("on" + eventName,funcSet.thisCall);
			/*/ イベントオブジェクトの共有方針を利用
			引数として渡されるイベント情報は同一オブジェクト上のイベントリスナでは同じになる。
			(何故か window.event から得た場合はそうならない。) */
			funcSet.preExec = function(event) { event.currentTarget = element; };
		}
		funcSet.set(listener);
		return isEmpty;
	}
	function _removeEventForIE(element,eventName,listener) {
		var funcSet = _getFuncSet(element,eventName);
		if(funcSet == undefined)return false;
		funcSet.remove(listener);
		var isLast = funcSet.empty();
		if(isLast)
			element.detachEvent("on"+eventName,funcSet.thisCall);
		return isLast;
	}
	function _cleanupAll() {
		bw.cleanup(window);
		for(var i=_toRemove.iterator();i!==null;i=i.getNext())
			bw.cleanup(i.value);
	}
	// 要素のクリーンアップ
	bw.cleanup = function(element) {
		var _element = $(element);
		var eventNames = [];
		// 登録されているイベント名の記録
		if(_element.bubbles === null)return;
		for(var eventName in _element.bubbles)
		if(_element.bubbles.hasOwnProperty(eventName))
			eventNames.push(eventName);
		for(var i=0;i<eventNames.length;i++) {
			var funcSet = _getFuncSet(element,eventNames[i]);
			if(funcSet === undefined) continue;
			element.detachEvent("on"+eventNames[i],funcSet.thisCall);
			_element.bubbles[eventNames[i]] = null;
		}
	};
	// マウスイベントをウィンドウ外で追跡
	bw.setCapture = (document.documentElement.setCapture !== undefined)? // IE
		function() { document.documentElement.setCapture(); }:bw.env.gk? // gecko系
		function() { 
			bw.addEventListener(document.documentElement,"mousemove",_mousemove,true);
			bw.addEventListener(document.documentElement,"mouseout",_mouseout,true);
		}:
		function(){}; // Opera未対応
	bw.releaseCapture = (document.documentElement.releaseCapture !== undefined)? // IE
		function() { document.documentElement.releaseCapture(); }:bw.env.gk? // gecko系
		function() {
			bw.removeEventListener(document.documentElement,"mousemove",_mousemove,true);
			bw.removeEventListener(document.documentElement,"mouseout",_mouseout,true);
		}:
		function(){};// Opera未対応
	// HTML5
	bw.getElementsByClassName = function(element, className) {
		var results = [];
		var elements = element.getElementsByTagName("*");
		for(var i=0,I=elements.length;i<I;i++)
		if(wu.hasClass(elements[i],className))
			results.push(elements[i]);
		return results;
	};
	// AJAX
	bw.newXMLHttpRequest = window.XMLHttpRequest? function(){ 
			return new XMLHttpRequest(); 
		}: window.ActiveXObject? function() {
			var xObjects = ["Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
			for(var i=0;i<xObjects.length;i++) try {
				return new ActiveXObject(xObjects[i]);
			} catch(e) { }
			return null;
		}: function() {return null};
//	bw.XMLHTTPRequest = function() {
//		var This = this;
//		$(This).original = _getOriginalXMLHttpRequest();
//		original.onreadystatechange = function() {
//			This.readystate = $(This).original.readyState;
//			This.status = $(This).original.status;
//			This.responseText = $(This).original.responseText;
//			This.responseXML = $(This).original.responseXML;
//			This.onreadystatechange();
//		};
//	};
//	copy(bw.XMLHTTPRequest.prototype, {
//		open: function(method, url, async) { $(this).original.open(method, url, async); },
//		send: function(data) { $(this).original.send(data); }
//		onreadystatechange: function() {}
//	});
//	var _getOriginalXMLHttpRequest = window.XMLHttpRequest?function() {
//		return new XMLHttpRequest(); 
//	}: window.ActiveXObject? function() {
//		try { 
//			return new ActiveXObject("Msxml2.XMLHTTP");
//		} catch(e) {
//			return null;
//		}
//	}: function() {
//		return null;
//	}
	// XML
	bw.importNode = !bw.env.ie?
		function(docTo, node, deep) { return docTo.importNode(node, deep); }:
		function(docTo, node, deep) {
			var docFrom = node.ownerDocument;
			var toHTML = _isHTML(docTo), fromHTML = _isHTML(docFrom);
			return (!toHTML && !fromHTML)?
				node.cloneNode(deep):_convertDoc(docTo, node, deep);
		};
	// private //
	var _lastMouseEvent = null;
	function _mousemove(event) {
		_lastMouseEvent = event;
	}
	function _mouseout(event) {
		if(event.relatedTarget !== null)return;
		var dumyMouseUpEvent = document.createEvent('MouseEvents');
		dumyMouseUpEvent.initMouseEvent('mouseup',
			// bubles,cancelable,view,detail
			true, true, window, 1, 
			_lastMouseEvent.screenX, // screenX
			_lastMouseEvent.screenY, // screenY
			_lastMouseEvent.clientX, // clientX
			_lastMouseEvent.clientY, // clientY
			// ctrlKey,altKey,shiftKey,metaKey,button,relatedTarget
			false, false, false, false, 0, null);
		 document.documentElement.dispatchEvent(dumyMouseUpEvent);
	}
	// 各イベントのデフォルトパラメタ
	// IEとDOMで挙動が違う物、abort,error,select,change,submit,reset,scrollには未対応
	var _eventDefaults = { 
		"click":new _EventDefault(true,true),
		"mousedown":new _EventDefault(true,true),
		"mouseup":new _EventDefault(true,true),
		"mouseover":new _EventDefault(true,true),
		"mousemove":new _EventDefault(true,false),
		"mouseout":new _EventDefault(true,true),
		"load":new _EventDefault(false,false),
		"unload":new _EventDefault(false,false),
		"focus":new _EventDefault(false,false),
		"blur":new _EventDefault(false,false),
		"resize":new _EventDefault(false,false)
	};
	function _EventDefault(canBubble,cancelable) {
		this.canBubble = canBubble;
		this.cancelable = cancelable;
	}
	function _convertDoc(docTo, node, deep) {
		switch(node.nodeType) {
		case 1: // ELEMENT_NODE
			if(_isHTML(docTo)) {
				var temp = docTo.createElement("div");
				var copyNode = deep?node:node.cloneNode(false);
				temp.innerHTML = copyNode.xml || copyNode.outerHTML;
				return temp.removeChild(temp.firstChild);
			} else {
				var newNode = docTo.createElement(node.tagName);
				var attributes = node.attributes;
				for (var i=0,I=attributes.length; i < I; i++)
					if (attributes[i].specified)
						newNode.setAttributeNode(arguments.callee(docTo, attributes[i], true));
				if(deep) importChildNodes(newNode, node);
				return newNode;
			}
		case 2: // ATTRIBUTE_NODE
			var newNode = docTo.createAttribute (node.name);
			newNode.value = node.value;
			return newNode;
		case 3: // TEXT_NODE
			return docTo.createTextNode(node.data);
		case 4: // CDATA_SECTION_NODE
			return docTo.createCDATASection(node.data);
		case 5: // ENTITY_REFERENCE_NODE
			return docTo.createEntityReference(node.nodeName);
		case 8: // COMMENT_NODE
			return docTo.createComment(node.data);
		case 11: // DOCUMENT_FRAGMENT_NODE
			var newNode = docTo.createDocumentFragment();
			if(deep) importChildNodes(newNode, node);
			return newNode;
		default: throw Error("not supported");
		}
		function importChildNodes(newNode, node) {
			var childNodes = node.childNodes;
			for (var i=0,I=childNodes.length;i<I;i++)
				newNode.appendChild(convertDoc(docTo, childNodes[i], true));
		}
	}
	function _isHTML(doc) {
		return doc.body !== undefined;
	}
}();

// << Web Utility >> //
var wu = {};
void function() {
	var $ = (new jsutil.Package).get;
	// CSSのクラスを検索、追加または除去します。
	wu.addClass = function(element, className) {
		wu.removeClass(element,className);
		element.className += (getSeparater() + className);
		function getSeparater() {
			if(/\S/.test(element.className))
				return " ";
			else
				return "";
		}
	};
	wu.removeClass = function(element, className) {
		// クラス名は空白系文字で区切るが、使用可能な文字は規定されていない。
		var regExp = new RegExp("\\s*\\b" + className + "\\b\\s*");
		var matchStr = element.className.match(regExp);
		element.className = element.className.replace(regExp,getSeparater());
		function getSeparater() {
			if(matchStr === null) return "";
			if(element.className.indexOf(matchStr) === 0
			|| element.className.lastIndexOf(matchStr) === 0)
				return "";
			else
				return " ";
		}
	};
	wu.hasClass = function(element, className) {
		// クラス名は空白系文字で区切るが、使用可能な文字は規定されていない。
		var regExp = new RegExp("\\s*\\b" + className + "\\b\\s*");
		return regExp.test(element.className);
	};

	/*/ [begin] 実行中のスクリプトを利用した機能
	script 要素により呼び出されるスクリプトのための機能。各種機能の使用には
	script 要素が他の要素を追加する前に setCurrentScript の呼び出しが必須。*/
	var _currentScript = null;
	// 巨大なスクリプトが残る場合に備えメモリの解放
	bw.addEventListener(window, "load", function() {
		_currentScript = null; 
	});
	// コンテキストの設定
	wu.setCurrentScript = function() {
		var scripts = document.getElementsByTagName("script");
		if(scripts.length < 1) throw Error("scripts.length < 1");
		_currentScript = scripts[scripts.length-1];
	};
	// スクリプトの位置を取得
	wu.getSrc = function() {
		if(_currentScript === null) throw Error("_currentScript === null");
		return _currentScript.src;
	};
	// XHTML モードで使用できない document.write の代用
	wu.writeNode = function(node) {
		if(_currentScript === null) throw Error("_currentScript === null");
		_currentScript.parentNode.appendChild(node);
	};
	/// [end] 実行中のスクリプトを利用した機能

	/*/ ライブラリのルートディレクトリを取得 
	このスクリプト自身は必ずライブラリのルートディレクトリに
	置くため、他のライブラリはそこからの相対パスになります。*/
	wu.setCurrentScript();
	var _librarys = {};
	var _rootDirectory = jsutil.URI.fromString(
		wu.getSrc()).resolve(jsutil.URI.fromString("."));
	// リソースの位置を取得
	wu.getResourceURI = function(uri) {
		return _rootDirectory.resolve(jsutil.URI.fromString(uri));
	};
	// ライブラリの準備
	wu.prepare = function(names, loadedAction) {
		if(!(names instanceof Array)) throw jsutil.ArgError();
		for(var i=0,I=names.length;i<I;i++) {
			var library = wu.getLibrary(names[i]);
			library.addObserver(processLoading);
			library.load();
			processLoading();
		}
		function processLoading() {
			if(!checkAllLoaded()) return;
			for(var i=0,I=names.length;i<I;i++)
				wu.getLibrary(names[i]).removeObserver(processLoading);
			loadedAction();
		}
		function checkAllLoaded() {
			for(var i=0,I=names.length;i<I;i++)
			if(wu.getLibrary(names[i]).getState() !== _Library.LOADED)
				return false;
			return true;
		}
	};
	// ライブラリの取得
	wu.getLibrary = function(name) {
		var library = _librarys[name];
		if(library === undefined)
			_librarys[name] = new _Library(name);
		return _librarys[name];
	};
	/*/ ライブラリ本体
	ライブラリの管理を行います。ロード用のメソッドがあり、これを
	使ってライブラリのロードを行えます。ただし、他のロード方法も
	考えられるため、ロード結果通知メソッドはロードメソッド本体から
	分離しています。*/
	function _Library(name) {
		this.unloadAction = nothingTodo;
		$(this).name = name;
		$(this).state = _Library.EMPTY;
		$(this).observers = new jsutil.FuncSet;
		$(this).referenceCount = 0;
	}
	copy(_Library, {
		EMPTY:0, LOADING:1, LOADED:2
	});
	copy(_Library.prototype, {
		// 状態取得
		getState:function() {
			return $(this).state;
		},
		// ロード実行
		load:function() {
			var This = this;
			$(this).referenceCount++;
			if($(this).state > _Library.EMPTY)
				return;
			var xhr = bw.newXMLHttpRequest();
			alert(wu.getResourceURI($(this).name));
			xhr.open("GET", wu.getResourceURI($(this).name), true);
			xhr.send(null);
			xhr.onreadystatechange = function() {
				if(xhr.readyState === 4 && xhr.status === 200) {
					Function(xhr.responseText)();
					$(This).state = _Library.LOADED;
					This.observe();
				}
			};
		},
		// アンロード実行
		unload:function() {
			if($(this).referenceCount===1)
				this.unloadAction();
			$(this).referenceCount = Math.min($(this).referenceCount-1, 0);
		},
		// オブザーバ登録
		addObserver:function(observer) {
			$(this).observers.set(observer);
		},
		// オブザーバ削除
		removeObserver:function(observer) {
			$(this).observers.remove(observer);
		},
		// ロード結果通知
		observe:function() {
			$(this).observers.exec(this);
		}
	});
}();

