/**
 * POSTリクエストのレスポンスでフォームを更新する
 * @param response レスポンスボディ全文
 */
function updateFromByAjaxResponse(response)
{
	var target_node = null;
	var target_node_array = null;

	if(response.indexOf('_debug') == 0)
	{
		alert(response);
	}

	var lines = response.split("\n");

	for(var i = 0; i < lines.length; i++)
	{
		var line = lines[i];
		if(line.length == 0)
		{
			continue;
		}

		var pos = line.indexOf(":", 0);
		if(pos < 1)
		{
			continue;
		}

		var key = line.substr(0, pos).toLowerCase();

		var value = line.substr(pos + 1);
		value = replaceAll(value, "\\n", "\n");
		value = replaceAll(value, "\\\\", "\\");

		// updateコマンドは対象ノードの種類により処理を振り分ける
		if(key == "update")
		{
			if(target_node == null)
			{
				continue;
			}
			
			if(target_node.nodeName.toLowerCase() == 'select')
			{
				key = "selectoptionvalue";
			}
			else if(target_node.nodeName.toLowerCase() == 'textarea')
			{
				key = "settextarea";
			}
			else if(target_node.nodeName.toLowerCase() == 'input')
			{
				if(target_node.type.toLowerCase() == 'checkbox')
				{
					key = "checkifequal";
				}
				else if(target_node.type.toLowerCase() == 'radio')
				{
					key = "checkifequal";
				}
				else
				{
					key = "setvalue";
				}
			}
			else if(target_node.nodeName.toLowerCase() == 'img')
			{
				key = "setsource";
			}
			else if(target_node.nodeName.toLowerCase() == 'form')
			{
				key = "setaction";
			}
			else
			{
				key = "setinnerhtml";
			}
		}

		// アラートを表示する alert:メッセージ
		if(key == "alert")
		{
			alert(value);
		}
		// 指定のURLに遷移する location:URL
		else if(key == "location")
		{
			location.href = value;
		}
		// ウィンドウをclose()する closewindow:
		else if(key == "closewindow")
		{
			window.close();
		}
		// フォームをsubmit()する submit:
		else if(key == "submit")
		{
			if(target_node != null)
			{
				target_node.submit();
			}
		}
		// 新しいウィンドウをopen()する opennewwindow:URL
		else if(key == "opennewwindow")
		{
			window.open(value, "_blank");
		}
		// idによる処理対象ノード指定 targetbyid:識別子
		else if(key == "targetbyid")
		{
			target_node = document.getElementById(value);
			target_node_array = new Array(target_node);
		}
		// nameによる処理対象ノード指定 targetbyname:識別子
		else if(key == "targetbyname")
		{
			target_node_array = document.getElementsByName(value);
			target_node = target_node_array.item(0);
		}
		// valueプロパティの値を設定する setvalue:値
		else if(key == "setvalue")
		{
			if(target_node != null)
			{
				target_node.value = value;
			}
		}
		// srcプロパティの値を設定する setsource:値
		else if(key == "setsource")
		{
			if(target_node != null)
			{
				target_node.src = value;
			}
		}
		// actionプロパティの値を設定する setaction:値
		else if(key == "setaction")
		{
			if(target_node != null)
			{
				target_node.action = value;
			}
		}
		// textareaの値を設定する settextarea:値
		// 値には以下のエスケープシーケンスを含める。\n..改行 \\..円マーク
		else if(key == "settextarea")
		{
			if(target_node != null)
			{
				target_node.value = value;
			}
		}
		// disabledプロパティの値を設定する setdisabled:true|yes|false|no
		else if(key == "setdisabled")
		{
			if(target_node != null)
			{
				value = value.toLowerCase();
				target_node.disabled = (value == "true" || value == "yes");
			}
		}
		// valueの値が等しければcheckedにする checkifequal:値
		else if(key == "checkifequal")
		{
			for(var n = 0; n < target_node_array.length; n++)
			{
				target_node_array.item(n).checked = (target_node_array.item(n).value == value);
			}
		}
		// select要素のoptionをクリアする setinnerhtml:
		else if(key == "clearoption")
		{
			if(target_node != null)
			{
				while(target_node.length > 0)
				{
					target_node.options[target_node.length - 1] = null;
				}
			}
		}
		// select要素のoptionを追加する setinnerhtml:テキスト,値[,selected]
		else if(key == "addoption")
		{
			if(target_node != null)
			{
				var values = value.split(",");
				target_node.options[target_node.length] = new Option(values[0], values[1]);
				if(valuse[2] == "selected")
				{
					target_node.selectedIndex = target_node.length - 1;
				}
			}
		}
		// select要素のoptionをインデックスで選択状態にする selectoptionindex:インデックス
		else if(key == "selectoptionindex")
		{
			if(target_node != null)
			{
				target_node.selectedIndex = value;
			}
		}
		// select要素のoptionを値で選択状態にする selectoptionvalue:値
		else if(key == "selectoptionvalue")
		{
			if(target_node != null)
			{
				target_node.selectedIndex = -1;
				for(var n = 0; n < target_node.length; n++)
				{
					if(target_node.options[n].value == value)
					{
						target_node.selectedIndex = n;
						break;
					}
				}
			}
		}
		// innerHTMLプロパティの値を設定する setinnerhtml:値
		else if(key == "setinnerhtml")
		{
			if(target_node != null)
			{
				target_node.innerHTML = value;
			}
		}
		// innerHTMLプロパティの値に追記する addinnerhtml:値
		else if(key == "addinnerhtml")
		{
			if(target_node != null)
			{
				target_node.innerHTML += value;
			}
		}
		// 存在しないコマンドが指定された場合
		else
		{
			alert("RESPONSE:" + value);
		}
	}
}

