$.fn.mf = function(param){

	String.prototype.format = function(arg){
		// 引数が配列でない場合は配列にする
		if(Object.prototype.toString.call(arg) != "[object Array]")
			arg = [arg];
		var str = this;
		for(var i = 0; i < arg.length; i++){
			str = str.replace("{" + i +"}", arg[i]);
		}
		return str;
	}

	Array.prototype.last = function(){
		return this[this.length - 1];
	}

	/*--------------------------------
		コードをパース
	--------------------------------*/
	var parse = function(code, labels, defs){

		/*--------------------------------
			codeを整形して各行に分解
		--------------------------------*/

		// 改行の統一
		code = code.replace(/\r\n/g, "\n");
		code = code.replace(/\r/g, "\n");
		// 行末の空白を削除
		code = code.replace(/\s*$/mg, "");
		// 空行・コメント行を削除
		code = code.replace(/^\s*$/mg, "");
		code = code.replace(/^\s*#.*$/mg, "");
		code = code.replace(/^\n/mg, "");
		// 無駄なインデントを削除
		while(!/^\S/m.test(code))
			code = code.replace(/^\t/mg, "");
		// codeを行で配列に分解する．
		var lines = code.split("\n");

		/*--------------------------------
			列定義の解析
		--------------------------------*/

		// 引数が省略されたときに代わりに使用する列のインデックス
		var alt;
		for(var i = 0; i < defs.length; i++){
			if(/^\*/.test(defs[i])){
				alt = i;
				defs[i] = defs[i].replace(/^\*(.*)/, "$1");
			}
		}

		/*--------------------------------
			一括指定ラベルの処理
		--------------------------------*/
		var section = [];
		for(var deleted = false, i = 0; i < lines.length; i += deleted?0:1, deleted = false){
			// インデント数を調べる
			var indent = lines[i].match(/^\t+/);
			indent = indent?indent[0].length:0;
			// TAB文字で分解
			var items = lines[i].replace(/^\t*(.*)/, "$1").split("\t");
			// 現在の行のインデント数以上で定義された一括指定ラベルを削除
			if(section.length != 0){
				for(var j = section.length - 1; 0 <= j; j--){
					if(indent <= section[j].indent){
						section.pop();
					}
				}
			}
			// 先頭文字が*なら一括指定あり
			if(items[0].charAt(0) == "*"){
				// 一括指定ラベルとインデント数を保存
				section.push({
					indent: indent,
					label: lines[i].split("\t")[0].replace(/^\*(.*)/, "$1")
				});
				// この行は削除する
				lines.splice(i, 1);
				deleted = true;
				// 次の行の処理へ
				continue;
			}
			// 一括指定されたラベルを追加する処理
			if(section.length != 0){
				for(var j = section.length - 1; 0 <= j; j--){
					if(section[j].indent < indent){
						// インデントを一つ削除
						lines[i] = lines[i].replace(/^\t{0,1}(.*)/, "$1");
						// 一括指定されたラベルを追加
						lines[i] = lines[i].replace(/^(\t*)/, "$1" + section[j].label + "\t");
					}else{
						section.pop();
					}
				}
			}
		}

		/*--------------------------------
			各行をオブジェクトに変換
		--------------------------------*/

		var objs = [];
		// 各行ごとにオブジェクトに変換する
		for(var i = 0; i < lines.length; i++){
			var obj = {};
			// インデント数を調べる
			var indent = lines[i].match(/^\t*/);
			obj.indent = indent?indent[0].length:0;
			// TAB文字で分解
			var items = lines[i].replace(/^\t*(.*)/, "$1").split("\t");
			// 省略可能な列数以下ならこの行は飛ばす
			if(items.length < alt)
				continue;
			// 各列をオブジェクトに変換
			for(var j = 0; j < defs.length; j++){
				if(j in items)
					obj[defs[j]] = items[j];
				else
					obj[defs[j]] = items[alt]
				// 定義済みラベルがあるならそれで置換
				if(labels[obj[defs[j]]] != undefined)
					obj[defs[j]] = labels[obj[defs[j]]];
			}
			objs.push(obj);
		}

		return objs;
	}

	/*--------------------------------
		formの基礎を生成
	--------------------------------*/
	var generateForm = function(param){
		var form = $("<form>");
		// urlを追加
		if(param.url)
			form.attr("action", param.url);
		// actionを追加
		if(param.method)
			form.attr("method", param.method);
		// 文字コード指定を追加
		if(param.encode)
			form
				.attr("accept-charset", param.encode)
				.append($("<input>")
					.attr("type", "hidden")
					.attr("name", "ie") 
					.attr("value", param.encode)
				)
			;
		// クラスを設定
		if(param._class)
			form.attr("class", param._class);
		return form;
	}

	/*--------------------------------
		formからオブジェクトに変換
	--------------------------------*/
	var form2obj = function(form){
		var obj = {
			url: form.attr("action"),
			method: form.attr("method"),
			charset: form.attr("accept-charset"),
			params: {}
		};
		// すべての入力要素を調べる
		form.find("input[type=hidden],select,input[type=text]").each(function(){
			obj.params[$(this).attr("name")] = $(this).val();
		});
		return obj;
	}

	/*--------------------------------
		複合キーの分割処理
	--------------------------------*/
	var split = function(data) {
		var params = {};
		for(var name in data.params){
			names = name.split(",");
			vals = data.params[name].split(",");
			for(var j = 0; j < names.length; j++){
				params[names[j]] = vals[j]?vals[j]:"";
			}
		}
		data.params = params;
		return data;
	}

	/*--------------------------------
		連結キーの連結処理
	--------------------------------*/
	var associate = function(data) {
		var params = {};
		for(var name in data.params){
			// とりあえず各成分を取り出す
			var item = {
				name: name.match(/[a-zA-Z]*/)[0],
				num: name.match(/\d*$/)[0],
				val: data.params[name]
			}
			// 名前の重複チェック
			if(!params[item.name])
				params[item.name] = [];
			// numで昇順に並べる．numがnullなら先頭に収める
			var i;
			if(item.num == "")
				i = 0;
			else
				for(i = 0; i < params[item.name].length; i++)
					if(item.num <= params[item.name][i].num)
						break;
			params[item.name].splice(i, 0, item);
		}
		// すべて連結
		for(var name in params){
			var val = params[name][0].val;
			for(var i = 1; i < params[name].length; i++){
				val += params[name][i].val; 
			}
			params[name] = val;
		}
		data.params = params;
		return data;
	}

	/*--------------------------------
		submit
	--------------------------------*/
	var submit = function(data){
		var form = $("<form>")
			.attr("action", data.url)
			.attr("method", data.method)
			.attr("accept-charset", data.charset)
			.hide()
		;
		for(var name in data.params){
			form.append($("<input>")
				.attr("name", name)
				.val(data.params[name])
			);
		}
		$("body").append(form);	// IEはDOMに追加しないと送信してくれない
		form.get(0).submit();
		//Ajaxで送信
		/*switch(data.method){
			case "get":
			case "GET":
				$.get(data.url, data.params);
				break;
			case "post":
			case "POST":
				$.post(data.url, data.params);
				break;
		}*/
	}

	/*--------------------------------
		簡易なformを生成
	--------------------------------*/
	var martl = function(form, data){
		for(var i = 0; i < data.length; i++){
			switch(data[i].attr){
				case "f":
					// inputを生成
					form.append($("<input>")
						.attr("type", "hidden")
						.attr("name", data[i].value)
						.attr("value", data[i].label)
					);
					continue;
				case "s":
					// selectを生成
					form
						.append($("<label>")
							.attr("for", data[i].value)
							.text(data[i].label)
							.append($("<select>")
								.attr("name", data[i].value)
							)
						)
					;
					continue;
				case "o":
					// optionを生成
					form
						.find("select:last")
							.append($("<option>")
								.val(data[i].value)
								.text(data[i].label)
							)
					;
					continue;
				case "t":
					// textを生成
					form
						.append($("<label>")
							.attr("for", data[i].value)
							.text(data[i].label)
							.append($("<input>")
								.attr("name", data[i].value)
								.attr("type", "text")
								.attr("size", "8")
							)
						)
					;
				default:
					continue;
			}
		}
		// フォームに送信ボタンを追加
		form
			.append($("<input>")
				.attr("type", "button")
				.attr("value", "購入する")
				.bind("click", function(){
					// 送信する
					var data = form2obj(form);
					data = split(data);
					data = associate(data);
					submit(data);
				})
			)
		;

		return form;
	}

	/*--------------------------------
		絞り込みできるformを生成
	--------------------------------*/
	var mart = function(form, data){
		var selects = [];
		var table = [];
		var row = [];
		for(var i = 0; i < data.length; i++){
			switch(data[i].attr){
				case "f":
					// inputを生成
					form
						.append($("<input>")
							.attr("type", "hidden")
							.attr("name", data[i].value)
							.attr("value", data[i].label)
						)
					;
					continue;
				case "s":
					// selectを生成
					selects[data[i].indent] = $("<select>")
						.attr("name", data[i].value)
						// 未選択を意味するoptionを追加
						.append($("<option>")
							.val("")
							.text("選択する")
						)
					;
					form
						.append($("<label>")
							.attr("for", data[i].value)
							.text(data[i].label)
							.append(selects[data[i].indent])
						)
					;
					continue;
				case "o":
					// selectに重複したoptionが無い場合はoptionを追加
					var exist = false;
					var options = selects[data[i].indent].find("option");
					for(var j = 0; j < options.length; j++){
						if(options.eq(j).val() == data[i].value){
							exist = true;
							break;
						}
					}
					if(!exist){
						selects[data[i].indent]
							.append($("<option>")
								.val(data[i].value)
								.text(data[i].label)
							)
						;
					}
					// テーブルを生成
					row[data[i].indent] = data[i].value;
					if(data[i].indent == selects[data[i].indent].length)
						table.push(row.slice(0));
					continue;
			}
		}

		// セレクトボックスにフォーカスしたら項目を絞り込む
		for(var index = 0; index < selects.length; index++){
			with({index:index}){
				selects[index].bind("focus", function(){
					// とりあえず先頭の項目以外をすべて無効化。先頭の項目は未選択を意味しているので。
					selects[index]
						.find("option")
							.attr("disabled", "disabled")
							.filter(":first")
								.removeAttr("disabled")
					;

					// 選択肢テーブルを絞り込む
					var reduced = table.slice(0);		// ただインスタンスをコピーしているだけ
					for(var j = 0; j < selects.length; j++){
						if(j == index){
							continue;
						}
						var selected = selects[j].val();
						if(selected == ""){
							continue;
						}
						for(var deleted = false, i = 0; i < reduced.length; i += deleted?0:1, deleted = false){
							if(reduced[i][j] != selected){
								reduced.splice(i, 1);
								deleted = true;
							}
						}
					}

					// 絞り込んだ選択肢テーブルにある項目だけ有効化
					for(var i = 0; i < reduced.length; i++){
						var valid = reduced[i][index];
						var options = selects[index].find("option");
						for(var i2 = 1; i2 < options.length; i2++)
							if(options.eq(i2).val() == valid)
								options.eq(i2).removeAttr("disabled");
					}
				});
			}
		}

		// フォームに送信ボタンを追加
		form
			.append($("<input>")
				.attr("type", "button")
				.val("購入する")
				.bind("click", function(){
					// 全項目が選択されているか調べる
					for(var i = 0; i < selects.length; i++){
						if(selects[i].get(0).selectedIndex == 0){
							alert("全ての項目を選択してください");
							return false;
						}
					}
					// 送信する
					var data = form2obj(form);
					data = split(data);
					data = associate(data);
					submit(data);
				})
			)
		;

		return form;
	}

	/*--------------------------------
		メイン処理
	--------------------------------*/
	return $(this).hide().each(function(){
		// コードをオブジェクトに変換
		var data = parse($(this).val(), {"[empty]": ""}, ["attr", "*value", "label"]);
		// formを生成
		var form = generateForm(param)
			.insertBefore($(this))
		;
		// formの中身を生成
		if(param.light)
			martl(form, data);
		else
			mart(form, data);
		// formを整形
/*		form
			.find("input:visible,label:visible")
				.wrap("<li>")
			.end()
			.find("input:hidden,label:hidden")
				.wrap($("<li>").hide())
		;*/
	});

}

/*--------------------------------
	ページ読み込み時の処理
--------------------------------*/
$(function(){
	$("textarea.mfl").mf({
		url: "/bin/mart.cgi",
		method: "GET",
		encode: "UTF-8",
		light: true,
		_class: "martl"
	});
	$("textarea.mf").mf({
		url: "/bin/mart.cgi",
		method: "GET",
		encode: "UTF-8",
		light: false,
		_class: "martl"
	});
});

/*--------------------------------
	以下互換性維持のために安定版のコード
--------------------------------*/

$.fn.mfstable = function(path, param){

	// 定義済みラベル
	var definedLabels = {
		'[nolabel]': ''
	};

	// まずtextareaを非表示にする
	$(this).hide();

	return $(this).each(function(){
		var code = this.value;
		if(param.light){

			/*--------------------------------
				codeの整形
			--------------------------------*/

			// 改行が\rの場合の対策
			code = code.replace(/\r/g, "\n");
			newLine = "\n";

			// 行末の空白を削除
			code = code.replace(/\s*$/mg, "");

			// 空行を削除
			code = code.replace(/^\s*$/mg, "");
			code = code.replace(/^\n/mg, "");

			// 無駄なインデントを削除
			while(code.match(/^[^ ]/) == null){
				code = code.replace(/^ /mg, "");
			}
			while(code.match(/^\S/) == null){
				code = code.replace(/^\t/mg, "");
			}

			/*--------------------------------
				各種データ
			--------------------------------*/

			var inputs = new Array();
			var labels = new Array();
			var selects = new Array();

			/*--------------------------------
				コードの解析
			--------------------------------*/

			// selectはoptionを加える度に参照するために
			// forループ中で維持したいからここで宣言．
			var select;

			// codeを行で配列に分解する．
			var lines = code.split(newLine);

			for(var i = 0; i < lines.length; i++){
				var items = lines[i].split("\t");
				switch(items[0]){

					// fixed row => input
					case "f":
						// Create input
						var input = document.createElement("input");
						input.type = "hidden";
						input.name = items[1];
						input.value = items[2];
						inputs.push(input);

						continue;

					// select row => label & select
					case "s":
						// labelを生成
						var label = document.createElement("label");
						label.setAttribute("for", items[1]);
						// 第3引数，valueが省略されている場合はnameと同じ．
						if(2 in items){ // 第3引数が存在
							// 定義済みのラベルがある場合は置き換える．
							if(items[2] in definedLabels){
								label.innerHTML = definedLabels[items[2]];
							}else{
								label.innerHTML = items[2];
							}
						}else{ // 第3引数が非存在
							label.innerHTML = items[1];
						}
						labels.push(label);

						// Create select
						select = document.createElement("select");
						select.name = items[1];
						label.appendChild(select);
						selects.push(select);

						continue;

					// option row => option
					case "o":
						// Create option
						var option = document.createElement("option");
						option.value = items[1];
						// 第3引数，textが省略されている場合はvalueと同じ．
						if(2 in items)
							option.innerHTML = items[2];
						else
							option.innerHTML = items[1];
						select.appendChild(option);

						continue;

					// disabled option row
					case "d":
						// Create option
						option = document.createElement("option");
						option.value = items[1];
						option.disabled = "disabled";
						// 第3引数，textが省略されている場合はvalueと同じ．
						if(3 <= items.length)
							option.innerHTML = items[2];
						else
							option.innerHTML = items[1];
						select.appendChild(option);

						continue;

				}
			}
			/*--------------------------------
				formを生成
			--------------------------------*/

			// submitボタンを作る
			var button = $("<input>")
				.attr("type", "submit")
				.attr("value", "購入する")
			;

			// formを生成
			var form = $("<form>")
				.attr("method", "GET")
				.attr("action", path)
				.append(inputs)
				.append(labels)
				.append(button)
			;

			// 文字コード指定を追加
			if(param.encode){
				var encode = $("<input>")
					.attr("type", "hidden")
					.attr("name", "ie") 
					.attr("value", param.encode)
				;
				form
					.append(encode)
					.attr("accept-charset", param.encode)
				;
			}

			// クラスを設定
			if(param._class){
				form.attr("class", param._class);
			}

			// formを追加
			$(this).before(form);

		}else{

			/*--------------------------------
				補助関数
			--------------------------------*/
			function getIndentLevel(line){
				var level = 0;
				while(line.charAt(0) == "\t"){
					level++;
					line = line.slice(1);
				}
				return level;
			}

			function enableOption(select, optionValue){
				for(var i = 0; i < select.options.length; i++){
					if(select.options[i].value == optionValue){
						select.options[i].removeAttribute("disabled");
					}
				}
			}

			// 文字列をTABで分割した配列を返す．ただし，空の要素は削除して．
			function splitLine(line){
				items = line.split("\t");
				for(var deleted = false, i = 0; i < items.length; i += deleted?0:1){
					if(items[i] == "" || items[i] == undefined){
						items.splice(i, 1);
						deleted = true;
					}else{
						deleted = false;
					}
				}
				return items;
			}

			/*--------------------------------
				選択肢を絞る関数
			--------------------------------*/

			function reduceOptions(onesIndex){
				return function(){
					// とりあえずすべて無効化
					for(var i = 0; i< selects.length; i++){
						for(var j = 1; j < selects[i].options.length; j++){
							selects[i].options[j].disabled = "disabled";
						}
					}

					// 選択肢テーブルを絞り込む
					var temporaryData = data.slice(0);		// ただインスタンスをコピーしているだけ
					for(var i = 0; i < selects.length; i++){
						if(onesIndex == i){
							continue;
						}
						var selected = selects[i].options[selects[i].selectedIndex].value;
						if(selected != ""){
							for(var deleted = false, j = 0; j < temporaryData.length; j += deleted?0:1){
								if(temporaryData[j][i] != selected){
									temporaryData.splice(j, 1);
									deleted = true;
								}else{
									deleted = false;
								}
							}
						}
					}

					// 絞り込んだ選択肢テーブルにある項目だけ有効化
					for(var j = 0; j< temporaryData.length; j++){
						for(var i = 0; i < temporaryData[j].length; i++){
							enableOption(selects[i], temporaryData[j][i]);
						}
					}
				};
			}

			/*--------------------------------
				codeの整形
			--------------------------------*/

			// 改行が\rの場合の対策
			code = code.replace(/\r/g, "\n");
			newLine = "\n";

			// 行末の空白を削除
			code = code.replace(/\s*$/mg, "");

			// 空行を削除
			code = code.replace(/^\s*$/mg, "");
			code = code.replace(/^\n/mg, "");

			// 無駄なインデントを削除
			while(code.match(/^[^ ]/) == null){
				code = code.replace(/^ /mg, "");
			}
			while(code.match(/^\S/) == null){
				code = code.replace(/^\t/mg, "");
			}

			/*--------------------------------
				各種データ
			--------------------------------*/

			var data = new Array();
			var inputs = new Array();
			var labels = new Array();
			var selects = new Array();

			/*--------------------------------
				ツリーからテーブルに変換
				同時にselectsとlabelsを生成
			--------------------------------*/

			// 現在のセクション [hidden]等
			var section = "";
			// 冗長化2次元配列に納める一時データ
			var itemCandidate = new Array();

			// codeを行で配列に分解する．
			var lines = code.split(newLine);

			// コードの解析
			for(var i = 0; i < lines.length; i++){
				indentLevel = getIndentLevel(lines[i]);
				if(indentLevel == 0){
					context = lines[i]
					continue;
				}else{
					indentLevel--;
					items = splitLine(lines[i]);
					if(items.length < 1)
						continue;
					switch(context){
						case "[hidden]":
							var input = document.createElement("input");
							input.type = "hidden";
							input.name = items[0];
							// 第2引数はvalueが省略されている場合はnameと同じ．
							if(1 in items){
								input.value = items[1];
							}else{
								input.value = items[0];
							}
							inputs.push(input);
							continue;
						case "[select]":
							// select要素を生成
							selects[indentLevel] = document.createElement("select");
							selects[indentLevel].onfocus = reduceOptions(indentLevel);
							selects[indentLevel].index = indentLevel;
							// 未選択を意味するoptionを追加
							option = document.createElement("option");
							option.value = "";
							option.innerHTML = "選択する";
							selects[indentLevel].appendChild(option);
							// 生成したselect要素をlabelに内包させる．
							labels[indentLevel] = document.createElement("label");
							selects[indentLevel].name = items[0];
							// 第2引数，valueが省略されている場合はnameと同じ．
							if(1 in items){	// 第2引数が存在
								// 定義済みのラベルがある場合は置き換える．
								if(items[1] in definedLabels){
									labels[indentLevel].innerHTML = definedLabels[items[1]];
								}else{
									labels[indentLevel].innerHTML = items[1];
								}
							}else{			// 第2引数が非存在
								labels[indentLevel].innerHTML = items[0];
							}
							labels[indentLevel].appendChild(selects[indentLevel]);
							continue;
						case "[option]":
							// 全部入りリスト(Element)を作る．
							var option = document.createElement("option");
							option.value = items[0];
							// 第2引数，textが省略されている場合はvalueと同じ．
							if(1 in items){
								option.innerHTML = items[1];
							}else{
								option.innerHTML = items[0];
							}
							// 重複チェック
							var alreadyAdded = false;
							for(var k = 0; k < selects[indentLevel].options.length; k++){
								if(selects[indentLevel].options[k].value == option.value){
									alreadyAdded = true;
									break;
								}
							}
							if(!alreadyAdded){
								selects[indentLevel].appendChild(option);
							}
							// 選択肢テーブルを作る．(ツリーからテーブルへ変換)
							itemCandidate = itemCandidate.slice(0);
							itemCandidate[indentLevel] = option.value;
							if(indentLevel == selects.length - 1){ // ツリーの末端である
								data.push(itemCandidate);
							}
							continue;
					}
				}
			}

			/*--------------------------------
				formを生成
			--------------------------------*/

			// formを生成
			var form = $("<form>")
				.attr("method", "GET")
				.attr("action", path)
				.append(inputs)
				.append(labels)
			;

			// 文字コード指定を追加
			if(param.encode){
				var encode = $("<input>")
					.attr("type", "hidden")
					.attr("name", "ie") 
					.attr("value", param.encode)
				;
				form
					.append(encode)
					.attr("accept-charset", param.encode)
				;
			}
			
			// クラスを設定
			if(param._class){
				form.attr("class", param._class);
			}

			// submitボタンを作る
			var button = $("<button>")
				.bind("click", function(){
					// 全項目が選択されているか調べる
					for(var i = 0; i < selects.length; i++){
						if(selects[i].selectedIndex == 0){
							alert("全ての項目を選択してください");
							return false;
						}
					}
					// 送信する
					form.submit();
					return false;
				})
				.text("購入する")
			;

			// formにボタンを追加
			form.append(button);
			
			// formを追加
			$(this).before(form);

		}
	});
}

$(function(){
	$("textarea.martl").mfstable("/bin/mart.cgi", {
		light: true,
		_class: "martl",
		encode: "UTF-8"
	});
	$("textarea.mart").mfstable("/bin/mart.cgi", {
		light: false,
		_class: "mart",
		encode: "UTF-8"
	});
});

