/*-----------------------------------------------------------------------
 * 共通関数
 *
 * DEPENDENCIES
 *  - 
 *
 * @version $Revision$
 *-----------------------------------------------------------------------
 */

/**
 * BAcommonのコンストラクタ
 */
function BAcommon() {
	this.env  = {};		// 環境変数
	this.ns   = {};		// 名前空間
	this.prfx = {};		// 属性情報
	this.geom = {};		// 座標
	this.env.ua       = navigator.userAgent; 		// エージェント
	this.env.isMac    = this.env.ua.match(/Mac/); 	// Mac判定
	this.env.isWin    = this.env.ua.match(/Win/); 	// Windows判定
	this.env.isNN     = document.layers;			// Netscape判定
	this.env.isMoz    = this.env.ua.match(/Gecko\//);	// Mozilla判定
	this.env.isSafari = this.env.ua.match(/AppleWebKit/);	// Safari判定
	this.env.isOpera  = window.opera;	// Operaの判定
	this.env.isIE     = (document.all && !this.env.isOpera); // IEの判定
	this.env.DOMok    = (document.documentElement && document.getElementsByTagName); // DOMが使用可能か判定
	this.ns.rootNS    = (this.env.DOMok) ? document.documentElement.namespaceURI : null; // ドキュメントの名前空間
	this.ns.xhtml1    = 'http://www.w3.org/1999/xhtml'; // 名前空間(xhtml1)
	this.ns.bAattrs   = 'urn:bA.attrs'; // urnの設定
	this.prfx.bAattrs = 'bAattrs:'; // 属性
	this.sharedDir    = '/public/'; // ディレクトリ
	this.showErrMsg   = false ; // エラーメッセージ表示設定 true:表示 false:非表示
	window.onerror    = this.errorHandler; // エラーハンドラー
}

/* -- BAcommon関数 -- */
BAcommon.prototype = {

	/**
	 * 引数baseNode内にある引数tagNameと同じタグの要素一覧を取得
	 * 
	 * @param tagName タグ名
	 * @param baseNode 親要素
	 * @return 引数baseNode内にある引数tagNameと同じタグの要素一覧
	 */
	getElementsByTagName : function(tagName, baseNode) {

		// 引数にタグ名が存在しない場合、タグ名を"*"にする
		if (!tagName) tagName = '*';

		// 引数のタグ名が"body"(大文字も可)の場合かつbodyタグが存在する場合、要素"body"を返す
		if (tagName.toLowerCase() == 'body' && document.body) return [document.body];  // measure for Netscape7.1

		// 親要素が存在しないまたは要素でない場合、documentを親要素とする
		if (!baseNode || !baseNode.nodeType || baseNode.nodeType != 1) baseNode = document;

		// 親要素の子要素が取得できない場合はnullを返す
		// 親要素の子要素が存在する場合かつ名前空間が存在する場合、タグ名と名前空間に紐付く子要素の一覧を返す
		// 親要素の子要素が存在する場合かつ名前空間が存在しない場合、タグ名に紐付く子要素の一覧を返す
		return (!baseNode.getElementsByTagName) ?
			null :
			(BA.ns.rootNS) ? baseNode.getElementsByTagNameNS(env.ns.xhtml1, tagName) :
			                 baseNode.getElementsByTagName(tagName);
	},

	/**
	 * 引数classNameをclass属性に持つ要素一覧を取得
	 * 
	 * @param className class属性の値
	 * @param tagName タグ名
	 * @param baseNode 親要素
	 * @return 引数classNameをclass属性に持つ要素一覧
	 */
	getElementsByClassName : function(className, tagName, baseNode) {

		// 引数classNameが存在しない場合、NULLを返す
		if (!className) return null;

		// 戻り値の配列を宣言
		var ret  = [];

		// タグ名の要素一覧を取得
		var objs = BA.getElementsByTagName(tagName, baseNode);

		// 要素の数だけ繰り返す
		for (var i = 0; i < objs.length; i++) {

			// 要素にクラス名が存在しない場合、次の要素へ
			if (!objs[i].className) continue;

			// クラス名を半角スペースで分割
			var classes = objs[i].className.split(' ');

			// 分割したクラス名の数だけ繰り返す
			for (var j = 0; j < classes.length; j++)

				// 要素のクラス名が引数のクラス名と一致した場合、戻り値の配列に要素を格納
				if (classes[j] == className) { ret[ret.length] = objs[i]; break; }
		}

		// 戻り値を返す
		return ret;
	},

	/**
	 * 要素を作成
	 * 
	 * @param tagName タグ名
	 * @return タグ名の空要素
	 */
	createElement : function(tagName) {
		// タグ名の要素を作成し返す
		// 名前空間が存在する場合、env.ns.xhtml1のタグ名の空要素を作成
		// 名前空間が存在しない場合、タグ名の空要素を作成
		return (BA.ns.rootNS) ?
			document.createElementNS(env.ns.xhtml1, tagName) :
			document.createElement(tagName);
	},

	/**
	 * 要素にテキストを追加
	 * 
	 * @param str テキスト
 	 * @param node 要素
	 * @return テキストを追加した要素
	 */
	createText : function(str, node) {

		// 引数が存在しない、引数nodeが要素でない場合は処理終了
		if (!str || !node || node.nodeType != 1) return;

		// MacIE5.0の場合
		if (BA.env.isMac && BA.env.isIE && BA.env.ua.match(/MSIE 5.0/))
			node.innerHTML += str;

		// それ以外の場合
		else
			// 要素に子要素(テキストノード)を追加する
			node.appendChild(document.createTextNode(str));
		return node;
	},

	/**
	 * 引数nodeの属性attrの値を取得
	 * 
 	 * @param node 要素
	 * @param attr 属性
	 * @return 属性の値
	 */
	getAttr : function(node, attr) {

	 	// 引数が存在しない、、引数nodeが要素でない場合はNULLを返す
		if (!node || !attr || node.nodeType != 1) return null;

		// IEの場合、"class"を"className"に変換
		attr += (document.all && attr == 'class') ? 'Name' : '';

		// 引数nodeの属性attrの値を取得
		var ret = node.getAttribute(attr);

		// 名前空間が指定されている場合
		if (!ret && node.getAttributeNS && attr.match(/:/)) {
			var prfx = attr.split(':')[0];
			var attr = attr.split(':')[1];

			// 名前空間の属性を返す
			return node.getAttributeNS(BA.ns[prfx], attr)
		} else

			// 属性を返す
			return ret;
	},

	/**
	 * 引数nodeの属性attrに引数valueを設定
	 * 
 	 * @param node 要素
	 * @param attr 属性
	 * @param value 値
	 */
	setAttr : function(node, attr, value) {

		// 引数が存在しない場合
		if (!node || !attr) return;

		// 属性に名前空間の指定がある場合
		if (attr.match(/:/)) {
			var prfx = attr.split(':')[0];
			var attr = attr.split(':')[1];

			// Safariの場合
			if (node.setAttributeNS && node.namespaceURI || BA.env.isSafari)

				// 値を設定
				node.setAttributeNS(BA.ns[prfx], attr, value);
			else {

				// 値を設定
				node.setAttribute('xmlns:' + prfx, BA.ns[prfx]);
				node.setAttribute(prfx + ':' + attr, value);
			}

		// 属性に名前空間の指定がない場合
		} else {

			// 属性が"class"の場合、"className"に置き換える
			attr += (document.all && attr == 'class') ? 'Name' : '';

			// 値を設定
			node.setAttribute(attr, value);
		}
	},

	/**
	 * class属性を操作する関数ライブラリ
	 */
	classAttr : {

		/**
		 * 引数nodeのclass属性にvalueが存在するか確認
		 * 
		 * @param node 要素
		 * @param value 値
		 */
		check : function (node, value) {

			// 戻り値の設定
			var ret = false;

			// 要素が存在し、値が存在し、要素にclass属性が存在する場合
			if (node && value && BA.getAttr (node, 'class')) {

				// 要素のclass属性の値を配列に取得
				var names = BA.getAttr (node, 'class').split(' ');

				// 値を全て繰り返す
				for (var i = 0; i < names.length && !ret; i++)

					// 値が存在した場合、trueを返す
					ret = (names[i] == value)
			}
			return ret;
		},

		/**
		 * 引数nodeのclass属性にvalueを追加する
		 * 
		 * @param node 要素
		 * @param value 値
		 * @return 追加した値を含めたclass属性の値を返す
		 */
		add : function(node, value) {

			// 引数nodeが存在しない場合、NULLを返す
			if (!node)
				return null;

			// 値が存在し、引数nodeのclass属性に値が存在しない場合
			if (value && !BA.classAttr.check(node, value))

				// class属性にvalueを追加
				BA.setAttr(node, 'class', ((node.className) ? node.className + ' ' + value : value))

			// 追加した値を含めたclass属性の値を返す
			return BA.getAttr(node, 'class');
		},

		/**
		 * 引数nodeのclass属性のvalueを削除する
		 * 
		 * @param node 要素
		 * @param value 値
		 * @return 削除後のclass属性の値を返す
		 */
		remove : function(node, value) {

			// 引数が存在しない場合
			if (!node)
				return null;

			// 引数nodeのclass属性にvalueが存在する場合
			if (value && BA.classAttr.check(node, value)) {

				// class属性の値を取得する
				var names  = BA.getAttr(node, 'class').split(' ');
				var nNames = [];

				// 値を全て繰り返す
				for (var i = 0; i < names.length; i++)

					// 削除する値以外を配列に残す
					if (names[i] != value)
						nNames[nNames.length] = names[i];

				// class属性の値を新しい値で更新する
				BA.setAttr(node, 'class', nNames.join(' '));
			}
			// 更新した値を返す
			return BA.getAttr(node, 'class');
		}
	},

	/**
	 * 引数objに引数listenerを実行する引数typeを追加する
	 * 
	 * @param obj		要素
	 * @param type 		イベントハンドラ
	 * @param listener	関数
	 */
	addEvent : function(obj, type, listener) {

		// 引数が存在しない場合、処理終了
		if(!obj || !type || !listener) return;

		// addEventListenerが使用できる場合
		if (obj.addEventListener) {  // Std DOM Events

			// イベントを追加
			obj.addEventListener(type, listener, false);

		// addEventListenerが使用できない場合
		} else {

			// attachEventが使用できる場合、イベントを追加
			if (obj.attachEvent) {   // WinIE
					obj.attachEvent(
						'on' + type,
						function() { listener( {
							type            : window.event.type,
							target          : window.event.srcElement,
							currentTarget   : obj,
							clientX         : window.event.clientX,
							clientY         : window.event.clientY,
							pageX           : (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + window.event.clientX,
							pageY           : (document.documentElement.scrollTop  ? document.documentElement.scrollTop  : document.body.scrollTop ) + window.event.clientY,
							stopPropagation : function() { window.event.cancelBubble = true  },
							preventDefault  : function() { window.event.returnValue  = false }
						} ) }
					);
			// attachEventが使用できない場合
			} else {                 // MacIE

				// イベントハンドラを取得
				var exists = obj['on' + type];

				// イベントハンドラがブラウザに存在する場合と存在しない場合で追加処理をわける
				obj['on' + type]  = (exists) ?
					function() {
						exists();
						listener( {
							type            : window.event.type,
							target          : window.event.srcElement,
							currentTarget   : obj,
							clientX         : window.event.clientX,
							clientY         : window.event.clientY,
							pageX           : (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + window.event.clientX,
							pageY           : (document.documentElement.scrollTop  ? document.documentElement.scrollTop  : document.body.scrollTop ) + window.event.clientY,
							stopPropagation : function() { window.event.cancelBubble = true  },
							preventDefault  : function() { window.event.returnValue  = false }
						} );
					} : function() {
						listener( {
							type            : window.event.type,
							target          : window.event.srcElement,
							currentTarget   : obj,
							clientX         : window.event.clientX,
							clientY         : window.event.clientY,
							pageX           : (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + window.event.clientX,
							pageY           : (document.documentElement.scrollTop  ? document.documentElement.scrollTop  : document.body.scrollTop ) + window.event.clientY,
							stopPropagation : function() { window.event.cancelBubble = true  },
							preventDefault  : function() { window.event.returnValue  = false }
						} )
					};
			}
		}
	},

	/**
	 * 画面の初期表示処理に関数を実行する設定を行う
	 * 
	 * @param listener	関数
	 */
	addOnload : function(listener) {
		BA.addEvent(window, 'load', listener)
	},

	/**
	 * Javascript実行時にエラーが発生した場合、エラー発生場所とエラーメッセージをアラートで表示
	 */
	errorHandler : function() {

		// エラーメッセージを表示する設定の場合
		if (BA.showErrMsg) {

			// エラーメッセージを設定
			var msg = 'Error: ' + arguments[0] + '\n' +
			          'File: '  + arguments[1] + '\n' + 
			          'Line: '  + arguments[2];

			// エラーメッセージを表示
			alert(msg);
		}
		return true;
	}
};

// 共通関数オブジェクトを生成
var BA = new BAcommon;

