1 
  2 /**
  3  * @name	CeL 下拉式表單 function
  4  * @fileoverview
  5  * 本檔案包含了下拉選擇式表單的 functions。
  6  * @since	
  7  */
  8 
  9 /*
 10 TODO:
 11 HTML 5 <datalist> Tag
 12 date
 13 http://plugins.jquery.com/project/timepicker
 14 http://digitalbush.com/projects/masked-input-plugin/
 15 理想:
 16 http://gs.statcounter.com/
 17 
 18 
 19 http://plugins.jquery.com/search/node/Autocomplete+type%3Aproject_project
 20 http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
 21 	http://jsgears.com/thread-114-1-1.html
 22 
 23 set focus/blue background-image instead of HTML 5 placeholder text
 24 	http://dev.w3.org/html5/spec/Overview.html#the-placeholder-attribute
 25 	The placeholder attribute represents a short hint (a word or short phrase) intended to aid the user with data entry.
 26 e.g., background-image: url(http://www.google.com/cse/intl/en/images/google_custom_search_watermark.gif); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: rgb(255, 255, 255); background-position: 0% 50%; background-repeat: no-repeat no-repeat;
 27 or
 28 http://perldoc.perl.org/
 29 usually show a <div>. show <input> only at focus.
 30 
 31 
 32 http://miketaylr.com/pres/html5/forms2.html
 33 http://people.opera.com/brucel/demo/html5-forms-demo.html
 34 http://www.erichynds.com/examples/jquery-multiselect/examples.htm
 35 http://x.wawooo.com/archives/891
 36 
 37 http://www.google.com.tw/dictionary
 38 鍵盤選擇時同時改變值
 39 */
 40 
 41 if (typeof CeL === 'function'){
 42 
 43 /**
 44  * 本 module 之 name(id),<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。
 45  * @type	String
 46  * @constant
 47  * @inner
 48  * @ignore
 49  */
 50 var module_name = 'net.form.select_input';
 51 
 52 //===================================================
 53 /**
 54  * 若欲 include 整個 module 時,需囊括之 code。
 55  * @type	Function
 56  * @param	{Function} library_namespace	namespace of library
 57  * @param	load_arguments	呼叫時之 argument(s)
 58  * @return
 59  * @name	CeL.net.form.select_input
 60  * @constant
 61  * @inner
 62  * @ignore
 63  */
 64 var code_for_including = function(library_namespace, load_arguments) {
 65 
 66 //	requires
 67 if (eval(library_namespace.use_function(
 68 		'net.web.get_node_offset,net.web.parse_URI')))
 69 	return;
 70 
 71 library_namespace.include_module_resource('select_input.css',module_name);
 72 
 73 /**
 74  * 簡易型 net.web.XML_node @ net.form.select_input
 75  * @param tag	p.appendChild tag
 76  * @param p	parent node
 77  * @param t	text
 78  * @param classN	className
 79  * @inner
 80  * @ignore
 81  * @return
 82  */
 83 var create_DO = function(tag, p, t, classN) {
 84 	var _e;
 85 	if (t && (typeof t != 'object' || (t instanceof Object)))
 86 		t = document.createTextNode(t + '');
 87 	if (typeof tag == 'object') {
 88 		_e = tag;
 89 	} else if (tag) {
 90 		_e = document.createElement(tag);
 91 		if (classN)
 92 			_e.className = classN;
 93 		if (t)
 94 			_e.appendChild(t);
 95 	} else if (t)
 96 		_e = t;
 97 	if (p && _e)
 98 		p.appendChild(_e);
 99 	return _e;
100 };
101 
102 
103 /**
104  * get scrollbar height
105  * @return
106  * @since	2008/9/3 23:31:21
107  * @inner
108  * @ignore
109  * @see
110  * http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php
111  * lazy evaluation
112  * http://peter.michaux.ca/articles/lazy-function-definition-pattern
113  */
114 function scrollbar_width() {
115 	var _f = scrollbar_width;
116 	if (!_f.w) {
117 		var w, p = create_DO('div', document.body), c = create_DO('div', p, ' '), s = p.style;
118 		s.width = s.height = '80px';
119 		// 有時沒這行才出得來
120 		// c.style.width='100%';
121 		s.overflow = 'hidden';
122 		w = c.offsetWidth;
123 		s.overflow = 'scroll';
124 		_f.w = w - c.offsetWidth;
125 		// sl('scrollbar_width: '+w+'-'+c.offsetWidth+'='+_f.w);
126 		document.body.removeChild(p);
127 	}
128 	return _f.w;
129 }
130 
131 
132 /**
133  * scroll 到可以看到 object
134  * TODO:
135  * 考慮可能沒 scrollbar
136  * 包括橫向
137  * @param o	object
138  * @param [p]	parentNode to scroll
139  * @return
140  * @since	2008/9/3 23:31:29
141  * @inner
142  * @ignore
143  */
144 function scroll_to_show(o, p) {
145 	if (!p) {
146 		p = o;
147 		while ((p = p.parentNode) && p.offsetHeight == p.scrollHeight)
148 			;
149 	}
150 	//sl('scroll_to_show: '+p.scrollTop+', '+p.scrollHeight+', '+p.offsetHeight+', '+o.offsetTop);
151 
152 	var s, a;
153 	if (a = o.offsetTop, a < p.scrollTop)
154 		s = a;
155 	else if (a = o.offsetTop + o.offsetHeight - p.offsetHeight
156 			+ scrollbar_width(), a > p.scrollTop)
157 		if (s = a, a = o.offsetTop, a < s)
158 			s = a;
159 
160 	if (!isNaN(s))
161 		p.scrollTop = s;
162 }
163 
164 
165 /*
166 
167 {
168 	title: '',
169 	name: '',
170 	container: 'id' | obj,
171 	list: [] | {} | {group1:{}, group2:[],.. },
172 	default: '' | [],
173 	type: 'select' | 'radio' | 'checkbox',
174 }
175 
176 return <select>
177 
178 
179 TODO:
180 複選 <select>
181 <radio>
182 <checkbox>
183 +<label>
184 autocomplete: 假如所有備取 list 都有一樣的 prefix,則自動完成。
185 	把後面的用反白自動完成。
186 
187 在 list 上下安排三角,onmouseover 即可自動滾動。
188 
189 color panel
190 
191 http://www.itlearner.com/code/js_ref/choi3.htm
192 selectName.options[i]=new Options("option_value","option_Text", defaultSelected, selected);
193 */
194 
195 /**
196  * container object, list
197  * @param o
198  * @param l
199  * @return
200  * @inner
201  * @ignore
202  */
203 function menu_creater(o, l) {
204 
205 };
206 
207 
208 
209 //	===================================================
210 
211 var
212 
213 //	class private	-----------------------------------
214 
215 
216 /*	可紀錄的 set class name,不過對大多數人來說,更常用的是 instance.setClassName
217 
218 usage:
219 (item[, obj])		set obj to className item, return real className that setted
220 (0,'prefix')	set prefix & 重設(全部重跑)
221 */
222 setClassName=function(i,o,noRec){	//	(0, prefix) or (item, object)
223  var _t=this,s=_.classNameSet;
224  if(!_t.settedClass)_t.settedClass=[];
225 
226  if(!o||typeof o==='object'){
227   //	設定並回傳 className
228   //sl('setClassName: test '+'class_'+i+': '+('class_'+i in _t?'<em>YES</em>':'none'));
229   s=[	i in s?
230 		s[i].charAt(0)==='~'?s.prefix+s[i].slice(1):
231 		s[i]:
232 	''
233   ];
234   if('class_'+i in _t)
235    if(i==='error'||i==='warning')s.unshift(_t['class_'+i]);
236    else s.push(_t['class_'+i]);
237   s=s.join(' ');
238   //sl('setClassName: set '+o+(s?' to ['+s+']':', <em>There is no ['+i+'] in classNameSet or instance set.</em>'));
239   if(o && (o.className=s,!noRec))
240    _t.settedClass.push(s,o);
241   return s;
242  }
243 
244  s.prefix=o;
245  o=_t.settedClass;
246  //	重設(全部重跑)
247  for(var i=0;i<o.length;i++)
248   _f.call(_t,o[0],o[1],1);
249 },
250 
251 funcButton=function(_t,t,f,title){	//	add text t, function f to instance _t
252  var _p=pv(_t),o=create_DO('span',_p.listO,'['),b;
253  setClassName.call(_t,'functionB',o);
254  b=create_DO('span',o,t);
255  setClassName.call(_t,'functionT',b);
256  b.title=title,b.onclick=f;
257  create_DO(0,o,']');
258  return b;
259 },
260 
261 //	簡易型
262 removeAllChild=function(o){
263  o.innerHTML='';
264  return o;
265 },
266 
267 //	show/hide list
268 showList=function(show){	//	():get, 0:hide, 1:show
269  var _t=this,_p=pv(_t),o=_p.listO,s,c=0;
270 
271  if(!o)return;
272  s=o.style;
273  if(show){
274   c=get_node_offset(_p.inputO);
275   s.top = c.top + c.height + 2 + 'px';
276   s.left=c.left+'px';
277 
278   s.width=c.width+'px';
279   s.height='';	//	reset
280   c=s.display='block';
281   if(_t.maxListHeight&&o.offsetHeight>_t.maxListHeight)
282    s.height=_t.maxListHeight+'px';
283  }else if(typeof show!='undefined')
284   c=s.display='none';
285 
286  if(c)
287   create_DO(0,removeAllChild(_p.arrowO),_.textSet[c!='none'?'hideList':'showList']);
288  else c=s.display;
289 
290  return c!='none';
291 },
292 
293 
294 /*	準備選擇清單的某一項
295 TODO:
296 自動完成
297 到最後若可能自動轉到全部
298299 */
300 cK=0,	//	control key pressed
301 readyTo=function(e,o){
302  if(!e)e=event;
303  var _t=this,_p=pv(_t),c,gI=function(o){
304   return o&&/*(can_use_special_attribute?o.getAttribute("sIndex"):o.sIndex)*/o.sIndex;
305  };
306  //sl('readyTo: '+e.type+', key: '+(e.keyCode||e.which||e.charCode)+', _p.listA: '+(_p.listA&&_p.listA.length));
307 
308  if(!_p.listA||!_p.listA.length)return;
309 
310  if(e.type==='mouseover'||e.type==='mouseout'){
311   if(_p.readyItem)setClassName.call(_t,'item',_p.readyItem,0);
312   if(e.type==='mouseover')c='item_select',_p.readyItem=o;
313   else if(c='item',_p.readyItem===o)_p.readyItem=0;
314  //	需更改 _p.inputO.onkeyup 以防止重新 list!!
315  }else if(c=e.keyCode||e.which||e.charCode,c==13){
316   if(_p.readyItem){
317    //sl('readyTo: 以鍵盤選擇: '+_p.readyItem.innerHTML);
318    cK=c,_p.readyItem.onclick();	//	用 .click() 無效!
319    return false;
320   }else return;
321  //	key input 用鍵盤控制上下	←↑→↓: 37~40
322  }else if(c==38||c==40){
323   cK=c;
324   o=_p.readyItem;
325   //sl('readyTo: 以鍵盤移至: '+(o&&(o.getAttribute("sIndex")+','+o.sIndex)));
326   if(!o)o=_p.listA[c==40?0:_p.listA.length-1];
327   else{
328    //if(!o.getAttribute)throw 1;	IE 可用 getAttribute,FF 或許在 appendChild 之後屬性重設?,得用 o.sIndex
329    c=gI(o)+(c==38?-1:1);
330    if(c<0||c>=_p.listA.length)return;
331 
332    setClassName.call(_t,'item',o,0);
333    o=_p.listA[c];
334   }
335   _p.readyItem=o;
336 
337   scroll_to_show(o,_p.listO);
338   c='item_select';
339  }else if(c==35||c==36){	//	35: End, 36: Home
340   cK=c;
341   if(o=_p.readyItem)setClassName.call(_t,'item',o,0);
342   _p.readyItem=o=_p.listA[c==36?0:_p.listA.length-1];
343   scroll_to_show(o,_p.listO);
344   c='item_select';
345  }else if(c==33||c==34){	//	33: PageUP, 34: PageDown
346   cK=c;
347   o=_p.readyItem;
348   if(!o)return;
349   setClassName.call(_t,'item',o,0);
350   var i=gI(o),t;
351   if(c==33){
352    t=_p.listO.scrollTop-1;
353    while(i&&_p.listA[i-1].offsetTop>t)i--;
354   }else{
355    t=_p.listO.scrollTop+_p.listO.offsetHeight-scrollbar_width();
356    while(i<_p.listA.length-1&&_p.listA[i+1].offsetTop<t)i++;
357   }
358   //sl('readyTo: Page: '+i+', top: '+t+', scroll: '+_p.listO.scrollTop);
359   if(i==gI(o))
360    if(c==33){
361     t-=_p.listO.offsetHeight;
362     if(t<2)i=0;
363     else while(i&&_p.listA[i-1].offsetTop>t)i--;
364    }else{
365     t+=_p.listO.offsetHeight;
366     while(i<_p.listA.length-1&&_p.listA[i+1].offsetTop<t)i++;
367    }
368   //sl('readyTo: Page: '+i+', top: '+t+', height: '+_p.listO.offsetHeight);
369   _p.readyItem=o=_p.listA[i];
370   scroll_to_show(o,_p.listO);
371   c='item_select';
372  }else return;
373 
374  setClassName.call(_t,c,o,0);
375  return false;
376 },
377 
378 //can_use_special_attribute,
379 
380 //	顯示清單的工具函數
381 setList=function(l,force,limit,f){
382  var _t=this,_p=pv(_t),i,c=0,k,o;
383  if(isNaN(limit))limit=isNaN(_t.maxList)?_.maxList:_t.maxList||Number.MAX_VALUE;
384  if(!f)f=function(l,i){
385   var a=_t.onList(l,i),o;
386   if(!a)return;
387   o=create_DO('div',0,a[0]);
388   setClassName.call(_t,'item',o);
389   o.title=a[1];
390   k=a[2]||a[1];
391   o.onmouseover=o.onmouseout=function(e){readyTo.call(_t,e,o);};
392   o.onclick=function(){var v=_t.onSelect(l,i);_t.setValue(v);_t.onInput(v);};
393 
394   //	這邊本來放在下面 for 的地方
395   c++,_p.listO.appendChild(o);
396   //if(!can_use_special_attribute){o.setAttribute("sIndex",1);can_use_special_attribute=o.getAttribute("sIndex")?1:-1;}
397   //if(can_use_special_attribute==1)o.setAttribute("sIndex",_p.listA.length);else o.sIndex=_p.listA.length;
398   o.sIndex=_p.listA.length;//o.setAttribute("sIndex",o.sIndex=_p.listA.length);
399   _p.listA.push(o);
400 
401   return o;
402  };
403 
404  //_t.showList(0);
405 
406  _p.listO=removeAllChild(_p.listO),_p.listA=[],_p.readyItem=0;
407  if(l instanceof Array){
408   for(i=0;i<l.length&&c<limit;i++)
409    f(l,i);
410  }else
411   for(i in l)
412    if(c<limit){
413     f(l,i);
414    }else break;
415 
416  //sl('setList: list '+c+' items, key '+k+'=?'+_t.setValue());
417  if(c==1&&_t.setValue()==k)c=0;	//	僅有一個且與 key 相同
418  if(!force&&!c)return;	//	無 list
419 
420  //	add function
421  if(c!=_t.allListCount)funcButton(_t,_.textSet.allBtn,function(){_t.doFunc=1;_t.focus();_t.setList(_t.setAllList(),1,Number.MAX_VALUE);},'顯示全部 '+_t.allListCount+' 列表。');
422  if(_t.setValue())funcButton(_t,_.textSet.clearBtn,function(){_t.doFunc=2;_t.focus();_t.onInput(_t.setValue(''));},'清除輸入內容,重新列表。');
423  funcButton(_t,_.textSet.closeBtn,function(){_t.doFunc=3;_t.showList(0);},'close menu \n關閉列表');
424 
425  showList.call(_t,1);
426  return _t.listCount=c;
427 },
428 
429 //	return verify 之後的 key(<input>) 值
430 do_verify=function(k){
431  var _t=this,c=_t.verify(k||_t.setValue());
432  //sl('do_verify: input status: '+(c==1?'warning':c==2?'error':'OK'));
433 
434  if(typeof c==='string')_t.setValue(k=c);	//	可以設定 key 值!
435  else setClassName.call(_t,c==1?'warning':c==2?'error':'input',pv(_t).inputO,1);
436 
437  return k;
438 },
439 
440 //	簡易設定常用的 onInput 型式
441 searchInList=function(f,o){	//	o: 傳入 (list, index, key)
442  var _t=this;
443  if(typeof f==='string'&&(f in _.searchFunctionSet))f=_.searchFunctionSet[f];
444 
445  //	因為允許傳入 list,所以不能在這邊用 _t.setAllList() 判別函數,而得要寫一個泛用的。
446  return _t.onInput=function(k,L,force){
447   //sl('searchInList, onInput: search ['+k+'] use '+f);
448 
449   if(!L)L=_t.setAllList();
450   k=do_verify.call(_t,k||'');
451 
452   var l,i;
453 
454   //sl('searchInList: search '+(L instanceof Array?'array':'object'));
455   if(L instanceof Array){
456    l=[];//new L.constructor();
457    for(i=0;i<L.length;i++)
458     if(o?f(L,i,k):L[i]&&f(L[i],k))l.push(L[i]);	//	search value
459   }else{
460    l={};
461    for(i in L)
462     if(o?f(L,i,k):i&&f(i,k)||L[i]&&f(L[i],k))l[i]=L[i];	//	search key+value
463   }
464   _t.setList(l,force);
465  };
466 
467 },
468 
469 
470 /**
471  * 切換 [input] / inputted [span]
472  * @param {Boolean|undefined} to_input	切換至 input or not. default: 切換至 [input]
473  * @return
474  * @private
475  * @inner
476  * @ignore
477  */
478 triggerToInput = function(to_input) {
479 	var _t = this, _p = pv(_t);
480 	if (to_input || typeof to_input === 'undefined') {
481 		//	to <input>
482 		_p.inputtedO.style.display = 'none';
483 		if (_t.allListCount)
484 			_p.arrowO.style.display = 'inline';
485 		_p.inputO.style.display = 'inline';
486 		return 1;
487 	} else {
488 		//	to inputted <span>
489 		_t.showList(0);
490 		_t.setInputted();
491 
492 		if (to_input = library_namespace.get_style
493 				&& parseInt(library_namespace.get_style(_p.inputO, 'width'))){
494 			//library_namespace.debug(to_input);
495 			//	TODO: +16, +10: magic number
496 			try {
497 				//	.get_style(_p.arrowO, 'width') 可能回傳 'auto' @ IE8
498 				_p.inputtedO.style.width = (to_input+parseInt(library_namespace.get_style(_p.arrowO, 'width'))+16)+'px';
499 				_p.inputtedO.style.height = (parseInt(library_namespace.get_style(_p.inputO, 'height'))+10)+'px';
500 			} catch (e) {
501 				// TODO: handle exception
502 			}
503 		}
504 
505 		_p.arrowO.style.display = _p.inputO.style.display = 'none';
506 		if(!_p.inputtedO.innerHTML)
507 			_p.inputtedO.innerHTML=' ';
508 
509 		//_p.inputtedO.style.border = '3px #aaa dotted';
510 		_p.inputtedO.style.display = to_input ? 'inline-block' : 'inline';
511 	}
512 },
513 
514 //	TODO: http://blog.xuite.net/sugopili/computerblog/17695447
515 set_source=function(URL){
516 	;
517 },
518 
519 /*	配置元件
520 
521 本函數會配置/增加:
522 <div>		.container
523 	<input>	.inputO
524 	<span>	.inputtedO
525 	<span>	.arrowO
526 	<div>	.listO
527 
528 arguments:
529 <input> 會被當作 inputO 主元件
530 <select> 會被當作選項
531 others: container
532 */
533 layout=function(o){
534  var _t=this,_p=pv(_t),t;
535 
536  if(typeof o!=='object')
537   o=document.getElementById(o);
538 
539  if(!o || (o.tagName.toLowerCase() in {hr:1,br:1}))return;	//	** 這邊應該檢查 o 是不是 <hr/> 等不能加 child 的!
540 
541  //library_namespace.debug(('layout: use <'+o.tagName+(o.id?'#'+o.id:'')+'>: '+o.innerHTML).replace(/</g,'<'));
542 
543  //	TODO: 這邊應該有一個更完善的刪除策略
544  if(_t.loaded){
545   t=_p.container;
546   //	不必多做功,已經達到所需配置了。
547   if(t===o.parentNode)return;
548   for(var i=0,e='inputO,inputtedO,arrowO,listO'.split(',');i<e.length;i++)
549    //library_namespace.debug('layout: removeChild '+e[i]),
550    _p[e[i]].parentNode.removeChild(_p[e[i]]);//t.removeChild(_p[e[i]]);
551   if(!t.childNodes.length)t.parentNode.removeChild(t);
552  }
553 
554 
555  //	依照各種不同的傳入 object 作出應對
556  t=o.tagName.toLowerCase();
557 
558  if(t==='input'){
559 
560   try{
561    //	http://www.w3.org/TR/html5/forms.html#the-pattern-attribute
562    //	http://www.whatwg.org/specs/web-apps/current-work/#attr-input-pattern
563    //	http://www.w3school.com.cn/html5/html5_input.asp
564    t=o.pattern||
565 	//o.getAttribute&&
566 	o.getAttribute('pattern');
567    //	compiled as a JavaScript regular expression with the global, ignoreCase, and multiline flags disabled
568    //	somewhat as if it implied a ^(?: at the start of the pattern and a )$ at the end
569    if(t&&(t=new RegExp('^('+t+')?$')))
570     //library_namespace.debug('set verify pattern of ['+o.id+']: '+t),
571     _t.set_verify(t);
572   }catch(e){
573    library_namespace.err('error pattern: ['+t+']');
574    library_namespace.err(e);
575   }
576 
577   o.parentNode.insertBefore(
578 	t=_p.container=create_DO('span'),
579 	_p.inputO=o
580 	);
581   setClassName.call(_t,'container',t);
582 
583   if(!o.className)setClassName.call(_t,'input',o);
584 
585   t.appendChild(o.parentNode.removeChild(o));
586 
587   o=t;
588 
589  }else if(t==='select'){
590   o.parentNode.insertBefore(t=_p.container=create_DO('span'),o);
591 
592   _p.inputO=create_DO('input',t);
593   setClassName.call(_t,'input',_p.inputO);
594   _p.inputO.name=o.name;
595   if(o.selectedIndex>=0)_p.inputO.value=o.options[o.selectedIndex].value;
596 
597   var l={},opt=o.options,i=0;
598   for(;i<opt.length;i++)
599    l[opt[i].value||opt[i].innerHTML]=opt[i].innerHTML;
600 
601   //	list setting
602   _t.setAllList(l);
603 
604   o.parentNode.removeChild(removeAllChild(o));
605 
606   o=t;
607 
608  }else{
609   _p.container=o;	//	容器
610   if(!o.className)setClassName.call(_t,'container',o);
611 
612   _p.inputO=create_DO('input',o);
613   setClassName.call(_t,'input',_p.inputO);
614 
615  }
616 
617 
618  //	補足其他的設定
619  _p.inputO.setAttribute("autocomplete","off");
620 
621  _p.inputtedO=create_DO('span',o);
622  setClassName.call(_t,'inputted',_p.inputtedO);
623  _p.inputtedO.style.display='none';
624 
625  _p.inputtedO.onclick=function(){
626   _t.clickNow=1;
627   _t.triggerToInput();
628   _p.inputO.focus();
629   _t.clickNow=0;
630  };
631 
632  (_p.arrowO=create_DO('span',o,_.textSet.showList))
633   .title=_.textSet.arrowTitle;
634  setClassName.call(_t,'arrow',_p.arrowO);
635 
636  _p.listO=create_DO('div',o);
637  _p.arrowO.onmouseover=_p.listO.onmouseover=function(){_t.clickNow=1;};
638  _p.arrowO.onmouseout=_p.listO.onmouseout=function(){_t.clickNow=0;};
639  setClassName.call(_t,'list',_p.listO);
640  _t.showList(0);
641 
642 
643  // event setting
644  //_p.inputO.onmouseover=
645  _p.inputO.onkeydown=function(e){readyTo.call(_t,e);};
646  _p.inputO.onmouseup=_p.inputO.onkeyup=_p.inputO.ondragend=function(e){
647   if(!e)e=event;
648   var c=e.keyCode||e.which||e.charCode;
649   //sl('up: '+e.type+', key: '+c+', _p.listA: '+(_p.listA&&_p.listA.length));
650   if(cK&&cK==c){cK=0;return false;}
651   //	Esc
652   if(c==27){_t.showList(0);return false;}
653   _t.clickNow=1;_t.onInput(_t.setValue());
654  };
655  _p.inputO.onmouseout=function(){_t.clickNow=0;};
656  if(_p.inputO.addEventListener)_p.inputO.addEventListener('dragdrop',_p.inputO.ondragend,false);
657  //if(window.addEventListener)window.addEventListener('click',function(){_t.showList(0);},true);
658  //addListener(0,'click',function(){sl('close..');_t.showList(0);sl('close done.');})
659  _p.inputO.onblur=function(){
660   //if(_t.verify(_t.setValue())==2){alert('Wrong input!');return false;}	//	這在 Firefox 似乎沒啥效果..
661 /*	設定這項在按 _p.arrowO 的時候會出問題,所以建議在其他地方自訂。
662   if(_t.setValue() && (_t.setValue() in _t.setAllList()))
663    _t.triggerToInput(0);
664 */
665   //	TODO: 假如以鍵盤離開,應該也 showList(0);
666   //library_namespace.debug('clickNow='+_t.clickNow,1,'_p.inputO.onblur');
667   if(_t.clickNow)_t.clickNow=0;
668   else _t.showList(0);
669  };
670 
671  //	show/hide by press arrow
672  _p.arrowO.onclick=function(){
673   //sl('arrowO.onclick start');
674   _t.clickNow=1;
675   if(_t.showList())
676    //	正在顯示就把他關起來
677    _t.showList(0);
678   else
679    //	沒在顯示就把他開起來: setValue 設定完 list,onInput 模擬 key-down
680    _t.onInput(_t.setValue(),0,1);
681   _t.clickNow=0;
682   //sl('arrowO.onclick end');
683  };
684  // ondblclick: double click
685  //_p.inputO.ondblclick=function(){_t.onInput(_p.inputO.value,0,1);};
686 
687 
688  _t.loaded=1;	//	isLoaded
689 
690 },
691 
692 //	instance constructor	---------------------------
693 instanceL=[],
694 initI=function(o,l,s){	//	(HTML object, list: Array or Object)
695  var _t=this,_p;
696  // objects setting
697  if(typeof o!='object')
698   //library_namespace.debug('Use object ['+o+']'),
699   o=document.getElementById(o);
700 
701  _p=pv(_t);	//	also do initial
702  instanceL.push(_t);	//	for destructor
703 
704  if(o)
705 	layout.call(this,o);
706 /*
707  else{
708   //throw new Error(1,'Can not get document object'+(o?' ['+o+']':'')+'!');
709   return;
710  }
711 */
712 
713  //	list setting
714  if(l&&!_t.allListCount)_t.setAllList(l);
715 
716  if(_p.arrowO)
717   _p.arrowO.style.display=_t.allListCount?'inline':'none';	//	無 list 的話先不顯示,等有 list 再說。
718 
719  //	setup default inputted value
720  _t.dInputted=_t.setValue;
721 
722  if(s)
723   _t.setSearch(s);
724  //return _t;
725 };
726 
727 
728 //===================================================
729 
730 /*
731 
732 _=this
733 
734 TODO:
735 浮水印 background-image:url();
736 
737 
738 HISTORY:
739 2008/7/22 0:38:14	create
740 7/27 22:55:18	verify()
741 8/7 21:18:47	attach()
742 */
743 /**
744 * 提供有選單的  input
745 * @class	form 的 functions
746 * @see
747 * http://dojocampus.org/explorer/#Dijit_Form%20Controls_Filtering%20Select_Basic
748 */
749 CeL.net.form.select_input
750 =function(){initI.apply(this,arguments);load_arguments&&load_arguments.apply(this,arguments);},
751 
752 //	(instance private handle)	不要 instance private 的把這函數刪掉即可。
753 _p='_'+(Math.random()+'').replace(/\./,''),
754 //	get private variables (instance[,destroy]), init private variables (instance[,access function list[, instance destructor]])
755 pv=function(i,d,k){var V,K=_p('k');return arguments.callee.caller===_p('i')?(V=_p(i[K]=_p()),V.O=i,V.L={}):(K in i)&&(V=_p(i[K]))&&i===V.O?d?(_p(i[K],1),delete i[K]):V.L:{};};
756 
757 //	(for inherit)	不要 inherit 的把這段刪掉即可。
758 //(_.clone=arguments.callee).toString=function(){return '[class_template]';};
759 
760 
761 //	class destructor	---------------------------
762 /*
763 please call at last (e.g., window.unload)
764 
765 usage:
766 classT=classT.destroy();
767 or if you has something more to do:
768 classT.destroy()&&classT=null;
769 */
770 
771 _.destroy=function(){for(var i=0;i<instanceL.length;i++)instanceL[i].destroy();_p();};
772 
773 //	(instance private handle, continue)
774 eval('_p=(function(){var '+_p+'={a:pv,d:_.destroy,c:0,k:"+pv+'+Math.random()+'",i:initI};return function(i,d){var f=arguments.callee.caller;if(f==='+_p+'.a){if(!d)return i in '+_p+'?'+_p+'[i]:('+_p+'[i='+_p+'.c++]={},i);'+_p+'[i]={};}if(f==='+_p+'.d)'+_p+'={};}})();');
775 _p.toString=function(){return'';};
776 
777 
778 /*
779 //	測試是否可用自訂之屬性
780 var o=document.createElement('div');
781 o.setAttribute('testA',2);
782 can_use_special_attribute=o.getAttribute('testA');
783 sl('can_use_special_attribute: '+can_use_special_attribute);
784 */
785 
786 //	class public interface	---------------------------
787 
788 
789 //	預設清單最大顯示數
790 _.maxList=10;
791 
792 
793 //	searchInList 常用到的函數
794 _.searchFunctionSet = {
795 	allTheSame : function(i, k) {
796 		return (i + '') === k;
797 	},
798 	startWith : function(i, k) {
799 		return (i + '').slice(0, k.length) === k;
800 	},
801 	//	不管大小寫 Whether the case
802 	startWithWC : function(i, k) {
803 		return (i + '').slice(0, k.length).toLowerCase() === k.toLowerCase();
804 	},
805 	includeKey : function(i, k) {
806 		return (i + '').toLowerCase().indexOf(k.toLowerCase()) !== -1;
807 	},
808 	includeKeyWC : function(i, k) {
809 		return (i + '').toLowerCase().indexOf((k + '').toLowerCase()) !== -1;
810 	},
811 	always : function() {
812 		return true;
813 	}
814 };
815 
816 
817 //	預設 className	前有 ~ 的會轉成 prefix
818 _.classNameSet={
819  prefix:'si_',
820  container:'~container',
821  input:'~input',
822  inputted:'~inputted',
823  arrow:'~arrow',
824  list:'~list',
825  item:'~item',
826  item_select:'~item_select',
827  functionB:'~function',
828 
829  functionT:'~functionText',
830  error:'~error',
831  warning:'~warning'
832 };
833 
834 
835 //	預設顯示文字
836 _.textSet={
837  showList:'▼',	//	4 way: [▴▸▾◂]
838  hideList:'▲',
839  arrowTitle:'trigger list \n切換顯示查詢列表',
840 
841  allBtn:'全部',
842  clearBtn:'清除',
843  closeBtn:'關閉'//'×'
844 };
845 
846 
847 //	default 欄位驗證 pattern
848 //	http://blog.wu-boy.com/2009/06/16/1454/
849 //	TODO: ID, Age, 電話, 地址, 性別, ..
850 _.default_verify_pattern = {
851 		'|word' : /^\w*$/,
852 		'word' : /^\w+$/,
853 		//	整數
854 		'|integer' : /^[+-]?\d*$/,
855 		'integer' : /^[+-]?\d+$/,
856 		//	自然數
857 		'|natural' : /^([1-9]\d*)?$/,
858 		'natural' : /^[1-9]\d*$/,
859 		//	十進位小數
860 		'|decimal' : /^\d*(\.\d+)?$/,
861 		'decimal' : /^(\d+|\d*\.\d+)$/,
862 		//	數字
863 		'|digit' : /^\d*$/,
864 		'digit' : /^\d$/,
865 
866 		IPv4 : /^[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}\.[12]?\d{1,2}$/,
867 
868 		URI : function(k){return !!parse_URI(k);},
869 
870 		//	RFC 2822
871 		//	http://regexlib.com/DisplayPatterns.aspx
872 		//'RFC2822' : /^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/,
873 		//	http://www.regular-expressions.info/email.html
874 		//'email' : /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+([a-z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b$/i,
875 		email : /^[a-z0-9+_~-]+(\.[a-z0-9+_~-]+)*@([a-z\d]([a-z\d-]*[a-z\d])?\.)+([a-z]{2}|com|org|net)\b$/i,
876 
877 		any : function(k){return k===''?2:0;}
878 };
879 
880 _.prototype={
881 //	應該盡量把東西放在 class,instance少一點?
882 
883 //	instance public interface	-------------------
884 
885 
886 /*	click 事件進行中
887 TODO:
888 用更好的方法取代
889 */
890 clickNow:0,
891 
892 //	instance 的 <input>,.. 之 className, override _.classNameSet.input,..
893 //class_input:'~',
894 //class_item:'~',
895 //..
896 
897 //maxList:\d,
898 
899 //	預設清單 height (px)
900 maxListHeight:200,
901 
902 
903 //	設定/取得所有可選的 list
904 setAllList:function(l){
905  var _t=this,_p=pv(_t),i,c=0,s=_p.arrowO;
906  _t.showList(0);
907  if(typeof l==='object'){
908   _p.list=l;
909   if(l instanceof Array)c=_t.allListCount=l.length;	//	這不準,得用 onList 測試。
910   else{for(i in l)c++;_t.allListCount=c;}
911   //sl('setAllList: Get about '+_t.allListCount+' items.');
912   if(s)
913    if(s=s.style,!c)s.display='none';
914    else if(_t.autoShowArrow)s.display='';
915  }
916  return _p.list;
917 },
918 //	自動於有 list 時 show arrow,無時 hide
919 autoShowArrow:0,
920 
921 //	設定要顯現的 list,會回傳 list,需注意可能被更改!
922 setList:function(l){	//	key
923  return setList.apply(this,arguments);
924 },
925 
926 showList:function(show){
927  return showList.apply(this,arguments);
928 },
929 
930 /*
931 showArrow:function(show){
932  var a=pv(this).arrowO.style;
933  if(typeof show!='undefined')a.display=show?'':'none';
934  return a.display;
935 },
936 */
937 
938 //	每次 input 就會被 call 一次。可用 instance.setSearch('includeKey') 簡易設定
939 onInput:function(k){	//	key
940 },
941 
942 //	設定表單文字欄位的欄位驗證	return 1: warning, 2: error ('Suffering from a pattern mismatch'), string: 將輸入改為回傳值, else OK
943 //	另外可設定 onkeypress(){return true/false;} 來對每一次按鍵作 check。但這不能處理 paste。	http://irw.ncut.edu.tw/peterju/jscript.html#skill
944 verify:function(k){	//	key
945 },
946 
947 set_verify:function(v){
948 	var m=_.default_verify_pattern;
949 	if(library_namespace.is_Object(m)&&(v in m))
950 		v = m[v];
951 
952 	if(v instanceof RegExp)
953 		this.verify = function(k) {
954 			return v.test(k) ? 0 : 2;
955 		};
956 	else if(typeof v === 'function')
957 		this.verify = v;
958 	else if(typeof v === 'string' && (m=v.match(/^(\d*)-(\d*)$/))&&(m[1]||m[2]))
959 		this.verify = new Function('k','return isNaN(k)'+(m[1]?'||k<'+m[1]:'')+(m[2]?'||k>'+m[2]:'')+' ? 2 : 0;');
960 	else
961 		library_namespace.err('error verify condition of ['+pv(this).inputO.id+']: ['+v+']');
962 
963 	return this;
964 },
965 
966 //	input: (list, index), return [value, title[, key=title||value]]
967 onList:function(l,i){
968  return [l[i]||i,l instanceof Array?l[i]:i];
969 },
970 
971 //	input: (list, index), return value to set as input key
972 onSelect:function(l,i){
973  return l instanceof Array?l[i]:i;
974 },
975 
976 /*	searchInList 的減縮版
977 _.searchInList.call(_instance_,'includeKey');
978 eq
979 _instance_.setSearch('includeKey');
980 */
981 setSearch:function(f){
982  return searchInList.call(this,f);
983 },
984 
985 setClassName:function(n){
986  var t=this;
987  if(n)t.class_input=t.class_error=t.class_warning=n;
988  else if(typeof n!='undefined'){delete t.class_input;delete t.class_error;delete t.class_warning;}
989  return setClassName.call(this,'input',pv(this).inputO);
990 },
991 
992 
993 setProperty:function(p,v){
994  var i=pv(this).inputO;
995  //sl('setProperty: '+p+'='+i[p]+'→'+v);
996  if(typeof v!='undefined'&&v!=null)i[p]=v;
997  return i[p];
998 },
999 
1000 //	set/get input value
1001 setValue:function(v){
1002  if(typeof v!=='undefined')
1003   this.triggerToInput();
1004  //library_namespace.log('setValue: '+this.setProperty('value',v));
1005  v=this.setProperty('value',v);
1006  //library_namespace.log('setValue: '+v);
1007  //	TODO: do not use arguments
1008  if(arguments.callee.caller!==do_verify)
1009   //library_namespace.log('setValue: call do_verify('+v+'), list: ['+this.allListCount+']'+this.setAllList()),
1010   do_verify.call(this,v);
1011  return v;
1012 },
1013 
1014 //	set inputted value: 轉換成輸入過的 <span> 時,設定其之值。
1015 setInputted:function(v){
1016  var _p=pv(this),i=_p.inputO;
1017  if(typeof v==='undefined')v=this.dInputted();	//	dInputted: default inputted value, =setValue
1018  create_DO(0,removeAllChild(_p.inputtedO),v);
1019  return v;
1020 },
1021 
1022 setMaxLength:function(l){
1023  //sl('setMaxLength: set length '+(l>0?l:null));
1024  return this.setProperty('maxLength',l>0?l:null);
1025 },
1026 
1027 setName:function(n){
1028  this.setProperty('id',n);
1029  return this.setProperty('name',n);
1030 },
1031 
1032 setTitle:function(t){
1033  if(t)pv(this).inputtedO.title=t;
1034  return this.setProperty('title',t||null);
1035 },
1036 
1037 //	切換 inputted span/input
1038 triggerToInput:function(){
1039  return triggerToInput.apply(this,arguments);
1040 },
1041 
1042 
1043 /*
1044 	for Unobtrusive JavaScript: 為未啟用JavaScript的情況提供替代方案。
1045 	接上 <input> 或 <select>
1046 */
1047 attach:function(o){	//	(input or select object)
1048  //sl('attach: '+o);
1049  //o.replaceNode(_p.inputO);
1050  o=layout.call(this,o);
1051  this.setAllList(this.setAllList());
1052  return o;
1053 },
1054 
1055 
1056 //	(focus or blur, 不驅動 onfocus/onblur)
1057 focus:function(f){	//	,noE
1058  var i=pv(this).inputO;
1059 /*
1060  sl('focus: '+(f?'focus':'blur')+(noE?' and no event':''));
1061  if(f||typeof f==='undefined'){
1062   if(noE)noE=i.onfocus,i.onfocus=null;
1063   i.focus();
1064   //if(noE)i.onfocus=noE;
1065  }else{
1066   if(noE)noE=i.onblur,i.onblur=null;else this.showList(0);
1067   i.blur();
1068   //if(noE)i.onblur=noE;
1069  }
1070 */
1071  if(f||typeof f==='undefined')
1072   try{
1073    //	@IE5-8 initial:  Error @CeL: 2110 [Error] (facility code 10): 控制項不可見、未啟動或無法接受焦點,因此無法將焦點移到控制項上。
1074    //	Error @CeL: 2110 [Error] (facility code 10): Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus.
1075    i.focus();
1076   }catch(e){}
1077  else
1078   this.showList(0),
1079   i.blur();
1080 },
1081 
1082 
1083 //	instance destructor	---------------------------
1084 /*
1085 usage:
1086 instance=instance.destroy();
1087 or if you has something more to do:
1088 instance.destroy()&&instance=null;
1089 */
1090 destroy : function(){
1091 	pv(this, 1);
1092 }
1093 
1094 };	//	_.prototype=
1095 
1096 
1097 //	===================================================
1098 
1099 
1100 //	prevent re-use. 防止再造 
1101 //delete _.clone;
1102 
1103 _.allow_inherit=true;
1104 
1105 return (
1106 	CeL.net.form.select_input
1107 );
1108 };
1109 
1110 //===================================================
1111 
1112 CeL.setup_module(module_name, code_for_including);
1113 
1114 };
1115