1 
  2 /**
  3  * @name	CeL form function
  4  * @fileoverview
  5  * 本檔案包含了 form 的 functions。
  6  * @since	
  7  */
  8 
  9 
 10 if (typeof CeL === 'function'){
 11 
 12 /**
 13  * 本 module 之 name(id),<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。
 14  * @type	String
 15  * @constant
 16  * @inner
 17  * @ignore
 18  */
 19 var module_name = 'net.form';
 20 
 21 //===================================================
 22 /**
 23  * 若欲 include 整個 module 時,需囊括之 code。
 24  * @type	Function
 25  * @param	{Function} library_namespace	namespace of library
 26  * @param	load_arguments	呼叫時之 argument(s)
 27  * @return
 28  * @name	CeL.net.form
 29  * @constant
 30  * @inner
 31  * @ignore
 32  */
 33 var code_for_including = function(library_namespace, load_arguments) {
 34 
 35 
 36 var _module_name = module_name;
 37 //requires
 38 if (library_namespace.use( [ 'data', 'net.web' ], module_name))
 39 	return;
 40 //	module_name 會被重設
 41 module_name = _module_name;
 42 
 43 var XML_node = library_namespace.net.web.XML_node,
 44 set_attribute = library_namespace.net.web.set_attribute,
 45 remove_all_child = library_namespace.net.web.remove_all_child,
 46 set_class = library_namespace.net.web.set_class,
 47 split_String_to_Object = library_namespace.data.split_String_to_Object;
 48 
 49 
 50 /**
 51  * null module constructor
 52  * @class	form 的 functions
 53  */
 54 CeL.net.form
 55 = function() {
 56 	//	null module constructor
 57 };
 58 
 59 /**
 60  * for JSDT: 有 prototype 才會將之當作 Class
 61  */
 62 CeL.net.form
 63 .prototype = {
 64 };
 65 
 66 
 67 
 68 
 69 
 70 /*	延遲執行: 加強版的 setTimeout
 71 
 72 id=delayRun(function[,ms=0])
 73 
 74 id=delayRun([function,[args],this] [,ms=0])
 75 
 76 */
 77 function delayRun(f,ms){
 78  var _f=arguments.callee,i;
 79  if(!_f.fL)_f.fL=[];
 80  i=_f.fL.length;
 81  _f.fL.push(f);
 82  setTimeout('delayRun.run('+i+');',ms||0);
 83  return i;
 84 }
 85 delayRun.clear=function(i){
 86  //	clearTimeout(): 為求簡單省略
 87  delete this.fL[i];
 88 };
 89 delayRun.run=function(i){
 90  var _t=this,f=_t.fL[i];
 91  if(f){
 92   if(typeof f=='function')f();
 93   else if(f instanceof Array)f[0].apply(f[2]||null,f[1]);
 94   else eval(f);
 95   delete _t.fL[i];
 96  }
 97 };
 98 
 99 
100 
101 //	簡易型 createE
102 var createO=function(tag,p,t,classN){	//	p.appendChild tag, text t,className classN
103  var _e;
104  if(t && (typeof t!='object' || (t instanceof Object)))
105   t=document.createTextNode(t+'');
106  if(typeof tag=='object'){
107   _e=tag;
108  }else if(tag){
109   _e=document.createElement(tag);
110   if(classN)_e.className=classN;
111   if(t)_e.appendChild(t);
112  }else if(t)_e=t;
113  if(p&&_e)p.appendChild(_e);
114  return _e;
115 };
116 
117 
118 /*	2008/9/3 23:31:21
119 	get scrollbar height	http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php
120 */
121 function scrollbarWidth(){
122  var _f=arguments.callee;
123  if(!_f.w){
124   var w,p=createO('div',document.body),c=createO('div',p,' '),s=p.style;
125   s.width=s.height='80px';
126   //c.style.width='100%';	//	有時沒這行才出得來
127   s.overflow='hidden';
128   w=c.offsetWidth;
129   s.overflow='scroll';
130   _f.w=w-c.offsetWidth;
131   //sl('scrollbarWidth: '+w+'-'+c.offsetWidth+'='+_f.w);
132   document.body.removeChild(p);
133  }
134  return _f.w;
135 }
136 
137 
138 /*	2008/9/3 23:31:29
139 	scroll 到可以看到 object
140 
141 TODO:
142 考慮可能沒 scrollbar
143 包括橫向
144 */
145 function scrollToShow(o,p){	//	(object, parentNode to scroll)
146  if(!p){p=o;while((p=p.parentNode)&&p.offsetHeight==p.scrollHeight);}
147  //sl('scrollToShow: '+p.scrollTop+', '+p.scrollHeight+', '+p.offsetHeight+', '+o.offsetTop);
148  var s,a;
149  if(a=o.offsetTop,a<p.scrollTop)s=a;
150  else if(a=o.offsetTop+o.offsetHeight-p.offsetHeight+scrollbarWidth(),a>p.scrollTop)
151   if(s=a,a=o.offsetTop,a<s)s=a;
152  if(!isNaN(s))p.scrollTop=s;
153 }
154 
155 
156 var
157 //ClassT={AccountF:{},AddressF:{}},	//	class template set
158 
159 /*
160 JavaScript 地址輸入表單支援 (address input form)
161 現有台灣可用。
162 AddressF.TW
163 */
164 AddressF={},
165 AccountF={};
166 
167 
168 
169 /*
170 
171 {
172 	title: '',
173 	name: '',
174 	container: 'id' | obj,
175 	list: [] | {} | {group1:{}, group2:[],.. },
176 	default: '' | [],
177 	type: 'select' | 'radio' | 'checkbox',
178 }
179 
180 return <select>
181 
182 
183 TODO:
184 複選 <select>
185 <radio>
186 <checkbox>
187 +<label>
188 autocomplete: 假如所有備取 list 都有一樣的 prefix,則自動完成。
189 	把後面的用反白自動完成。
190 
191 在 list 上下安排三角,onmouseover 即可自動滾動。
192 
193 color panel
194 
195 http://www.itlearner.com/code/js_ref/choi3.htm
196 selectName.options[i]=new Options("option_value","option_Text", defaultSelected, selected);
197 */
198 
199 function menuCreater(o,l){	//	container object, list
200 
201 };
202 
203 
204 
205 
206 
207 //	===================================================
208 
209 /*
210 	提供有選單的 input
211 
212 _=this
213 
214 TODO:
215 浮水印 background-image:url();
216 
217 
218 HISTORY:
219 2008/7/22 0:38:14	create
220 7/27 22:55:18	verify()
221 8/7 21:18:47	attach()
222 */
223 var
224 ChooseI=
225 
226 (/*ClassT.ChooseI=*/function(initF){
227 
228 var
229 
230 //	class private	-----------------------------------
231 
232 
233 /*	可紀錄的 set class name,不過對大多數人來說,更常用的是 instance.setClassName
234 
235 usage:
236 (item[, obj])		set obj to className item, return real className that setted
237 (0,'prefix')	set prefix & 重設(全部重跑)
238 */
239 setClassName=function(i,o,noRec){	//	(0, prefix) or (item, object)
240  var _t=this,s=_.classNameSet;
241  if(!_t.settedClass)_t.settedClass=[];
242 
243  if(!o||typeof o=='object'){
244   //	設定並回傳 className
245   //sl('setClassName: test '+'class_'+i+': '+('class_'+i in _t?'<em>YES</em>':'none'));
246   s=[	i in s?
247 		s[i].charAt(0)=='~'?s.prefix+s[i].slice(1):
248 		s[i]:
249 	''
250   ];
251   if('class_'+i in _t)
252    if(i=='error'||i=='warning')s.unshift(_t['class_'+i]);
253    else s.push(_t['class_'+i]);
254   s=s.join(' ');
255   //sl('setClassName: set '+o+(s?' to ['+s+']':', <em>There is no ['+i+'] in classNameSet or instance set.</em>'));
256   if(o && (o.className=s,!noRec))
257    _t.settedClass.push(s,o);
258   return s;
259  }
260 
261  s.prefix=o;
262  o=_t.settedClass;
263  //	重設(全部重跑)
264  for(var i=0;i<o.length;i++)
265   _f.call(_t,o[0],o[1],1);
266 },
267 
268 funcButton=function(_t,t,f,title){	//	add text t, function f to instance _t
269  var _p=pv(_t),o=createO('span',_p.listO,'['),b;
270  setClassName.call(_t,'functionB',o);
271  b=createO('span',o,t);
272  setClassName.call(_t,'functionT',b);
273  b.title=title,b.onclick=f;
274  createO(0,o,']');
275  return b;
276 },
277 
278 //	簡易型
279 removeAllChild=function(o){
280  o.innerHTML='';
281  return o;
282 },
283 
284 //	show/hide list
285 showList=function(show){	//	():get, 0:hide, 1:show
286  var _t=this,_p=pv(_t),o=_p.listO,s,c=0;
287 
288  if(!o)return;
289  s=o.style;
290  if(show){
291   c=getObjLoc(_p.inputO);
292   s.top=c[1]+c[3]+2+'px';
293   s.left=c[0]+'px';
294 
295   s.width=_p.inputO.offsetWidth+'px';
296   s.height='';	//	reset
297   c=s.display='block';
298   if(_t.maxListHeight&&o.offsetHeight>_t.maxListHeight)
299    s.height=_t.maxListHeight+'px';
300  }else if(typeof show!='undefined')
301   c=s.display='none';
302 
303  if(c)
304   createO(0,removeAllChild(_p.arrowO),_.textSet[c!='none'?'hideList':'showList']);
305  else c=s.display;
306 
307  return c!='none';
308 },
309 
310 
311 /*	準備選擇清單的某一項
312 TODO:
313 自動完成
314 到最後若可能自動轉到全部
315316 */
317 cK=0,	//	control key pressed
318 readyTo=function(e,o){
319  if(!e)e=event;
320  var _t=this,_p=pv(_t),c,gI=function(o){
321   return o&&/*(can_use_special_attribute?o.getAttribute("sIndex"):o.sIndex)*/o.sIndex;
322  };
323  //sl('readyTo: '+e.type+', key: '+(e.keyCode||e.which||e.charCode)+', _p.listA: '+(_p.listA&&_p.listA.length));
324 
325  if(!_p.listA||!_p.listA.length)return;
326 
327  if(e.type=='mouseover'||e.type=='mouseout'){
328   if(_p.readyItem)setClassName.call(_t,'item',_p.readyItem,0);
329   if(e.type=='mouseover')c='item_select',_p.readyItem=o;
330   else if(c='item',_p.readyItem===o)_p.readyItem=0;
331  //	需更改 _p.inputO.onkeyup 以防止重新 list!!
332  }else if(c=e.keyCode||e.which||e.charCode,c==13){
333   if(_p.readyItem){
334    //sl('readyTo: 以鍵盤選擇: '+_p.readyItem.innerHTML);
335    cK=c,_p.readyItem.onclick();	//	用 .click() 無效!
336    return false;
337   }else return;
338  //	key input 用鍵盤控制上下	←↑→↓: 37~40
339  }else if(c==38||c==40){
340   cK=c;
341   o=_p.readyItem;
342   //sl('readyTo: 以鍵盤移至: '+(o&&(o.getAttribute("sIndex")+','+o.sIndex)));
343   if(!o)o=_p.listA[c==40?0:_p.listA.length-1];
344   else{
345    //if(!o.getAttribute)throw 1;	IE 可用 getAttribute,FF 或許在 appendChild 之後屬性重設?,得用 o.sIndex
346    c=gI(o)+(c==38?-1:1);
347    if(c<0||c>=_p.listA.length)return;
348 
349    setClassName.call(_t,'item',o,0);
350    o=_p.listA[c];
351   }
352   _p.readyItem=o;
353 
354   scrollToShow(o,_p.listO);
355   c='item_select';
356  }else if(c==35||c==36){	//	35: End, 36: Home
357   cK=c;
358   if(o=_p.readyItem)setClassName.call(_t,'item',o,0);
359   _p.readyItem=o=_p.listA[c==36?0:_p.listA.length-1];
360   scrollToShow(o,_p.listO);
361   c='item_select';
362  }else if(c==33||c==34){	//	33: PageUP, 34: PageDown
363   cK=c;
364   o=_p.readyItem;
365   if(!o)return;
366   setClassName.call(_t,'item',o,0);
367   var i=gI(o),t;
368   if(c==33){
369    t=_p.listO.scrollTop-1;
370    while(i&&_p.listA[i-1].offsetTop>t)i--;
371   }else{
372    t=_p.listO.scrollTop+_p.listO.offsetHeight-scrollbarWidth();
373    while(i<_p.listA.length-1&&_p.listA[i+1].offsetTop<t)i++;
374   }
375   //sl('readyTo: Page: '+i+', top: '+t+', scroll: '+_p.listO.scrollTop);
376   if(i==gI(o))
377    if(c==33){
378     t-=_p.listO.offsetHeight;
379     if(t<2)i=0;
380     else while(i&&_p.listA[i-1].offsetTop>t)i--;
381    }else{
382     t+=_p.listO.offsetHeight;
383     while(i<_p.listA.length-1&&_p.listA[i+1].offsetTop<t)i++;
384    }
385   //sl('readyTo: Page: '+i+', top: '+t+', height: '+_p.listO.offsetHeight);
386   _p.readyItem=o=_p.listA[i];
387   scrollToShow(o,_p.listO);
388   c='item_select';
389  }else return;
390 
391  setClassName.call(_t,c,o,0);
392  return false;
393 },
394 
395 //can_use_special_attribute,
396 
397 //	顯示清單的工具函數
398 setList=function(l,force,limit,f){
399  var _t=this,_p=pv(_t),i,c=0,k,o;
400  if(isNaN(limit))limit=isNaN(_t.maxList)?_.maxList:_t.maxList||Number.MAX_VALUE;
401  if(!f)f=function(l,i){
402   var a=_t.onList(l,i),o;
403   if(!a)return;
404   o=createO('div',0,a[0]);
405   setClassName.call(_t,'item',o);
406   o.title=a[1];
407   k=a[2]||a[1];
408   o.onmouseover=o.onmouseout=function(e){readyTo.call(_t,e,o);};
409   o.onclick=function(){var v=_t.onSelect(l,i);_t.setValue(v);_t.onInput(v);};
410 
411   //	這邊本來放在下面 for 的地方
412   c++,_p.listO.appendChild(o);
413   //if(!can_use_special_attribute){o.setAttribute("sIndex",1);can_use_special_attribute=o.getAttribute("sIndex")?1:-1;}
414   //if(can_use_special_attribute==1)o.setAttribute("sIndex",_p.listA.length);else o.sIndex=_p.listA.length;
415   o.sIndex=_p.listA.length;//o.setAttribute("sIndex",o.sIndex=_p.listA.length);
416   _p.listA.push(o);
417 
418   return o;
419  };
420 
421  //_t.showList(0);
422 
423  _p.listO=removeAllChild(_p.listO),_p.listA=[],_p.readyItem=0;
424  if(l instanceof Array){
425   for(i=0;i<l.length&&c<limit;i++)
426    f(l,i);
427  }else
428   for(i in l)
429    if(c<limit){
430     f(l,i);
431    }else break;
432 
433  //sl('setList: list '+c+' items, key '+k+'=?'+_t.setValue());
434  if(c==1&&_t.setValue()==k)c=0;	//	僅有一個且與 key 相同
435  if(!force&&!c)return;	//	無 list
436 
437  //	add function
438  if(c!=_t.allListCount)funcButton(_t,_.textSet.allBtn,function(){_t.doFunc=1;_t.focus();_t.setList(_t.setAllList(),1,Number.MAX_VALUE);},'顯示全部 '+_t.allListCount+' 列表。');
439  if(_t.setValue())funcButton(_t,_.textSet.clearBtn,function(){_t.doFunc=2;_t.focus();_t.onInput(_t.setValue(''));},'清除輸入內容,重新列表。');
440  funcButton(_t,_.textSet.closeBtn,function(){_t.doFunc=3;_t.showList(0);},'close menu \n關閉列表');
441 
442  showList.call(_t,1);
443  return _t.listCount=c;
444 },
445 
446 //	return verify 之後的 key(<input>) 值
447 do_verify=function(k){
448  var _t=this,c=_t.verify(k||_t.setValue());
449  //sl('do_verify: input status: '+(c==1?'warning':c==2?'error':'OK'));
450 
451  if(typeof c=='string')_t.setValue(k=c);	//	可以設定 key 值!
452  else setClassName.call(_t,c==1?'warning':c==2?'error':'input',pv(_t).inputO,1);
453 
454  return k;
455 },
456 
457 //	簡易設定常用的 onInput 型式
458 searchInList=function(f,o){	//	o: 傳入 (list, index, key)
459  var _t=this;
460  if(typeof f=='string'&&(f in _.searchFunctionSet))f=_.searchFunctionSet[f];
461 
462  //	因為允許傳入 list,所以不能在這邊用 _t.setAllList() 判別函數,而得要寫一個泛用的。
463  return _t.onInput=function(k,L,force){
464   //sl('searchInList, onInput: search ['+k+'] use '+f);
465 
466   if(!L)L=_t.setAllList();
467   k=do_verify.call(_t,k||'');
468 
469   var l,i;
470 
471   //sl('searchInList: search '+(L instanceof Array?'array':'object'));
472   if(L instanceof Array){
473    l=[];//new L.constructor();
474    for(i=0;i<L.length;i++)
475     if(o?f(L,i,k):L[i]&&f(L[i],k))l.push(L[i]);	//	search value
476   }else{
477    l={};
478    for(i in L)
479     if(o?f(L,i,k):i&&f(i,k)||L[i]&&f(L[i],k))l[i]=L[i];	//	search key+value
480   }
481   _t.setList(l,force);
482  };
483 
484 },
485 
486 
487 //	切換 input/inputted span
488 triggerToInput=function(y){	//	切換至 input or not
489  var _t=this,_p=pv(_t);
490  if(y||typeof y=='undefined'){
491   //	to input
492   _p.inputtedO.style.display='none';
493   if(_t.allListCount)_p.arrowO.style.display='inline';
494   _p.inputO.style.display='inline';
495   return 1;
496  }else{
497   //	to inputted span
498   _t.showList(0);
499   _t.setInputted();
500   _p.arrowO.style.display=_p.inputO.style.display='none';
501   _p.inputtedO.style.display='inline';
502  }
503 },
504 
505 
506 /*	配置元件
507 
508 本函數會配置/增加:
509 <div>		.container
510 	<input>	.inputO
511 	<span>	.inputtedO
512 	<span>	.arrowO
513 	<div>	.listO
514 
515 arguments:
516 <input> 會被當作 inputO 主元件
517 <select> 會被當作選項
518 others: container
519 */
520 dispose=function(o){
521  var _t=this,_p=pv(_t),t;
522 
523  if(typeof o!='object')
524   o=document.getElementById(o);
525 
526  if(!o || (o.tagName.toLowerCase() in {hr:1,br:1}))return;	//	** 這邊應該檢查 o 是不是 <hr/> 等不能加 child 的!
527 
528  //sl(('dispose: use <'+o.tagName+'>: '+o.innerHTML).replace(/</g,'<'));
529 
530  //	TODO: 這邊應該有一個更完善的刪除策略
531  if(_t.loaded){
532   t=_p.container;
533   for(var i=0,e='inputO,inputtedO,arrowO,listO'.split(',');i<e.length;i++)
534    //sl('dispose: removeChild '+e[i]),
535    _p[e[i]].parentNode.removeChild(_p[e[i]]);//t.removeChild(_p[e[i]]);
536   if(!t.childNodes.length)t.parentNode.removeChild(t);
537  }
538 
539 
540  //	依照各種不同的傳入 object 作出應對
541  t=o.tagName.toLowerCase();
542 
543  if(t=='input'){
544   o.parentNode.insertBefore(
545 	t=_p.container=createO('span'),
546 	_p.inputO=o
547 	);
548   setClassName.call(_t,'container',t);
549 
550   if(!o.className)setClassName.call(_t,'input',o);
551 
552   t.appendChild(o.parentNode.removeChild(o));
553 
554   o=t;
555 
556  }else if(t=='select'){
557   o.parentNode.insertBefore(t=_p.container=createO('span'),o);
558 
559   _p.inputO=createO('input',t);
560   setClassName.call(_t,'input',_p.inputO);
561   _p.inputO.name=o.name;
562   if(o.selectedIndex>=0)_p.inputO.value=o.options[o.selectedIndex].value;
563 
564   var l={},opt=o.options,i=0;
565   for(;i<opt.length;i++)
566    l[opt[i].value||opt[i].innerHTML]=opt[i].innerHTML;
567 
568   //	list setting
569   _t.setAllList(l);
570 
571   o.parentNode.removeChild(removeAllChild(o));
572 
573   o=t;
574 
575  }else{
576   _p.container=o;	//	容器
577   if(!o.className)setClassName.call(_t,'container',o);
578 
579   _p.inputO=createO('input',o);
580   setClassName.call(_t,'input',_p.inputO);
581  }
582 
583 
584  //	補足其他的設定
585  _p.inputO.setAttribute("autocomplete","off");
586 
587  _p.inputtedO=createO('span',o);
588  setClassName.call(_t,'inputted',_p.inputtedO);
589  _p.inputtedO.style.display='none';
590 
591  _p.inputtedO.onclick=function(){
592   _t.clickNow=1;
593   _t.triggerToInput();
594   _p.inputO.focus();
595   _t.clickNow=0;
596  };
597 
598  (_p.arrowO=createO('span',o,_.textSet.showList))
599   .title=_.textSet.arrowTitle;
600  setClassName.call(_t,'arrow',_p.arrowO);
601 
602  _p.listO=createO('div',o);
603  _p.arrowO.onmouseover=_p.listO.onmouseover=function(){_t.clickNow=1;};
604  _p.arrowO.onmouseout=_p.listO.onmouseout=function(){_t.clickNow=0;};
605  setClassName.call(_t,'list',_p.listO);
606  _t.showList(0);
607 
608 
609  // event setting
610  //_p.inputO.onmouseover=
611  _p.inputO.onkeydown=function(e){readyTo.call(_t,e);};
612  _p.inputO.onmouseup=_p.inputO.onkeyup=_p.inputO.ondragend=function(e){
613   if(!e)e=event;
614   var c=e.keyCode||e.which||e.charCode;
615   //sl('up: '+e.type+', key: '+c+', _p.listA: '+(_p.listA&&_p.listA.length));
616   if(cK&&cK==c){cK=0;return false;}
617   //	Esc
618   if(c==27){_t.showList(0);return false;}
619   _t.clickNow=1;_t.onInput(_t.setValue());
620  };
621  _p.inputO.onmouseout=function(){_t.clickNow=0;};
622  if(_p.inputO.addEventListener)_p.inputO.addEventListener('dragdrop',_p.inputO.ondragend,false);
623  //if(window.addEventListener)window.addEventListener('click',function(){_t.showList(0);},true);
624  //addListener(0,'click',function(){sl('close..');_t.showList(0);sl('close done.');})
625  _p.inputO.onblur=function(){
626   //if(_t.verify(_t.setValue())==2){alert('Wrong input!');return false;}	//	這在 Firefox 似乎沒啥效果..
627 /*	設定這項在按 _p.arrowO 的時候會出問題,所以建議在其他地方自訂。
628   if(_t.setValue() && (_t.setValue() in _t.setAllList()))
629    _t.triggerToInput(0);
630 */
631   //	TODO: 假如以鍵盤離開,應該也 showList(0);
632   if(_t.clickNow)_t.clickNow=0;else _t.showList(0);
633  };
634 
635  //	show/hide
636  _p.arrowO.onclick=function(){
637   //sl('arrowO.onclick start');
638   _t.clickNow=1;
639   if(_t.showList())_t.showList(0);
640   else _t.onInput(_t.setValue(),0,1);
641   _t.clickNow=0;
642   //sl('arrowO.onclick end');
643  };
644  // ondblclick: double click
645  //_p.inputO.ondblclick=function(){_t.onInput(_p.inputO.value,0,1);};
646 
647 
648  _t.loaded=1;	//	isLoaded
649 
650 },
651 
652 //	instance constructor	---------------------------
653 instanceL=[],
654 initI=function(o,l){	//	(HTML object, list: Array or Object)
655  var _t=this,_p;
656  // objects setting
657  if(typeof o!='object')
658   //sl('Use object ['+o+']'),
659   o=document.getElementById(o);
660 
661  _p=pv(_t);	//	also do initial
662  instanceL.push(_t);	//	for destructor
663 
664   if(o)
665    dispose.call(this,o);
666 /*
667  else{
668   //throw new Error(1,'Can not get document object'+(o?' ['+o+']':'')+'!');
669   return;
670  }
671 */
672 
673  //	list setting
674  if(l&&!_t.allListCount)_t.setAllList(l);
675 
676  if(_p.arrowO)
677   _p.arrowO.style.display=_t.allListCount?'inline':'none';	//	無 list 的話先不顯示,等有 list 再說。
678 
679  //	setup default inputted value
680  _t.dInputted=_t.setValue;
681 
682  //return _t;
683 },_=function(){initI.apply(this,arguments);initF&&initF.apply(this,arguments);},
684 
685 //	(instance private handle)	不要 instance private 的把這函數刪掉即可。
686 _p='_'+(Math.random()+'').replace(/\./,''),
687 //	get private variables (instance[,destroy]), init private variables (instance[,access function list[, instance destructor]])
688 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:{};};
689 
690 //	(for inherits)	不要 inherit 的把這段刪掉即可。
691 
692 (_.clone=arguments.callee).toString=function(){return '[class_template]';};
693 
694 
695 //	class destructor	---------------------------
696 /*
697 please call at last (e.g., window.unload)
698 
699 usage:
700 classT=classT.destroy();
701 or if you has something more to do:
702 classT.destroy()&&classT=null;
703 */
704 
705 _.destroy=function(){for(var i=0;i<instanceL.length;i++)instanceL[i].destroy();_p();};
706 
707 //	(instance private handle, continue)
708 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+'={};}})();');
709 _p.toString=function(){return'';};
710 
711 
712 /*
713 //	測試是否可用自訂之屬性
714 var o=document.createElement('div');
715 o.setAttribute('testA',2);
716 can_use_special_attribute=o.getAttribute('testA');
717 sl('can_use_special_attribute: '+can_use_special_attribute);
718 */
719 
720 //	class public interface	---------------------------
721 
722 
723 //	預設清單最大顯示數
724 _.maxList=10;
725 
726 
727 //	searchInList 常用到的函數
728 _.searchFunctionSet={
729  allTheSame:function(i,k){return (i+'')===k;},
730  startWith:function(i,k){return (i+'').slice(0,k.length)===k;},
731  //	不管大小寫 Whether the case
732  startWithWC:function(i,k){return (i+'').slice(0,k.length).toLowerCase()===k.toLowerCase();},
733  includeKey:function(i,k){return (i+'').toLowerCase().indexOf(k.toLowerCase())!==-1;},
734  includeKeyWC:function(i,k){return (i+'').toLowerCase().indexOf((k+'').toLowerCase())!==-1;}
735 };
736 
737 
738 //	預設 className	前有 ~ 的會轉成 prefix
739 _.classNameSet={
740  prefix:'cI_',
741  container:'~container',
742  input:'~input',
743  inputted:'~inputted',
744  arrow:'~arrow',
745  list:'~list',
746  item:'~item',
747  item_select:'~item_select',
748  functionB:'~function',
749  functionT:'~functionText',
750  error:'~error',
751  warning:'~warning'
752 };
753 
754 
755 //	預設顯示文字
756 _.textSet={
757  showList:'▼',	//	4 way: [▴▸▾◂]
758  hideList:'▲',
759  arrowTitle:'trigger list \n切換顯示查詢列表',
760 
761  allBtn:'全部',
762  clearBtn:'清除',
763  closeBtn:'關閉'//'×'
764 };
765 
766 
767 _.prototype={
768 //	應該盡量把東西放在 class,instance少一點?
769 
770 //	instance public interface	-------------------
771 
772 
773 /*	click 事件進行中
774 TODO:
775 用更好的方法取代
776 */
777 clickNow:0,
778 
779 //	instance 的 <input>,.. 之 className, override _.classNameSet.input,..
780 //class_input:'~',
781 //class_item:'~',
782 //..
783 
784 //maxList:\d,
785 
786 //	預設清單 height (px)
787 maxListHeight:200,
788 
789 
790 //	設定/取得所有可選的 list
791 setAllList:function(l){
792  var _t=this,_p=pv(_t),i,c=0,s=_p.arrowO;
793  _t.showList(0);
794  if(typeof l=='object'){
795   _p.list=l;
796   if(l instanceof Array)c=_t.allListCount=l.length;	//	這不準,得用 onList 測試。
797   else{for(i in l)c++;_t.allListCount=c;}
798   //sl('setAllList: Get about '+_t.allListCount+' items.');
799   if(s)
800    if(s=s.style,!c)s.display='none';
801    else if(_t.autoShowArrow)s.display='';
802  }
803  return _p.list;
804 },
805 //	自動於有 list 時 show arrow,無時 hide
806 autoShowArrow:0,
807 
808 //	設定要顯現的 list,會回傳 list,需注意可能被更改!
809 setList:function(l){	//	key
810  return setList.apply(this,arguments);
811 },
812 
813 showList:function(show){
814  return showList.apply(this,arguments);
815 },
816 
817 /*
818 showArrow:function(show){
819  var a=pv(this).arrowO.style;
820  if(typeof show!='undefined')a.display=show?'':'none';
821  return a.display;
822 },
823 */
824 
825 //	每次 input 就會被 call 一次。可用 instance.setSearch('includeKey') 簡易設定
826 onInput:function(k){	//	key
827 },
828 
829 //	設定文字欄位的欄位驗證	return 1: warning, 2: error, string: 將輸入改為回傳值, else OK
830 //	另外可設定 onkeypress(){return true/false;} 來對每一次按鍵作 check。但這不能處理 paste。	http://irw.ncut.edu.tw/peterju/jscript.html#skill
831 verify:function(k){	//	key
832 },
833 
834 //	input: (list, index), return [value, title[, key=title||value]]
835 onList:function(l,i){
836  return [l[i]||i,l instanceof Array?l[i]:i];
837 },
838 
839 //	input: (list, index), return value to set as input key
840 onSelect:function(l,i){
841  return l instanceof Array?l[i]:i;
842 },
843 
844 /*	searchInList 的減縮版
845 ChooseI.searchInList.call(_instance_,'includeKey');
846 eq
847 _instance_.setSearch('includeKey');
848 */
849 setSearch:function(f){
850  return searchInList.call(this,f);
851 },
852 
853 setClassName:function(n){
854  var t=this;
855  if(n)t.class_input=t.class_error=t.class_warning=n;
856  else if(typeof n!='undefined'){delete t.class_input;delete t.class_error;delete t.class_warning;}
857  return setClassName.call(this,'input',pv(this).inputO);
858 },
859 
860 
861 setProperty:function(p,v){
862  var i=pv(this).inputO;
863  //sl('setProperty: '+p+'='+i[p]+'→'+v);
864  if(typeof v!='undefined'&&v!=null)i[p]=v;
865  return i[p];
866 },
867 
868 //	set/get input value
869 setValue:function(v){
870  if(typeof v!='undefined')this.triggerToInput();
871  v=this.setProperty('value',v);
872  if(arguments.callee.caller!==do_verify)
873   //sl('setValue: call do_verify'),
874   do_verify.call(this,v);
875  return v;
876 },
877 
878 //	set inputted value: 轉換成輸入過的 <span> 時,設定其之值。
879 setInputted:function(v){
880  var _p=pv(this),i=_p.inputO;
881  if(typeof v=='undefined')v=this.dInputted();	//	dInputted: default inputted value, =setValue
882  createO(0,removeAllChild(_p.inputtedO),v);
883  return v;
884 },
885 
886 setMaxLength:function(l){
887  //sl('setMaxLength: set length '+(l>0?l:null));
888  return this.setProperty('maxLength',l>0?l:null);
889 },
890 
891 setName:function(n){
892  this.setProperty('id',n);
893  return this.setProperty('name',n);
894 },
895 
896 setTitle:function(t){
897  if(t)pv(this).inputtedO.title=t;
898  return this.setProperty('title',t||null);
899 },
900 
901 //	切換 inputted span/input
902 triggerToInput:function(){
903  return triggerToInput.apply(this,arguments);
904 },
905 
906 
907 /*
908 	for Unobtrusive JavaScript: 為未啟用JavaScript的情況提供替代方案。
909 	接上 <input> 或 <select>
910 */
911 attach:function(o){	//	(input or select object)
912  //sl('attach: '+o);
913  //o.replaceNode(_p.inputO);
914  o=dispose.call(this,o);
915  this.setAllList(this.setAllList());
916  return o;
917 },
918 
919 
920 //	(focus or blur, 不驅動 onfocus/onblur)
921 focus:function(f){	//	,noE
922  var i=pv(this).inputO;
923 /*
924  sl('focus: '+(f?'focus':'blur')+(noE?' and no event':''));
925  if(f||typeof f=='undefined'){
926   if(noE)noE=i.onfocus,i.onfocus=null;
927   i.focus();
928   //if(noE)i.onfocus=noE;
929  }else{
930   if(noE)noE=i.onblur,i.onblur=null;else this.showList(0);
931   i.blur();
932   //if(noE)i.onblur=noE;
933  }
934 */
935  if(f||typeof f=='undefined')
936   i.focus();
937  else
938   this.showList(0),
939   i.blur();
940 },
941 
942 
943 //	instance destructor	---------------------------
944 /*
945 usage:
946 instance=instance.destroy();
947 or if you has something more to do:
948 instance.destroy()&&instance=null;
949 */
950 destroy:function(){pv(this,1);}
951 
952 };	//	_.prototype=
953 
954 return _;
955 })();	//	(function(){
956 
957 
958 //	===================================================
959 
960 
961 
962 
963 
964 //	===================================================
965 /*
966 	used for address input form
967 	住址輸入
968 
969 TODO:
970 parse address
971 
972 
973 HISTORY:
974 2008/7/24 20:38:18	create
975 */
976 
977 AddressF.TW=
978 
979 (/*ClassT.AddressF.TW=*/function(){
980 
981 var
982 
983 //	class private	-----------------------------------
984 
985 /*	存放 data 的 path
986 path/:
987 zip/	ZIP code data
988 
989 */
990 path='./',
991 
992 /*
993 
994 ZIP[city: 縣市][district: 鄉鎮市區]=ZIP code
995 
996 ZIP5[Number(zip3)]={ "zip5,路街":"路街,no_range",.. }
997 
998 ZIP_to_cd[String(zip3)]=[city,district];
999 
1000 ZIP5_to_town[String(zip5)]=[路街,no_range]
1001 
1002 c_d[city]=[district list]
1003 
1004 ENG[city][district]=english
1005 
1006 
1007 cityL, districtL: list for ChooseI
1008 cityL[_city_name_]='_city_name_ (districts number)'
1009 districtL[_district_name_]={city_name_:1}
1010 
1011 */
1012 ZIP={},ZIP5=[],ZIP_to_cd={},ZIP5_to_town={},ENG={},c_d={},cityL={},districtL={},
1013 
1014 getZIP5=function(z3,force){
1015  var d,i=0,a,Z;
1016  z3=Math.floor(z3);
1017  if(!(z3 in ZIP5)||!ZIP5[z3])
1018   Z=ZIP5[z3]={};
1019  else if(Z=ZIP5[z3],!force)return Z;
1020 
1021  try{
1022   d=getU(path+'zip'+(z3>99?'':z3>9?'0':'00')+z3+'.csv');
1023  }catch(e){
1024   //sl('getZIP5: <em>Can not get ZIP5 data of ['+(z3>99?'':z3>9?'0':'00')+z3+']</em>!');
1025   return;
1026  }
1027 
1028  if(d&&(d=parseCSV(d)))for(;i<d.length;i++)
1029   if((a=d[i])&&a.length)Z[a[0]+','+a[3]]=ZIP5_to_town[a[0]]=!a[4]||a[4]=='全'?a[3]:a[3]+','+a[4].replace(/^[ \s]+/,'');//去除前空白
1030  //else sl('Can not parse ZIP5 data of ['+(z3>99?'':z3>9?'0':'00')+z3+']!');
1031 
1032  //sl('getZIP5: ['+(z3>99?'':z3>9?'0':'00')+z3+'] '+d.length+' records.');
1033 
1034  return Z;
1035 },
1036 
1037 
1038 addFunc=function(t,f){
1039  var _t=this,_p=pv(_t);
1040  createO(0,_p.container,' [');
1041  (createO('span',_p.container,t,_.classNameSet.clearMark))
1042 	.onclick=arguments.callee.caller==initI?function(){f.apply(_t);}:f;//function(){f(_p.zipI,_p.cityI,_p.districtI,_p.addressI);};
1043  createO(0,_p.container,']');
1044 },
1045 
1046 
1047 //	instance constructor	---------------------------
1048 instanceL=[],
1049 initI=function(o,prefix){
1050  if(typeof o!='object')o=document.getElementById(o);
1051  if(!o){
1052   //throw new Error(1,'Can not get outter document object!');
1053   return;
1054  }
1055 
1056  if(!prefix)prefix='adr_';
1057 
1058  var _t=this,_p=pv(_t),a;
1059  instanceL.push(_t);	//	for destructor
1060  _p.container=o;	//	容器
1061 
1062  //	initial instance object
1063  _t.nameSet={
1064   zip:'zip',
1065   city:'city',
1066   district:'district',
1067   address:'address'
1068  };
1069 
1070  //	layout setup
1071  _p.fullAdr=createO('input',o);	//	最後送出時用
1072  try{_p.fullAdr['type']='hidden';}catch(e){}	//	低版本 JScript: error
1073  _p.fullAdr.style.display='none';
1074 
1075  var zipOTrg,zipT=createO('span',o,'郵遞區號');	//	TODO: <label>
1076  a=_p.zipI=new ChooseI(o,ZIP_to_cd);
1077  a.setClassName(_.classNameSet.zipI);
1078  a.setTitle('郵遞區號');
1079 
1080  _p.zipI.dInputted=function(){
1081   return '['+this.setValue()+']';
1082  };
1083 
1084  zipOTrg=_p.zipI.triggerToInput;
1085  _p.zipI.triggerToInput=function(y){
1086   if(y=y||typeof y=='undefined'){
1087    zipT.style.display='inline';
1088    zipOTrg.call(_p.zipI,y);
1089   }else{
1090    zipT.style.display='none';
1091    zipOTrg.call(_p.zipI,y);
1092   }
1093  };
1094 
1095  createO(0,o,' ');//地址:
1096  (_p.cityI=new ChooseI(o,cityL)).setTitle('縣市');
1097  (_p.districtI=new ChooseI(o,districtL)).setTitle('鄉鎮市區');
1098  a=_p.addressI=new ChooseI(o);
1099  a.autoShowArrow=1;
1100  a.setClassName(_.classNameSet.addressI);
1101 
1102 /*
1103  addFunc.call(_t,'全關閉',function(){
1104  	var _p=pv(this);
1105 	//sl('全關閉: clear all value.');
1106 	_p.zipI.showList(0);
1107 	_p.cityI.showList(0);
1108 	_p.districtI.showList(0);
1109 	_p.addressI.showList(0);
1110  });
1111 */
1112  addFunc.call(_t,'全清除',function(){
1113  	var _p=pv(this);
1114 	//sl('全清除: clear all value.');
1115 	_p.zipI.setValue(''),		_p.zipI.showList(0),		_p.zipI.triggerToInput();
1116 	_p.cityI.setValue(''),		_p.cityI.showList(0),		_p.cityI.triggerToInput();
1117 	_p.districtI.setValue(''),	_p.districtI.showList(0),	_p.districtI.triggerToInput();
1118 	_p.addressI.setValue(''),	_p.addressI.showList(0),	_p.addressI.triggerToInput();
1119 	_p.addressI.setAllList(null);
1120  });
1121 
1122  //	功能設定
1123  var	zipF=_p.zipI.setSearch('startWith'),
1124 	cityF=_p.cityI.setSearch('includeKey'),
1125 	districtF=_p.districtI.setSearch('includeKey')
1126 	;
1127 
1128  a=_p.zipI;
1129  a.maxList=20,
1130  a.setMaxLength(5),
1131  a.onInput=function(k){
1132   zipF.apply(_p.zipI,arguments);
1133   _p.addressI.showList(0);
1134   if(k in ZIP_to_cd){
1135    var a=ZIP_to_cd[k];
1136    if(a[0])_p.cityI.setValue(a[0]),_p.cityI.showList(0);
1137    if(a[1])_p.districtI.setValue(a[1]),_p.districtI.showList(0);
1138   }else if((k in ZIP5_to_town)&&!_p.addressI.setValue())
1139    _p.addressI.setValue(ZIP5_to_town[k]),_p.addressI.showList(0);
1140  };
1141  a.verify=function(k){
1142   if(!k&&k!==0)return 1;
1143   var z;
1144   if(!/^\d+$/.test(k))return 2;
1145   //sl('zipI.verify: '+(_t.useZIP5?'Use':'Do not use')+' zip5.');
1146   if(k.length>=3)
1147    if(getZIP5(z=k.slice(0,3)), !(z in ZIP_to_cd) || _t.useZIP5&&getZIP5(z)&&(k.length==5&&!(k in ZIP5_to_town)))
1148     return 1;
1149  };
1150 
1151  a=_p.cityI;
1152  a.maxList=0,	//	unlimited
1153  a.setMaxLength(8),
1154  a.onInput=function(k){
1155   cityF.apply(_p.cityI,arguments);
1156   _p.addressI.showList(0);
1157   var c=districtL[_p.districtI.setValue()];
1158   if(!c||!(k in c)){
1159    //	選了不同的 city
1160    _p.zipI.setValue('');
1161    _p.districtI.setValue('');
1162    _p.addressI.setAllList([]);
1163    _p.zipI.showList(0),_p.districtI.showList(0);
1164   }
1165   if(!isNaN(ZIP[k]))_p.zipI.setValue(ZIP[k]);
1166   _p.districtI.setAllList(k in c_d?c_d[k]:districtL);
1167  };
1168  a.verify=function(k){
1169   if(!k || k&&!(k in ZIP))return 1;
1170  };
1171 
1172  a=_p.districtI;
1173  a.maxList=20,
1174  a.setMaxLength(20),
1175  a.onList=function(l,i){
1176   if(l instanceof Array)
1177    return [l[i],l[i]];
1178   for(var d in l[i])break;
1179   return [i,d];
1180  };
1181  a.onInput=function(k){
1182   districtF.apply(_p.districtI,arguments);
1183   _p.addressI.showList(0);
1184   var c=districtL[k],i=_p.cityI.setValue();
1185   if(c && !(i in c)){
1186    for(var i in c){c=i;break;}
1187    _p.cityI.showList(0);
1188    _p.cityI.setValue(c);
1189   }else c=i;
1190   if(c in ZIP){
1191    _p.cityI.showList(0);
1192    var z=ZIP[c];
1193    if(typeof z=='object')
1194     z=_p.districtI.setValue() in z?ZIP[c][_p.districtI.setValue()]:0;
1195    if(z)_p.zipI.setValue(z),_p.zipI.showList(0);
1196 
1197    //sl('ZIP['+c+']['+_p.districtI.setValue()+']=['+z+']');
1198    //if(!z){var i;z=ZIP[c];for(var i in z)sl('* ['+i+']='+z[i]);}
1199   }
1200  };
1201  a.verify=function(k){
1202   var c=_p.cityI.setValue();
1203   if(!k || k&& ((c in ZIP)&&typeof ZIP[c]=='object'&&!(k in ZIP[c])) || !c_d[c])return 1;
1204  };
1205 
1206  a=_p.addressI;
1207  a.maxList=40,
1208  a.onList=function(l,i){
1209   return [l[i]||i,l instanceof Array?l[i]:i,_p.addressI.setValue()];
1210  };
1211  a.onSelect=function(l,i){
1212   var c=i.indexOf(',');
1213   _p.zipI.setValue(i.slice(0,c));
1214   _p.zipI.triggerToInput(0);
1215   return i.slice(c+1);
1216  };
1217  a.setSearch('includeKey');
1218  a.setProperty('onfocus',function(){
1219   var c=_p.cityI.setValue(),d=_p.districtI.setValue();
1220   if(c && (c in ZIP) && typeof ZIP[c]=='object' && (d in ZIP[c])){
1221    _p.zipI.triggerToInput(0);
1222    _p.cityI.triggerToInput(0);
1223    _p.districtI.triggerToInput(0);
1224    //sl('addressI.onfocus: '+(_t.useZIP5?'Use':'Do not use')+' zip5.');
1225    if(_p.addressI.doFunc)_p.addressI.doFunc=0;
1226    else if(_t.useZIP5)
1227     _p.addressI.setAllList(getZIP5(ZIP[c][d])),
1228     _p.addressI.onInput();
1229   }
1230  });
1231  a.verify=function(k){
1232   if(!k||k.length<5)return 1;
1233  };
1234 
1235  _t.setNamePrefix(prefix);
1236 
1237  _t.loaded=1;
1238 
1239 },_=function(){initI.apply(this,arguments);},
1240 
1241 
1242 //	(instance private handle)	不要 instance private 的把這函數刪掉即可。
1243 _p='_'+(Math.random()+'').replace(/\./,''),
1244 //	get private variables (instance[,destroy]), init private variables (instance[,access function list[, instance destructor]])
1245 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:{};};
1246 
1247 //	class destructor	---------------------------
1248 /*
1249 please call at last (e.g., window.unload)
1250 
1251 usage:
1252 classT=classT.destroy();
1253 or if you has something more to do:
1254 classT.destroy()&&classT=null;
1255 */
1256 
1257 _.destroy=function(){for(var i=0;i<instanceL.length;i++)instanceL[i].destroy();_p();};
1258 
1259 //	(instance private handle, continue)
1260 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+'={};}})();');
1261 _p.toString=function(){return'';};
1262 
1263 
1264 //	class public interface	---------------------------
1265 
1266 
1267 //	預設 className
1268 _.classNameSet={
1269  clearMark:'adr_clear',
1270  zipI:'adr_zip',
1271  addressI:'adr_address'
1272 };
1273 
1274 
1275 //	read 郵局提供之 CSV file。
1276 //	這應該在所有 new 之前先作!
1277 _.readData=function(url){
1278  if(!url)return;
1279  path=url.match(/^(.+\/)?([^\/]+)$/)[1];
1280 
1281  var data,i=0,a,b;
1282  try{
1283   a=getU(url);
1284  }catch(e){
1285   //sl('readData: <em>Can not get data: ['+url+']!</em> '+e.message);
1286   return;
1287  }
1288  //sl('readData: Get data from ['+url+']:<br/>['+a.length+'] '+a.slice(0,200)+'..');
1289  if(!a||!(data=parseCSV(a))||data.length<9||data[0].length<3){
1290   //sl('readData: Can not read data from ['+url+']!');
1291   return;
1292  }
1293 
1294  //	reset
1295  ZIP={},ZIP5=[],ZIP_to_cd={},ZIP5_to_town={},ENG={},c_d={},cityL={},districtL={};
1296 
1297  //sl('readData: Get '+data.length+' data from ['+url+']:<br/>['+data[0]+']<br/>['+data[1]+']<br/>['+data[2]+']');
1298  for(;i<data.length;i++){
1299   a=data[i][1].match(/^([^縣市島]{1,3}[縣市島])(.{2,5})$/);
1300   if(!a){
1301    //sl('Can not parse: ['+data[i][1]+']');//continue;
1302    cityL[a=data[i][1]]='';
1303    //districtL[a]='';
1304    ZIP_to_cd[ZIP[a]=data[i][0]]=[a],ENG[a]=data[i][2];
1305   }else{
1306    b=a[2],a=a[1];
1307    if(!(b in districtL))districtL[b]={};
1308    districtL[b][a]=1;	//	districtL[_district_name_]={_city_name_:1}
1309 /*
1310    if(b in districtL)
1311     sl('readData: duplicate district: '+a+','+b),
1312     districtL[b+','+a]=[b,a];
1313    else
1314     //sl('readData: set district: '+a+','+b),
1315     districtL[b]=[b,a];
1316 */
1317    if(a in c_d)c_d[a].push(b);else c_d[a]=[b];
1318 
1319    if(!(a in ZIP))ZIP[a]={},ENG[a]={};
1320    ZIP_to_cd[ZIP[a][b]=data[i][0]]=[a,b],ENG[a][b]=data[i][2];
1321 
1322    //sl('ZIP['+a+']['+b+']=['+data[i][0]+']');
1323   }
1324  }
1325 
1326  a=cityL,cityL={};
1327  for(i in c_d)c_d[i].sort(),cityL[i]=i+' ('+c_d[i].length+')';	//	cityL[_city_name_]='_city_name_ (districts number)'
1328  for(i in a)cityL[i]=a[i];	//	將不常用(沒district)的放後面
1329 };
1330 
1331 //	class constructor	---------------------------
1332 
1333 /*	預先讀取同目錄下的 county.txt
1334 這些檔案由臺灣郵政全球資訊網下載專區取得。
1335 
1336 */
1337 _.readData(path+'zip/county.txt');
1338 
1339 
1340 _.prototype={
1341 //	應該盡量把東西放在 class,instance少一點…?
1342 
1343 //	instance public interface	-------------------
1344 
1345 //	使用 ZIP5
1346 useZIP5:1,
1347 
1348 //	** important ** 這邊不能作 object 之 initialization,否則因為 object 只會 copy reference,因此 new 時東西會一樣。initialization 得在 _() 中作!
1349 //nameSet:{},
1350 
1351 setNamePrefix:function(p){
1352  var _t=this,_p=pv(_t);
1353  if(typeof p!='undefined'){
1354   _p.fullAdr.name=_p.namePrefix=p;
1355   _p.zipI.setName(p+_t.nameSet.zip);
1356   _p.cityI.setName(p+_t.nameSet.city);
1357   _p.districtI.setName(p+_t.nameSet.district);
1358   _p.addressI.setName(p+_t.nameSet.address);
1359  }
1360  return _p.namePrefix;
1361 },
1362 
1363 setAddress:function(adr){
1364  var _p=pv(this),r;
1365  if(typeof adr=='object')
1366   _p.zipI.setValue(adr.zip),_p.cityI.setValue(adr.city),_p.districtI.setValue(adr.district),_p.addressI.setValue(adr.address);
1367 
1368  r={zip:_p.zipI.setValue(),city:_p.cityI.setValue(),district:_p.districtI.setValue(),address:_p.addressI.setValue()};
1369  r.fullAddress=(r.zip?'['+r.zip+'] ':'')+r.city+r.district+r.address;//'台灣'+
1370  return r;
1371 },
1372 
1373 
1374 //	use instance.submit() to check
1375 submit:function(n){
1376  var _t=this,_p=pv(_t);
1377  if(!_t.loaded)return true;
1378  if(typeof n!='undefined'){
1379   _p.zipI.setName('');
1380   _p.cityI.setName('');
1381   _p.districtI.setName('');
1382   _p.addressI.setName('');
1383   if(n)_p.fullAdr.name=n;
1384  }
1385  _p.fullAdr.value=_t.setAddress().fullAddress;
1386  return !_p.zipI.verify(_p.zipI.setValue()) && !_p.cityI.verify(_p.cityI.setValue()) && !_p.districtI.verify(_p.districtI.setValue()) && !_p.addressI.verify(_p.addressI.setValue());
1387 },
1388 
1389 
1390 //	增加功能 button
1391 addFunc:function(t,f){	//	(text, function)
1392  addFunc.apply(this,arguments);
1393 },
1394 
1395 
1396 //	(focus on what <input>, focus or blur)
1397 focus:function(i,f){
1398  var j,_p=pv(this),alias={a:'addressI',z:'zipI',d:'districtI',c:'cityI'};
1399  if(i in alias)i=alias[i];else if(i+'I' in _p)i+='I';
1400  if(i in _p)
1401   _p[i].focus(f);
1402  else if(!i)	//	to all
1403   for(j in alias)_p[alias[j]].focus(f);
1404 },
1405 
1406 
1407 //	instance destructor	---------------------------
1408 /*
1409 usage:
1410 instance=instance.destroy();
1411 or if you has something more to do:
1412 instance.destroy()&&instance=null;
1413 */
1414 destroy:function(){
1415  var _t=this,_p=pv(_t);
1416  _p.zipI.destroy();
1417  _p.cityI.destroy();
1418  _p.districtI.destroy();
1419  _p.addressI.destroy();
1420  pv(_t,1);
1421 }
1422 };	//	_.prototype=
1423 
1424 return _;
1425 })();	//	(function(){
1426 
1427 //	===================================================
1428 
1429 
1430 
1431 
1432 //	===================================================
1433 /*
1434 	used for account & bank id input form
1435 
1436 TODO:
1437 
1438 
1439 HISTORY:
1440 2008/7/26 14:46:14	create
1441 */
1442 
1443 AccountF.TW=
1444 
1445 (/*ClassT.AccountF.TW=*/function(){
1446 
1447 var
1448 
1449 
1450 //	class private	-----------------------------------
1451 
1452 //	存放 data 的 path
1453 path,
1454 
1455 
1456 // 總單位/支單位帳號長度
1457 mainLen=3,
1458 branchLen=7,
1459 
1460 /*
1461 
1462 //	and, ChooseI 用
1463 bank[Number(id)]={
1464 	id:'\d'			//	通匯金融代號, 郵局或是銀行代碼
1465 	name:'',		//	總單位名稱
1466 	digital:\d || [\d,..],	//	帳號長度之描述
1467 	maxD:\d,		//	max 長度
1468 	minD:\d,		//	min 長度
1469 	branch:{		//	分行
1470 		通匯金融代號:支單位名稱,..
1471 	}
1472 }
1473 
1474 */
1475 
1476 bank=[],
1477 bankNow,bankIdNow,
1478 
1479 getBankID=function(id,force){
1480  var o=bank[id=Math.floor(id)],l,d;
1481  if(!o)return;
1482  if(!force&&('branch' in o))return o.branch;
1483 
1484  //sl('getBankID: load ['+path+'id'+(id>99?'':id>9?'0':'00')+id+'.csv]');
1485  try{
1486   d=getU(path+'id'+(id>99?'':id>9?'0':'00')+id+'.csv');
1487  }catch(e){
1488   //sl('getBankID: <em>Can not get data: ['+url+']!</em> '+e.message);
1489   return;
1490  }
1491  if(!d||!(d=parseCSV(d))||!d.length){
1492   //sl('getBankID: Can not read data from ['+url+']!');
1493   return;
1494  }
1495 
1496  
1497  for(i=0,l=o.branch={};i<d.length;i++)
1498   if(!isNaN(d[i][0]))
1499    //sl('getBankID: branch ['+d[i][0]+'] '+d[i][1]),
1500    l[d[i][0]]=d[i][1];
1501 
1502  return l;
1503 },
1504 
1505 //	將帳號長度之描述轉成帳號長度, return max digital
1506 getDigital=function(id){
1507  var o=bank[id=Math.floor(id)],d,a,i=0,m,max=0,min=Number.MAX_VALUE;
1508  if(!o)return;	//	error
1509  if('maxD' in o)return o.maxD;	//	作過了
1510 
1511  //sl('getDigital: get id '+id+', parse ['+o.digital+']');
1512  d=o.digital,a=d.replace(/\n/g,'').match(/\d{1,2}位/g);
1513 
1514  if(a)	//	有可能資料錯誤,無法取得。
1515   for(d=[];i<a.length;i++)
1516    if(m=a[i].match(/\d{1,2}/)){
1517     d.push(m=Math.floor(m[0]));
1518     if(min>m)min=m;
1519     if(max<m)max=m;
1520    }
1521 
1522  if(!d.length)d=max=min=0;
1523  else if(d.length==1)d=max=min=d[0];
1524 
1525  //sl('getDigital: '+o.name+' '+min+'-'+max);
1526  o.maxD=max,o.minD=min;
1527 
1528  return max;
1529 },
1530 
1531 //	模擬 inherits
1532 _=ChooseI.clone(function(){
1533 	var _t=this,i;
1534 	if(!_t.loaded)return;
1535 
1536 	_t.setClassName('accountF_input');
1537 	_t.setSearch(function(i,k){
1538 	 //if(k)sl('compare function: ['+k+'], ['+(typeof i)+']'+i);
1539 	 return typeof i=='object'?
1540 	  //	bank
1541 	  i.id.slice(0,k.length)==k||i.name.indexOf(k)!=-1
1542 	  //	bank.branch
1543 	  :i.length<k.length?0/*i==k.slice(0,i.length)*/:i.slice(0,k.length)==k;
1544 	});
1545 	_t.setInputType(1);
1546 	i=_t.onInput;
1547 	(_t.onInput=function(k){
1548 	 //sl('onInput: input ['+k+'] - '+k.slice(0,3))
1549 	 if(_t.inputAs!=2&&k&&k.length>=mainLen){
1550 	  var id=Math.floor(k.slice(0,mainLen)),l;
1551 	  if((bank[id])&&(l=getBankID(id))&&l!==_t.setAllList())
1552 	   bankNow=bank[bankIdNow=id].name,_t.setInputType(0,id),_t.setAllList(l);
1553 	 }else if(bank!==_t.setAllList())bankNow=0,bankIdNow=-1,_t.setInputType(0,-1),_t.setAllList(bank);
1554 	 //	執行主要功能
1555 	 i.apply(_t,arguments);
1556 	 //	若達到標標準,則 triggerToInput。
1557 	 if(!_t.clickNow&&k&&(_t.inputAs==2&&k.length==mainLen||_t.inputAs==3&&k.length==branchLen||k.length==getDigital(bankIdNow)))
1558 	  _t.triggerToInput(0);
1559 	 else _t.focus();
1560 	})();
1561 	_t.triggerToInput(1);
1562 	_t.focus(0);
1563 }),
1564 _p=_.prototype;
1565 
1566 
1567 //	class public interface	---------------------------
1568 
1569 
1570 //	read bank id data
1571 //	這應該在所有 new 之前先作!
1572 _.readData=function(url){
1573  if(!url)return;
1574  path=url.match(/^(.+\/)?([^\/]+)$/)[1];
1575 
1576  var data,i=0,a,b;
1577  try{
1578   a=getU(url);
1579  }catch(e){
1580   //sl('readData: <em>Can not get data: ['+url+']!</em> '+e.message);
1581   return;
1582  }
1583  if(!a||!(data=parseCSV(a))||data.length<9||data[0].length<3){
1584   //sl('readData: Can not read data from ['+url+']!');
1585   return;
1586  }
1587 
1588  //	reset
1589  bank=[];
1590 
1591  for(;i<data.length;i++){
1592   a=data[i];
1593   bank[Math.floor(a[0])]={
1594 	id:a[0],	//	通匯金融代號
1595 	name:a[1],	//	總單位名稱
1596 	digital:a[2]	//	帳號長度之描述
1597   };
1598  }
1599 
1600 };
1601 
1602 
1603 //	class constructor	---------------------------
1604 
1605 _.readData('bank/id.csv');
1606 
1607 
1608 //	不再使繼承
1609 delete _.clone;
1610 
1611 
1612 //	instance public interface	-------------------
1613 
1614 //	1: all, 2: 到總單位, 3: 到支單位
1615 _p.setInputType=function(t,i){	//	(type,id)
1616  var _t=this;
1617  if(t)_t.inputAs=t,i=i||-1;
1618  t=_t.inputAs;
1619  if(i)_t.setMaxLength(t==2?mainLen:t==3?branchLen:i<0?20:getDigital(i)?mainLen+getDigital(i):20);	//	mainLen+getDigital(i): 看來似乎必要這麼做
1620  return t;
1621 };
1622 
1623 //	input: (list, index), return [value, title[, key=title||value]]
1624 _p.onList=function(l,i){
1625  if(bankNow)return [l[i],i+' '+bankNow];
1626  else if(i in l)return [l[i].name,l[i].id];
1627 };
1628 
1629 //	input: (list, index), return value to set as input key
1630 _p.onSelect=function(l,i){
1631  return bankNow?i:l[i].id;
1632 };
1633 
1634 _p.verify=function(k){
1635  //sl('verify ['+k+']');
1636  var m;
1637  if(!k&&k!==0)return 1;
1638  if(!/^\d+$/.test(k))return 2;
1639  if(k.length>=mainLen)
1640   if(!bank[m=Math.floor(k.slice(0,mainLen))] || k.length>=branchLen&&bank[m].branch&&!(k.slice(0,branchLen) in bank[m].branch))
1641    return 1;
1642 };
1643 
1644 return _;
1645 })();	//	(function(){
1646 
1647 //	===================================================
1648 
1649 
1650 
1651 //	prevent re-use. 防止再造 
1652 delete ChooseI.clone;
1653 
1654 
1655 
1656 
1657 
1658 
1659 return (
1660 	CeL.net.form
1661 );
1662 };
1663 
1664 //===================================================
1665 
1666 CeL.setup_module(module_name, code_for_including);
1667 
1668 };
1669