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