/*
 * $.StationChoose
 * station search helper plugin
 */

(function($){ // start $=jQuery encapsulation


// doc ready execution
$(function(){
	if($('#stChooseForm').size()){
		$.stationChoose = new $.StationChoose('#stChooseForm');
		$.stationChoose.init();
		$('#stSearchModBox').submit(function(e){
			e.preventDefault();
			var val = $('#stName').val();
			if(!val){
				return;
			}
			location.href = '/station/' + val + '/';
		});
	}
	if($('#passageChooseForm1').size()){
		$.passageChoose1 = new $.PassageChoose('#passageChooseForm1');
		$.passageChoose1.init();
	}
	if($('#passageChooseForm2').size()){
		$.passageChoose2 = new $.PassageChoose('#passageChooseForm2');
		$.passageChoose2.init();
	}
});

/**
 * $.containerCollection
 */
$.containerCollection = {
	_containers: [],
	hideAllContainers: function(){
		$.each(this._containers, function(){
			this.hide();
		});
		return this;
	},
	registerContainer: function(container){
		this._containers.push(container);
		return this;
	},
	discardContainer: function(container){
		var array = [];
		$.each(this._containers, function(){
			if(this===container){
				return;
			}else{
				array.push(this);
			}
		});
		this._containers = array;
	}
};

/**
 * $.StationChoose
 */
$.StationChoose = function(elem, options){
	this.options = $.extend({}, this.options, options);
	this.elem = $(elem);
};
$.StationChoose.prototype = {
	options: {
	
		/* load bgiframe plugin and turn this true if you got any trouble with select element in IE6 */
		bgiframe: true,

		/* fadeSpeed */
		container_fadeIn_speed: 200,
		container_fadeOut_speed: 200,

		/* options about lineChooseContainer */
		line_template_altSelect: [''
			,'<a class="altSelect" href="#"></a>'
		].join(''),
		line_template_placeholder: [''
			,'<div class="placeholder"></div>'
		].join(''),
		line_template_container: [''
			,'<div class="stSearch_open">'
			,'	<div class="frame01"><div class="frame02"><div class="frame03">'
			,'		<p class="closeBtn"><a href="#"><img src="/common/images/close_btn_01.gif" alt="閉じる" width="69" height="23" /></a></p>'
			,'		<div class="selectLineBoxWrap">'
			,'		</div>'
			,'	</div></div></div>'
			,'</div>'
		].join(''),
		line_container_selector_inner: '.selectLineBoxWrap',
		line_container_selector_close: '.closeBtn a',
		line_template_item: [''
			,'<div class="selectLineBox">'
			,'	<p class="ico"><a href="#"></a></p>'
			,'	<p class="line"><a href="#"></a></p>'
			,'</div>'
		].join(''),

		/* options about stationChooseContainer */
		station_template_altSelect: [''
			,'<a class="altSelect" href="#"></a>'
		].join(''),
		station_template_placeholder: [''
			,'<div class="placeholder"></div>'
		].join(''),
		station_template_container: [''
			,'<div class="stSearch_open">'
			,'	<div class="frame01"><div class="frame02"><div class="frame03">'
			,'		<p class="closeBtn"><a href="#" title="閉じる"><img src="/common/images/close_btn_01.gif" alt="閉じる" width="69" height="23" /></a></p>'
			,'		<div class="selectNameBoxWrap">'
			,'		</div>'
			,'	</div></div></div>'
			,'</div>'
		].join(''),
		station_container_selector_inner: '.selectNameBoxWrap',
		station_container_selector_close: '.closeBtn a',
		station_template_group: [''
			,'<div class="selectNameBox">'
			,'	<p class="ico"></p>'
			,'	<p class="line"></p>'
			,'	<ul class="list"></ul>'
			,'</div>'
		].join(''),
		station_group_selector_ico: '.ico',
		station_group_selector_line: '.line',
		station_group_selector_ul: '.list',
		station_template_item: [''
			,'<li>'
			,'	<a href="#"></a>'
			,'</li>'
		].join('')

	},
	_originalStationSelect: null,
	init: function(){
		if(!this.elem.size()){ return; }
		this._create();
		this._eventify();
		this._handleCache();
		return this;
	},
	_create: function(){
		var selects = $('select', this.elem);
		this._form = new $.StationChoose.Form(this.elem);
		this._create_selectLine(selects.eq(0));
		this._create_selectStation(selects.eq(1));
		this._originalStationSelect = selects.eq(1).clone();
		return this;
	},
	_create_selectLine: function(elem){
		var all = this.options;
		var o = {
			hasGroup: false,
			bgiframe: all.bgiframe,
			template_altSelect: all.line_template_altSelect,
			template_placeholder: all.line_template_placeholder,
			template_container: all.line_template_container,
			container_selector_inner: all.line_container_selector_inner,
			container_selector_close: all.line_container_selector_close,
			container_fadeIn_speed: all.container_fadeIn_speed,
			container_fadeOut_speed: all.container_fadeIn_speed,
			template_group: all.line_template_group,
			group_selector_ico: all.line_group_selector_ico,
			group_selector_line: all.line_group_selector_line,
			group_selector_ul: all.line_group_selector_ul,
			template_item: all.line_template_item
		};
		this._select_line = new $.expandSelect.Select(elem ,o);
		return this;
	},
	_create_selectStation: function(elem){
		var all = this.options;
		var o = {
			hasGroup: true,
			bgiframe: all.bgiframe,
			template_altSelect: all.station_template_altSelect,
			template_placeholder: all.station_template_placeholder,
			template_container: all.station_template_container,
			container_selector_inner: all.station_container_selector_inner,
			container_selector_close: all.station_container_selector_close,
			container_fadeIn_speed: all.container_fadeIn_speed,
			container_fadeOut_speed: all.container_fadeIn_speed,
			template_group: all.station_template_group,
			group_selector_ico: all.station_group_selector_ico,
			group_selector_line: all.station_group_selector_line,
			group_selector_ul: all.station_group_selector_ul,
			template_item: all.station_template_item
		};
		this._select_station = new $.expandSelect.Select(elem ,o);
		return this;
	},
	_eventify: function(){
		var form = this._form;
		var line = this._select_line;
		var station = this._select_station;
		line.addChangeListener(function(){
			var val = line.getVal();
			var text = line.getSlectedOptionText();
			if(val==='all' || val===''){
				station.showAllGroups();
			}else{
				station.narrowDownGroup(text);
			}
			line.showAllItems();
			station.enable();
			setTimeout(function(){
				station.getContainer().updateItemsOnOff();
			},100);
		});
		station.addChangeListener(function(){
			var val = station.getVal();
			val ? form.allowSubmit() : form.forbidSubmit();
		});
		return this;
	},
	_handleCache: function(){
		/* backbutton or reload make previous option element selected */
		var line = this._select_line;
		setTimeout(function(){
			line.elem.trigger('change');
			// station will automatically change triggered
			// after line was change triggered
		},200);
	}
};

/**
 * $.StationChoose.Form
 */
$.StationChoose.Form = function(elem, options){
	this.options = $.extend({}, this.options, options);
	this.elem = $(elem);
	this._init();
	this._eventify();
	this.forbidSubmit();
};
$.StationChoose.Form.prototype = {
	options: {},
	elem: null,
	_button: null,
	_submitAllowed: false,
	_init: function(){
		this._button = new $.StationChoose.Form.SubmitButton($(':image', this.elem));
		return this;
	},
	_eventify: function(){
		this.elem.submit( $.proxy(this._submitHandler, this) );
		return this;
	},
	_submitHandler: function(e){
		if(!this._submitAllowed){
			e.preventDefault();
		}
		return this;
	},
	allowSubmit: function(){
		this._submitAllowed = true;
		this._button.enable();
		return this;
	},
	forbidSubmit: function(){
		this._submitAllowed = false;
		this._button.disable();
		return this;
	}
};

/**
 * $.StationChoose.Form.SubmitButton
 */
$.StationChoose.Form.SubmitButton = function(elem){
	this.elem = $(elem);
	this.elem.rolloverImg();
	this._path_on = this.elem.attr('src');
	this._path_off = this._path_on.replace(/(^.+)(\.[^.]+)/,'$1_off$2');
		// hoge.gif -> hoge_off.gif
	this.disable();
};
$.StationChoose.Form.SubmitButton.prototype = {
	enable: function(){
		this.elem
			.css('cursor','pointer')
			.attr('src',this._path_on)
			.attr('alt','駅情報を表示');
	},
	disable: function(){
		this.elem
			.css('cursor','default')
			.attr('src',this._path_off)
			.attr('alt','駅名を選択してください');
	}
};

/**
 * $.PassageChoose
 */
$.PassageChoose = function(elem, options){
	this.options = $.extend({}, this.options, options);
	this.elem = $(elem);
};
$.PassageChoose.prototype = {
	options: {

		/* load bgiframe plugin and turn this true if you got any trouble with select element in IE6 */
		bgiframe: true,

		/* fadeSpeed */
		container_fadeIn_speed: 200,
		container_fadeOut_speed: 200,

		/* forceDirection */
		forceDirection: 'below', // 'above' or 'below'

		/* templates */
		template_altSelect: [''
			,'<a class="altSelect" href="#"></a>'
		].join(''),
		template_placeholder: [''
			,'<div class="placeholder"></div>'
		].join(''),
		template_container: [''
			,'<div class="stSearch_open">'
			,'	<div class="frame01"><div class="frame02"><div class="frame03">'
			,'		<p class="closeBtn"><a href="#" title="閉じる"><img src="/common/images/close_btn_01.gif" alt="閉じる" width="69" height="23" /></a></p>'
			,'		<div class="selectNameBoxWrap">'
			,'		</div>'
			,'	</div></div></div>'
			,'</div>'
		].join(''),
		container_selector_inner: '.selectNameBoxWrap',
		container_selector_close: '.closeBtn a',
		template_group: [''
			,'<div class="selectNameBox">'
			,'	<p class="ico"></p>'
			,'	<p class="line"></p>'
			,'	<ul class="list"></ul>'
			,'</div>'
		].join(''),
		group_selector_ico: '.ico',
		group_selector_line: '.line',
		group_selector_ul: '.list',
		template_item: [''
			,'<li>'
			,'	<a href="#"></a>'
			,'</li>'
		].join(''),

		swapButton_selector: 'a.swap'

	},
	_form: null,
	_selects: null,
	_swapButton: null,
	init: function(){
		this._selects = [];
		this._create();
		this._eventify();
		this._handleCache();
		return this;
	},
	_create: function(){
		this._form = new $.PassageChoose.Form(this.elem);
		this._create_selects();
		this._create_swapButton();
		return this;
	},
	_create_selects: function(){
		var selects = this._selects;
		var o = $.extend({}, this.options ,{ hasGroup: true });
		$('select.passageChooseSelect', this.elem).each(function(){
			var instance = new $.expandSelect.Select(this ,o);
			selects.push(instance);
		});
		return this;
	},
	_create_swapButton: function(){
		var selector = this.options.swapButton_selector;
		this._swapButton = new $.PassageChoose.SwapButton(
			$(selector, this.elem),
			this._selects[0],
			this._selects[1]
		);
	},
	_eventify: function(){
		var self = this;
		var form = this._form;
		$.each(this._selects, function(){
			var select = this;
			select.addChangeListener(function(){
				self._areAllSelectsChosen() ? form.allowSubmit() : form.forbidSubmit();
			});
			select.showAllGroups();
		});
	},
	_areAllSelectsChosen: function(){
		var res = true;
		$.each(this._selects, function(){
			res = !this.getVal() ? false : res;
		});
		return res
	},
	_handleCache: function(){
		/* backbutton or reload make previous option element selected */
		$.each(this._selects, function(){
			var select = this;
			setTimeout(function(){
				select.elem.trigger('change');
			},500);
		});
	}
};

/**
 * $.PassageChoose.SwapButton
 */
$.PassageChoose.SwapButton = function(elem, select1, select2){
	this.elem = $(elem);
	this._select1 = select1;
	this._select2 = select2;
	this._eventify();
};
$.PassageChoose.SwapButton.prototype = {
	elem: null,
	_select1: null,
	_select2: null,
	_eventify: function(){
		var self = this;
		this.elem.click(function(e){
			self.swap();
			e.preventDefault();
		});
	},
	swap: function(){
		var val1 = this._select1.getVal();
		var val2 = this._select2.getVal();
		this._select1.changeVal(val2);
		this._select2.changeVal(val1);
		return this;
	}
};


/**
 * $.PassageChoose.Form
 */
$.PassageChoose.Form = function(elem, options){
	this.options = $.extend({}, this.options, options);
	this.elem = $(elem);
	this._init();
	this._eventify();
	this.forbidSubmit();
};
$.PassageChoose.Form.prototype = {
	options: {},
	elem: null,
	_button: null,
	_submitAllowed: false,
	_init: function(){
		this._button = new $.PassageChoose.Form.Submit($(':image', this.elem));
		return this;
	},
	_eventify: function(){
		this.elem.submit( $.proxy(this._submitHandler, this) );
		return this;
	},
	_submitHandler: function(e){
		if(!this._submitAllowed){
			e.preventDefault();
		}
		return this;
	},
	allowSubmit: function(){
		this._submitAllowed = true;
		this._button.enable();
		return this;
	},
	forbidSubmit: function(){
		this._submitAllowed = false;
		this._button.disable();
		return this;
	}
};

/**
 * $.PassageChoose.Form.Submit
 */
$.PassageChoose.Form.Submit = function(elem){
	this.elem = $(elem);
	this.elem.rolloverImg();
	this._path_on = this.elem.attr('src');
	this._path_off = this._path_on.replace(/(^.+)(\.[^.]+)/,'$1_off$2');
		// hoge.gif -> hoge_off.gif
	this.disable();
};
$.PassageChoose.Form.Submit.prototype = {
	enable: function(){
		this.elem
			.css('cursor','pointer')
			.attr('src',this._path_on);
	},
	disable: function(){
		this.elem
			.css('cursor','default')
			.attr('src',this._path_off);
	}
};

/**
 * $.expandSelect
 * namespace
 */
$.expandSelect = {};

/**
 * $.expandSelect.Select
 */
$.expandSelect.Select = function(elem, options){
	this.options = $.extend({}, this.options, options);
	this._ob_change = new $.Observer();
	this.elem = $(elem);
	this._elem_original = this.elem.clone();
	this._createAltSelect();
	this._createPlaceholder();
	this._eventify();
};
$.expandSelect.Select.prototype = {
	options: {},
	_elem_original: null,
	_altSelect: null,
	_placeholder: null,
	_oldVal: null,
	_createAltSelect: function(){
		this._altSelect = new $.expandSelect.AltSelect(this, this.options);
		return this;
	},
	_createPlaceholder: function(){
		var o = this.options;
		var placeholder
			= this._placeholder
			= new $.expandSelect.Placeholder(this,o);
		placeholder.elem.insertAfter(this._altSelect.elem);
		return this;
	},
	_eventify: function(){
		var ob = this._ob_change;
		this.elem.change(function(){
			ob.notify();
		});
		return this;
	},
	_refreshElem: function(groupName){
		var select = this.elem;
		this._oldVal = select.val();
		var original = this._elem_original.clone();
		var option1st = $('> option', original);
		var optgroup = (groupName===undefined) ?
			$('optgroup', original):
			$('optgroup[label='+groupName+']', original);
		select.empty().append(option1st, optgroup);
		this._recallOldVal() || this._selectDefault();
		this.updateContainer();
		return this;
	},
	_chooseSelectWhoseValIs: function(val){
		var self = this;
		var option = $('option[value='+val+']', self.elem);
		if(!option.size()){
			return false;
		}
		/* ie got bugged when selected value was changed sometimes, so needs a little delay */
		setTimeout(function(){
			option.attr('selected', true);
			self.elem.trigger('change');
		},0);
		return true;
	},
	_selectDefault: function(){
		return this._chooseSelectWhoseValIs('');
	},
	_recallOldVal: function(){
		return this._chooseSelectWhoseValIs(this._oldVal);
	},
	updateContainer: function(){
		this._placeholder.updateContainer();
		return this;
	},
	addChangeListener: function(fn){
		this._ob_change.add(fn);
		return this;
	},
	getAltSelect: function(){
		return this._altSelect;
	},
	getContainer: function(){
		return this._placeholder.getContainer();
	},
	getSlectedOptionText: function(){
		return $('option:selected', this.elem).text();
	},
	getVal: function(){
		return this.elem.val();
	},
	changeVal: function(val){
		this.elem.val(val).trigger('change');
		return this;
	},
	enable: function(){
		this.elem.attr('disabled',false);
		this._altSelect.enable();
		return this;
	},
	disable: function(){
		this.elem.attr('disabled',true);
		this._altSelect.disable();
		return this;
	},
	narrowDownGroup: function(groupName){
		this._refreshElem(groupName);
		return this;
	},
	showAllGroups: function(){
		this._refreshElem();
		return this;
	},
	showAllItems: function(){
		this._placeholder.updateContainer();
		return this;
	},
	triggerChange: function(){
		this.elem.trigger('change');
		this._altSelect.focus();
	},
	setAltSelectText: function(text){
		this._altSelect.changeVal(text);
	}
};

/**
 * $.expandSelect.getIconImg
 */
$.expandSelect.getIconImg = function(lineName){
	function getSrc(n){
		var prefix = '/common/images/lineSign_ico_01';
		var suffix = '.gif';
		var body = null;
		switch (n){
			case '銀座線': body='g'; break;
			case '丸ノ内線': body='m'; break;
			case '日比谷線': body='h'; break;
			case '東西線': body='t'; break;
			case '千代田線': body='c'; break;
			case '有楽町線': body='y'; break;
			case '半蔵門線': body='z'; break;
			case '南北線': body='n'; break;
			case '副都心線': body='f'; break;
			default: body=null; break;
		}
		return (body===null) ? null : [prefix,body,suffix].join('');
	}
	var src = getSrc(lineName);
	if(src){
		return $('<img />',{
			alt: lineName,
			width: 29,
			height: 29,
			src: src
		});
	}
	return null;
};

/**
 * $.expandSelect.AltSelect
 */
$.expandSelect.AltSelect = function(selectInstance, options){
	this.options = $.extend({}, this.options, options);
	this._select = selectInstance;
	this._create();
	this._init();
	this._eventify();
};
$.expandSelect.AltSelect.prototype = {
	options: {
		template_altSelect: null,
		className_disabled: 'altSelect-disabled'
	},
	elem: null,
	_disabled: false,
	_select: null,
	_create: function(){
		this.elem = $(this.options.template_altSelect);
		return this;
	},
	_init: function(){
		this.elem.insertAfter(this._select.elem);
		this._select.elem.hide();
		this.changeVal(this._select.getSlectedOptionText());
		return this;
	},
	_eventify: function(){
		var self = this;
		var select = self._select;
		var elem_altSelect = self.elem;
		select.addChangeListener(function(){
			self.changeVal(select.getSlectedOptionText());
		});
		elem_altSelect.click(function(e){
			e.stopPropagation();
			e.preventDefault();
			elem_altSelect.focus();
			var container = select.getContainer();
			if(self._disabled){
				return;
			}
			if(container.isVisible()){
				container.hide();
			}else{
				$.containerCollection.hideAllContainers();
				container.show();
			}
		});
		return this;
	},
	changeVal: function(val){
		this.elem.text(val);
		return this;
	},
	disable: function(){
		this._disabled = true;
		this.elem.addClass(this.options.className_disabled);
		return this;
	},
	enable: function(){
		this._disabled = false;
		this.elem.removeClass(this.options.className_disabled);
		return this;
	},
	focus: function(){
		this.elem.focus();
	}
};

/**
 * $.expandSelect.Placeholder
 */
$.expandSelect.Placeholder = function(selectInstance, options){
	this.options = $.extend({}, this.options, options);
	this._select = selectInstance;
	this._create();
	//this._createContainer();
};
$.expandSelect.Placeholder.prototype = {
	options: {
		template_placeholder: null
	},
	elem: null,
	_select: null,
	_container: null,
	_anyGroupsCreated: false,
	_create: function(){
		this.elem = $(this.options.template_placeholder);
	},
	_createContainer: function(){
		var container
			= this._container
			= new $.expandSelect.Container(this._select, this, this.options);
		container.elem.hide().appendTo(this.elem);
		container.fixPNG();
		container.updateItemsOnOff();
		$.containerCollection.registerContainer(container);
		return this;
	},
	getContainer: function(){
		return this._container;
	},
	updateContainer: function(){
		var select = this._select;
		var oldContainer = this._container;
		if(oldContainer){
			$.containerCollection.discardContainer(oldContainer);
		}
		this.elem.empty();
		this._createContainer();
		return this;
	},
	tellAGroupWasCreated: function(){
		this._anyGroupsCreated = true;
	},
	createdAnyGroups: function(){
		return this._anyGroupsCreated;
	}
};

/**
 * $.expandSelect.Container
 */
$.expandSelect.Container = function(selectInstance, placeholderInstance, options){
	this.options = $.extend({}, this.options, options);
	this._placeholder = placeholderInstance;
	this._select = selectInstance;
	this._altSelect = selectInstance.getAltSelect();
	this._create();
	this._init();
	this._eventify();
	this._setupGroups();
	if(!this._groups){
		this._setupItems();
	}
};
$.expandSelect.Container.prototype = {
	options: {
		hasGroup: null,
		bgiframe: null,
		template_container: null,
		container_selector_frame01: null,
		container_selector_inner: null,
		container_selector_close: null,
		container_fadeIn_speed: 200,
		container_fadeOut_speed: 200,
		autoPositionAdjustment: true
	},
	elem: null,
	_elem_frame01: null,
	_elem_inner: null,
	_elem_close: null,
	_elem_mainArea: null,
	_groups: null,
	_items: null,
	_select: null,
	_altSelect: null,
	_placeholder: null,
	_mainOrigHeight: null,
	_win: $(window), // for reuse
	_doc: $(document), // for reuse
	_visible: false,
	_create: function(){
		var o = this.options;
		var e = this.elem = $(o.template_container);
		o.container = this;
		o.bgiframe && $.fn.bgiframe && e.bgiframe();
		this._elem_mainArea = $('#mainArea');
		this._elem_frame01 = $('div.frame01', e);
		this._elem_inner = $(o.container_selector_inner, e);
		this._elem_close = $(o.container_selector_close, e);
		$('img', this._elem_close).rolloverImg();
		return this;
	},
	fixPNG: function(){
		if(!$.browser.msie){ return; }
		if($.browser.version!=='6.0'){ return; }
		$('div.frame01, div.frame02, div.frame03', this.elem).each(function(){
			DD_belatedPNG.fixPng(this);
		});
	},
	_init: function(){
		this.hide();
		return this;
	},
	_eventify: function(){
		var self = this;
		self._eventify_outerClick();
		self._elem_close.click(function(e){
			e.preventDefault();
			self.hide();
		});
		$.keyDownObserver.observe({ key: 27, fn: $.proxy(self.hide, self) });
		$.keyDownObserver.start();
		self._select.addChangeListener(function(){
			self.updateItemsOnOff();
		});
	},
	_eventify_outerClick: function(){
		var self = this;
		self._elem_frame01.click(function(e){
			e.stopPropagation(); // but not inside
		});
		$('html').click(function(){
			if(self.elem.is(':hidden')){
				return;
			}
			self.hide();
		});
	},
	_setupGroups: function(){
		var self = this;
		var optgroups = $('optgroup', self._select.elem);
		if(!optgroups.size()){
			return self;
		}
		self._groups = [];
		if(!this._placeholder.createdAnyGroups()){
			// make a short delay to avoid freezing onload
			// dom creation occurs it.
			optgroups.each(function(i,current){
				setTimeout(function(){
					self.addGroup(current);
				},1.5*i);
			});
			setTimeout(function(){
				self._wrapLastGroup();
			},100);
			this._placeholder.tellAGroupWasCreated();
				// this would be executed only once.
		}else{
			optgroups.each(function(i,current){
				self.addGroup(current);
			});
			self._wrapLastGroup();
		}
		return self;
	},
	_wrapLastGroup: function(){
		var g = this._groups;
		var last = g[g.length-1];
		last.decorateAsLastBox();
	},
	_wrapLastItem: function(){
		var i = this._items;
		var last = i[i.length-1];
		last.decorateAsLastBox();
	},
	_createGroup: function(elem_optgroup){
		var o = this.options;
		var instanceOptions = {
			hasGroup: o.hasGroup,
			template_group: o.template_group,
			template_item: o.template_item,
			group_selector_ico: o.group_selector_ico,
			group_selector_line: o.group_selector_line,
			group_selector_ul: o.group_selector_ul
		};
		return new $.expandSelect.Group(elem_optgroup, this._select, this, instanceOptions);
	},
	addGroup: function(elem_optgroup){
		var group = this._createGroup(elem_optgroup);
		this._elem_inner.append(group.elem);
		this._groups.push(group);
	},
	_setupItems: function(){
		var self = this;
		self._items = [];
		$('option', this._select.elem).each(function(){
			if(!$(this).val()){
				return;
			}
			self.addItem(this);
		});
		if(!self.options.hasGroup){
			self._wrapLastItem();
		}
		return self;
	},
	_createItem: function(elem_option){
		var o = this.options;
		var instanceOptions = {
			hasGroup: o.hasGroup,
			template_item: o.template_item,
			container: this,
			select: this._select
		};
		var item = new $.expandSelect.Item(elem_option, instanceOptions);
		return item;
	},
	_adjustPosition: function(){
		var d = this.options.forceDirection;
		if(d === 'above'){
			this._putAbove();
		}else if(d === 'below'){
			this._putBelow();
		}else{
			if(this._canPutBelow()){
				this._putBelow();
			}else{
				if(this._canPutAbove()){
					this._putAbove();
				}else{
					if(this._expectedToOverThePageBottom()){
						this._expectedToOverThePageTop() ? 
							this._putBelowAndEnhanceMain():
							this._putAbove();
					}else{
						this._putBelow();
					}
				}
			}
		}
		return this;
	},
	_canPutAbove: function(){
		var spaceAbove = this._placeholder.elem.offset().top - this._win.scrollTop();
		return (spaceAbove > this.elem.height());
	},
	_canPutBelow: function(){
		var limit = this._win.scrollTop() + this._win.height();
		var expected = this._getExpectedBottomEdge();
		return (limit > expected);
	},
	_getExpectedBottomEdge: function(){
		return this._placeholder.elem.offset().top + this.elem.height();
	},
	_expectedToOverThePageTop: function(){
		return (this._placeholder.elem.offset().top < this.elem.height());
	},
	_expectedToOverThePageBottom: function(){
		var limit = this._doc.height();
		var expected = this._getExpectedBottomEdge();
		return (expected > limit);
	},
	_putAbove: function(){
		this.elem
			.css('top','auto')
			.css('bottom',
				this._placeholder.elem.innerHeight() + 
				this._altSelect.elem.innerHeight() + 5
			);
		return this;
	},
	_putBelow: function(){
		this.elem
			.css('bottom','auto')
			.css('top', 0);
		return this;
	},
	_putBelowAndEnhanceMain: function(){
		if(!this._elem_mainArea.size()){
			this._putBelow();
			return this;
		}
		var heightWeNeedMore = this._placeholder.elem.offset().top +
			this.elem.height() -
			$(document).height() +
			20;
		var main = this._elem_mainArea;
		var mainH = this._mainOrigHeight = main.height();
		main.height(mainH+heightWeNeedMore);
		return this;
	},
	addItem: function(elem_option){
		var item = this._createItem(elem_option);
		this._elem_inner.append(item.elem);
		this._items.push(item);
		return this;
	},
	getGroups: function(){
		return this._group;
	},
	_onShowClassChange: function(){
		// do this for z-index manipulation for Ie6,7
		if(!$.browser.msie){ return this; }
		var body = $(document.body);
		if(this.elem.parents('#mainArea').size()){
			body.addClass('mainComesFront');
		}else if(this.elem.parents('#asideArea').size()){
			body.addClass('sideComesFront');
		}
		return this;
	},
	show: function(){
		var e = this.elem;
		var s = this.options.container_fadeIn_speed;
		var adjust = this.options.autoPositionAdjustment;
		this._visible = true;
		adjust && this._adjustPosition();
		if($.support.opacity){
			e.fadeIn(s);
		}else{
			e.css('display','block');
		}
		$('body').delegate('a,input','focus', $.proxy(this._liveFocusHandler, this) );
		this._onShowClassChange();
		return this;
	},
	_liveFocusHandler: function(e){
		if(this.elem.has(e.target).size()){
			return;
		}else{
			this.hide();
		}
	},
	_onHideClassChange: function(){
		// do this for z-index manipulation for Ie6,7
		if(!$.browser.msie){ return this; }
		var body = $(document.body);
		if(this.elem.parents('#mainArea').size()){
			body.removeClass('mainComesFront');
		}else if(this.elem.parents('#asideArea').size()){
			body.removeClass('sideComesFront');
		}
		return this;
	},
	hide: function(){
		var e = this.elem;
		var s = this.options.container_fadeOut_speed;
		this._visible = false;
		if($.support.opacity){
			e.fadeOut(s);
		}else{
			e.css('display','none');
		}
		$('body').undelegate('a,input','focus', $.proxy(this._liveFocusHandler, this) );
		if(this._elem_mainArea.size() && this._mainOrigHeight){
			this._elem_mainArea.height(this._mainOrigHeight);
		}
		this._onHideClassChange();
		return this;
	},
	isVisible: function(){
		return this._visible;
	},
	_getAllItems: function(){
		var res = [];
		var groups = this._groups;
		var items = this._items;
		if(groups){
			$.each(groups, function(){
				$.merge(res, this.getItems());
			});
		}else{
			$.each(items, function(){
				res.push(this);
			});
		}
		return res;
	},
	updateItemsOnOff: function(){
		$.each(this._getAllItems(), function(){
			this.updateOnOff();
		});
	}
};

/**
 * $.expandSelect.Group
 */
$.expandSelect.Group = function(elem_optgroup, selectInstance, containerInstance, options){
	this.options = $.extend({}, this.options, options);
	this._elem_optgroup = $(elem_optgroup);
	this._container = containerInstance;
	this._select = selectInstance;
	this._create();
	this._setupItems();
};
$.expandSelect.Group.prototype = {
	options: {
		hasGroup: null,
		template_group: null,
		group_selector_ico: null,
		group_selector_line: null,
		group_selector_ul: null
	},
	_items: null,
	_elem_optgroup: null,
	elem: null,
	_container: null,
	_select: null,
	_create: function(){
		var o = this.options;
		var e = this.elem = $(o.template_group);
		var s_line = o.group_selector_line;
		var s_ico = o.group_selector_ico;
		var label = this._elem_optgroup.attr('label');
		var ico = $.expandSelect.getIconImg(label);
		$(s_line, e).text(label);
		$(s_ico, e).append(ico);
	},
	_setupItems: function(){
		var self = this;
		self._items = [];
		$('option', self._elem_optgroup).each(function(){
			self.addItem(this);
		});
	},
	_createItem: function(elem_option){
		var o = this.options;
		var instanceOptions = {
			hasGroup: o.hasGroup,
			template_item: o.template_item,
			group: this,
			container: this._container,
			select: this._select
		};
		var item = new $.expandSelect.Item(elem_option, instanceOptions);
		return item;
	},
	addItem: function(elem_option){
		var item = this._createItem(elem_option);
		var s = this.options.group_selector_ul;
		this._items.push(item);
		$(s, this.elem).append(item.elem);
	},
	getItems: function(){
		return this._items;
	},
	decorateAsLastBox: function(){
		this.elem.wrap('<div class="lastBox" />');
	}
};


/**
 * $.expandSelect.Item
 */
$.expandSelect.Item = function(elem_option, options){
	this.options = $.extend({}, this.options, options);
	this._elem_option = $(elem_option);
	this._select = this.options.select;
	this._container = this.options.container;
	this._create();
	this._eventify();
};
$.expandSelect.Item.prototype = {
	options:{
		hasGroup: null,
		template_item: null,
		item_className_on: 'on'
	},
	elem: null,
	_elem_a: null,
	_elem_option: null,
	_select: null,
	_container: null,
	_create: function(){
		var o = this.options;
		var e = this.elem = $(this.options.template_item);
		var a = this._elem_a = $('a', e);
		var text = this._elem_option.text();
		if(o.hasGroup){
			a.eq(0).text(text);
		}else{
			var icon = $.expandSelect.getIconImg(text);
			if(icon===null){
				$('.ico', e).remove();
			}else{
				a.eq(0).html(icon);
			}
			a.eq(1).text(text);
		}
	},
	_eventify: function(){
		var a = this._elem_a;
		var select = this._select;
		var option = this._elem_option;
		var container = this._container;
		a.click(function(e){
			e.preventDefault();
			container.hide();
			/* ie got bugged when selected value was changed sometimes, so needs a little delay */
			setTimeout(function(){
				option.attr('selected', true);
				select.triggerChange();
			},0);
		});
	},
	on: function(){
		var c = this.options.item_className_on;
		this.elem.addClass(c);
		return this;
	},
	off: function(){
		var c = this.options.item_className_on;
		this.elem.removeClass(c);
		return this;
	},
	updateOnOff: function(){
		var option = this._elem_option;
		option.is(':selected') ? this.on() : this.off();
		return this;
	},
	focus: function(){
		this._elem_a.eq(0).focus();
	},
	decorateAsLastBox: function(){
		this.elem.wrap('<div class="lastBox" />');
	}
};

/**
 * $.Observer
 */
$.Observer = function(){
	this.subscribers = [];
};
$.Observer.prototype = {
	add: function(fn){
		this.subscribers.push(fn);
	},
	notify: function(){
		var self = this;
		var args = arguments;
		$.each(self.subscribers, function(i, fn){
			fn.apply(null,args);
		});
	},
	empty: function(){
		this.subscribers.length = 0;
	}
};

/**
 * $.fn.rollover
 */
$.fn.rolloverImg = function(){
	this.each(function(){
		var img = $(this);
		var path_off = img.attr('src');
		var path_on = path_off.replace(/(^.+)(\.[^.]+)/,'$1_o$2');
			// hoge.gif -> hoge_o.gif
		var preloadImg = $('<img />').attr('src', path_on);
		img.bind('mouseover focus', function(){
			if(img.attr('src')!==path_off){
				return;
			}
			img.attr('src', path_on);
		}).bind('mouseout blur', function(){
			if(img.attr('src')!==path_on){
				return;
			}
			img.attr('src', path_off);
		});
	});
	return this;
};


/**
 * $.keyDownObserver
 */
$.keyDownObserver = new (function(){
	this._started = false;
	this._lastKey = null;
	this._items = [];
	this._find = function(key){
		var res = [];
		$.each(this._items, function(){
			if(this.key === key){
				res.push(this.fn);
			}
		});
		return res;
	}
	this.start = function(){
		var self = this;
		if(this._started){
			return;
		}
		$($.browser.msie ? 'body' : window).keydown(function(e){
			$.each(self._find(e.keyCode), function(){
				this();
			});
		});
	};
	this.observe = function(options){
		var item = {
			key: options.key,
			fn: options.fn
		};
		this._items.push(item);
		return item;
	};
})();


})($jq['base']); // end $=jQuery encapsulation

