      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 到最後若可能自動轉到全部
    298 →
    299 */
    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