/**
 * 全ての文字列を置き換える
 * @param expression 元の文字列
 * @param org 検索対象の文字列
 * @param dest 検索対象の替わりに挿入する文字列
 * @return 置換後の文字列
 */
function replaceAll(expression, org, dest)
{
    return expression.split(org).join(dest);  
}

/**
 * 指定のフォームをポストしその結果でフォームを更新する
 * @param form_name フォームのname。
 * @param callback_func 結果通知コールバック関数。
 * コールバック関数はfunc(result)形式。resultはtrue又はfalse。
 */
function postAndUpdateForm(form_name, callback_func)
{
	var input_nodes = new Array();
	disableAllInputNodes(document, input_nodes);
	postFormSync(
		form_name,
		function(result, data)
		{
			if(result)
			{
				enableInputNodes(input_nodes);
				updateFromByAjaxResponse(data);
				if('function' === typeof callback_func)
				{
					callback_func(true);
				}
			}
			else
			{
				alert(data);
				enableInputNodes(input_nodes);
				if('function' === typeof callback_func)
				{
					callback_func(false);
				}
			}
		});
}

/**
 * 指定のデータをポストしその結果でフォームを更新する
 * @param url ポスト先のURL
 * @param param_list ポストデータをプロパティとして指定したオブジェクト
 * @param callback_func 結果通知コールバック関数。
 * コールバック関数はfunc(result)形式。resultはtrue又はfalse。
 */
function postDataAndUpdate(url, param_list, callback_func)
{
	var input_nodes = new Array();
	disableAllInputNodes(document, input_nodes);
	postDataSync(
		url,
		param_list,
		function(result, data)
		{
			if(result)
			{
				enableInputNodes(input_nodes);
				updateFromByAjaxResponse(data);
				if('function' === typeof callback_func)
				{
					callback_func(true);
				}
			}
			else
			{
				alert(data);
				enableInputNodes(input_nodes);
				if('function' === typeof callback_func)
				{
					callback_func(false);
				}
			}
		});
}

/** 
 * 指定したフォーム入力ノードを有効化する
 * @param target_node_array ノードが格納されたArray
 * @return なし
 */
function enableInputNodes(target_node_array)
{
    for (var i = 0; i < target_node_array.length; i++)
    {
    	target_node_array[i].disabled = false;
    }
}

/** 
 * 指定したノード以下のフォーム入力ノードを無効化する
 * @param node_ins 処理対象のノード
 * @param target_node_array 無効化したノードを格納するArray
 * @return なし
 */
function disableAllInputNodes(node_ins, target_node_array)
{
	if(node_ins.nodeName.toLowerCase() == 'input' ||
		node_ins.nodeName.toLowerCase() == 'textarea' ||
		node_ins.nodeName.toLowerCase() == 'select')
	{
		if(!node_ins.disabled)
		{
			node_ins.disabled = true;
			target_node_array.push(node_ins);
		}
	}
	if(node_ins.hasChildNodes)
	{
		for(var i = 0; i < node_ins.childNodes.length; i++)
		{
			disableAllInputNodes(node_ins.childNodes.item(i), target_node_array);
		}
	}
}

/**
 * 指定したフォームをPOSTして結果を返す
 * @param form_name フォームのname。
 * @param callback_func 結果通知コールバック関数。
 * コールバック関数はfunc(result, data)形式。resultはtrue又はfalse。
 * resultがtrueの場合、dateはレスポンスボディ。
 * resultがfalseの場合、dataはエラー情報。
 */
function postFormSync(form_name, callback_func)
{
	// フォームを取得する
	var form = document.getElementsByName(form_name).item(0);
	// フォームの現在の値をPOST用形式で取得する
	var post_data = getInnerInputValuesByPostData(form, "");
	// 同期的にPOSTする
	postStringSync(form.action, post_data, callback_func);
}

/**
 * 指定したノードが内包する入力部品の現在の値をPOST用データに加工して返す
 * @param node_ins 検索対象のノード
 * @param data_str 結合するPOST用データ
 * @return POST用のデータ文字列
 */
