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