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