function getInnerInputValuesByPostData(node_ins, data_str)
{
	if(node_ins.nodeName.toLowerCase() == 'input' ||
		node_ins.nodeName.toLowerCase() == 'textarea')
	{
		if(node_ins.type.toLowerCase() == 'checkbox' ||
			node_ins.type.toLowerCase() == 'radio')
		{
			if(!node_ins.checked)
			{
				return data_str;
			}
		}
		if(data_str.length > 0)
		{
			data_str = data_str + "&";
		}
		data_str = data_str + node_ins.name + "=" + encodeURIComponent(node_ins.value);
		return data_str;
	}
	if(node_ins.nodeName.toLowerCase() == 'select')
	{
		if(data_str.length > 0)
		{
			data_str = data_str + "&";
		}
		data_str = data_str + node_ins.name + "=" + encodeURIComponent(node_ins.options[node_ins.selectedIndex].value);
		return data_str;
	}
	if(node_ins.hasChildNodes)
	{
		for(var i = 0; i < node_ins.childNodes.length; i++)
		{
			data_str = getInnerInputValuesByPostData(node_ins.childNodes.item(i), data_str);
		}
	}
	return data_str;
}

/**
 * 指定したデータをPOSTして結果を返す
 * @param url ポスト先のURL
 * @param param_list ポストデータをプロパティとして指定したオブジェクト
 * @param callback_func 結果通知コールバック関数。
 * コールバック関数はfunc(result, data)形式。resultはtrue又はfalse。
 * resultがtrueの場合、dateはレスポンスボディ。
 * resultがfalseの場合、dataはエラー情報。
 */
function postDataSync(url, param_list, callback_func)
{
	// プロパティをPOST用形式にエンコードする
	var post_data = "";
	for (var key in param_list)
	{
		if(post_data.length > 0)
		{
			post_data += "&";
		}
		post_data += key + "=" + encodeURIComponent(param_list[key]);
	}
	// 同期的にPOSTする
	postStringSync(url, post_data, callback_func);
}

/**
 * 指定したデータを同期通信でPOSTして結果を返す
 * @param url ポスト先のURL
 * @param post_data POSTするデータ
 * @param callback_func 結果通知コールバック関数。
 * コールバック関数はfunc(result, data)形式。resultはtrue又はfalse。
 * resultがtrueの場合、dateはレスポンスボディ。
 * resultがfalseの場合、dataはエラー情報。
 */
function postStringSync(url, post_data, callback_func)
{
    try
    {
		// HTTPクライアントオブジェクトを生成する
		var hc = createXMLHttpRequest();
    	// リクエストを開く
        hc.open("POST", url, false);
        // リクエストヘッダを設定する
        hc.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        // ポストデータを送信する
        hc.send(post_data);

		if(hc.status == 200 && hc.responseText)
	    {
	        if(callback_func)
	        {
	        	callback_func(true, hc.responseText);
	        }
	    }
	    else
	    {
	        if(callback_func)
	        {
	        	callback_func(false, "HTTP RESULT CODE:" + hc.status);
	        }
	    }
    }
    catch(e)
    {
       	callback_func(false, "SCRIPT EXCEPTION:" + e);
    }
}

/**
 * 指定したデータを非同期通信でPOSTして結果を返す
 * @param url ポスト先のURL
 * @param post_data POSTするデータ
 * @param callback_func 結果通知コールバック関数。
 * コールバック関数はfunc(result, data)形式。resultはtrue又はfalse。
 * resultがtrueの場合、dateはレスポンスボディ。
 * resultがfalseの場合、dataはエラー情報。
 */
function postStringAsync(url, post_data, callback_func)
{
    try
    {
		// HTTPクライアントオブジェクトを生成する
		var hc = createXMLHttpRequest();
    	// リクエストを開く
		hc.open("POST", url, true);
        // レスポンス処理関数を設定する
        hc.onreadystatechange = function()
        {
            if(hc.readyState == 4)
            {
	            if(hc.status == 200 && hc.responseText)
	            {
	                if(callback_func)
	                {
	                	callback_func(true, hc.responseText);
	                }
	            }
	            else
	            {
	                if(callback_func)
	                {
	                	callback_func(false, "HTTP RESULT CODE:" + hc.status);
	                }
	            }
	    	}
        }
        // リクエストヘッダを設定する
        hc.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        // ポストデータを送信する
        hc.send(post_data);
    }
    catch(e)
    {
       	callback_func(false, "SCRIPT EXCEPTION:" + e);
    }
}

/**
 * XMLHttpRequestオブジェクトを取得する
 * @return 失敗した場合はnullを返す
 */
function createXMLHttpRequest()
{
    var xo = null;
    if(typeof XMLHttpRequest != 'undefined')
    {
        xo = new XMLHttpRequest();
    }
    else
    {
	    try
	    {
	        xo = new ActiveXObject("Msxml2.XMLHTTP");
	    }
	    catch(e)
	    {
	        try
	        {
	            xo = new ActiveXObject("Microsoft.XMLHTTP");
	        }
	        catch(e2)
	        {
	        }
	    }
	}
    return xo;
}

