AjaxSelect = Class.create();
AjaxSelect.prototype = {
	/*
		args['nodes'] SELECTタグのIDの配列
		args['callback'] 最後のSELECTが変化したときのコールバック関数
		args['requestURI'] JSONリクエスト先URL
	*/
	bInitValues : false,
	initialize: function(args) {
		this.nodes = args['nodes'];
		this.callback = args['callback'];
		this.requestURI = args['requestURI'];

		var __this = this;
		this.preset = [];
		this.nodes.each(function(node,index){
			__this.preset[index] = ($(node).length > 1);
		});

		var values = args['values'];
		if (values) { this.initValues(values); }
		for (var i = 0; i < this.nodes.length; i++) {
			if (! $(this.nodes[i])) { return; } // error
			Event.observe($(this.nodes[i]), 'change', (function(_i) { return function() { __this.onChange(_i); }; })(i) );
		}
		if (! values) { this.onChange(-1); }
	},
	// プルダウンの初期化
	initOptions: function (index, list, defval) {
		var elem = $(this.nodes[index]);
		elem.disabled = true; // IE6ハック:ここでdisableにして要素追加後にenableにするとフィールド幅が調整される
		var opts = $A(elem.options).reverse(false);
		opts.pop(); // 最後の要素を取り除く
		opts.each(function(elem){ if (elem) { elem.parentNode.removeChild(elem); } });
		var hash = $H(list);
		hash.each(function(pair) {
			var eopt = document.createElement( 'option' );
			elem.appendChild( eopt );
			eopt.text  = pair.value;
			eopt.value = pair.key;
			if ( pair.key == defval ) eopt.selected = true;
		});
		elem.disabled = (elem.length == 1);
	},
	indexOf: function (elem, value) {
		var arr = $A(elem.options);
		return arr.indexOf(arr.find(function(opt) { return opt.value == value; }));
	},
	// プルダウンの値を初期設定する
	initValues: function (arr) {
		this.bInitValues = true;
		var hash = $H(new Object());
		var __this = this;
		
		$A(arr).each(function(value, index){ 
			var nodeid = __this.nodes[index];
			var element = $(nodeid);
			if (__this.preset[index]) {
				element.selectedIndex = __this.indexOf(element, value);
			} else {
			    var h = $H(hash).toQueryString();
				__this.updateRequest(__this.requestURI, hash.toQueryString(), function (data) {
					__this.initOptions( index, data, value );
				});
				element.selectedIndex = __this.indexOf(element, value);
			}
			hash.set(nodeid, value);
		});
		this.bInitValues = false;
	},
	// プルダウンの値を取得する
	getValue: function (index) {
		var elem = $(this.nodes[index]);
		if (! (elem && elem.options)) return;
		var opts = elem.options;
		for (var i = 0; i < opts.length; i++) {
			if (opts[i].selected) return opts[i].value;
		}
	},
	// プルダウンのキーとラベルの組を取得する
	getHash: function (index) {
		var elem = $(this.nodes[index]);
		if (! (elem && elem.options)) return;
		var opts = elem.options;
		for (var i = 0; i < opts.length; i++) {
			if (opts[i].selected) {
				return {key: opts[i].value, value: opts[i].text};
			}
		}
	},
	// 選択が変更された
	onChange: function (index) {
		if (this.bInitValues) { return; }
		var update = index + 1;
		if (update == this.nodes.length) {
			var hash = {};
			for (var i = 0; i < update; i++) {
				hash[this.nodes[i]] = this.getHash(i);
			}
			if (this.callback) {
				this.callback(hash);
			}
			return;
		}
		var hash = {};
		for (var i = 0; i <= index; i++) {
			hash[this.nodes[i]] = this.getValue(i);
		}
		var __this = this;
		var element = $(this.nodes[update]);
		this.updateRequest(this.requestURI, $H(hash).toQueryString(), function (data) {
			__this.initOptions( update, data );
		});
		for (var i = update + 1; i < this.nodes.length; i++) {
			this.initOptions(i, new Object());
		}
	},
	// 更新要求
	updateRequest: function(url, params, callback) {
		url += (url.indexOf('?') == -1 ? '?' : '&') + params;
		var __this = this;
		var opt = {
			method: 'GET', 
			asynchronous: true,
			onComplete: function (req) {
				if (req && req.responseText) {
					callback(eval('(' + __this.getResponseText(req) + ')'));
				}
			}
		};
		var conn = new Ajax.Request( url, opt );
	},
	// Safariの文字化け防止
	getResponseText: function (req) {
		var text = req.responseText;
		if ( navigator.appVersion.indexOf('KHTML') > -1 ) {
			var esc = escape(text);
			if ( esc.indexOf('%u') < 0 && esc.indexOf('%') > -1 ) {
				text = decodeURIComponent(esc);
			}
		}
		return text;
	}
};
