1 2 /** 3 * @name CeL address input function 4 * @fileoverview 5 * 本檔案包含了地址輸入表單的 functions。 6 * @since 7 */ 8 9 10 /* 11 改成僅用單一格子 12 http://blog.darkthread.net/blogs/darkthreadtw/archive/2010/06/01/taiwan-addr-helper.aspx 13 輸入*,? 14 */ 15 16 if (typeof CeL === 'function') 17 CeL.setup_module('interact.form.address', 18 { 19 require : 'interact.form.select_input.|data.CSV.parse_CSV', 20 code : function(library_namespace, load_arguments) { 21 22 // requiring 23 var parse_CSV; 24 eval(library_namespace.use_function(this)); 25 // .use_function 不會 extend module,因此得自己設定。 26 var select_input = library_namespace.select_input; 27 28 29 /** 30 * JavaScript 地址輸入表單支援 (address input form), 31 * 現有台灣(.TW)可用。 32 * @class form 的 functions 33 */ 34 CeL.interact.form.address 35 = function() { 36 // null module constructor 37 }; 38 39 /** 40 * for JSDT: 有 prototype 才會將之當作 Class 41 */ 42 CeL.interact.form.address 43 .prototype = { 44 }; 45 46 47 48 49 50 /** 51 * 簡易型 net.web.XML_node @ interact.form.select_input 52 * @param tag p.appendChild tag 53 * @param p parent node 54 * @param t text 55 * @param classN className 56 * @inner 57 * @ignore 58 * @return 59 */ 60 var create_DO = function(tag, p, t, classN) { 61 var _e; 62 if (t && (typeof t != 'object' || (t instanceof Object))) 63 t = document.createTextNode(t + ''); 64 if (typeof tag == 'object') { 65 _e = tag; 66 } else if (tag) { 67 _e = document.createElement(tag); 68 if (classN) 69 _e.className = classN; 70 if (t) 71 _e.appendChild(t); 72 } else if (t) 73 _e = t; 74 if (p && _e) 75 p.appendChild(_e); 76 return _e; 77 }; 78 79 80 //ClassT={Account_input:{},Address_input:{}}, // class template set 81 82 83 // =================================================== 84 /* 85 used for address input form 86 住址輸入 87 88 TODO: 89 parse address 90 91 92 HISTORY: 93 2008/7/24 20:38:18 create 94 */ 95 96 _.TW= 97 98 (/*ClassT.Address_input.TW=*/function(){ 99 100 var 101 102 // class private ----------------------------------- 103 104 /* 存放 data 的 path 105 path/: 106 zip/ ZIP code data 107 108 */ 109 path=library_namespace.get_module_path(this,''),// './', 110 111 /* 112 113 ZIP[city: 縣市][district: 鄉鎮市區]=ZIP code 114 115 ZIP5[Number(zip3)]={ "zip5,路街":"路街,no_range",.. } 116 117 ZIP_to_cd[String(zip3)]=[city,district]; 118 119 town/area/road/scoop 120 ZIP5_to_town[String(zip5)]=[路街,no_range] 121 122 c_d[city]=[district list] 123 124 ENG[city][district]=english 125 126 127 cityL, districtL: list for select_input 128 cityL[_city_name_]='_city_name_ (districts number)' 129 districtL[_district_name_]={city_name_:1} 130 131 */ 132 ZIP={},ZIP5=[],ZIP_to_cd={},ZIP5_to_town={},ENG={},c_d={},cityL={},districtL={}, 133 134 getZIP5=function(z3,force){ 135 var d,i=0,a,Z; 136 z3=Math.floor(z3); 137 if(!(z3 in ZIP5)||!ZIP5[z3]) 138 Z=ZIP5[z3]={}; 139 else if(Z=ZIP5[z3],!force)return Z; 140 141 try{ 142 d=library_namespace.get_file(path+'zip'+(z3>99?'':z3>9?'0':'00')+z3+'.csv'); 143 }catch(e){ 144 library_namespace.warn('getZIP5: Can not get ZIP5 data of ['+(z3>99?'':z3>9?'0':'00')+z3+']! ' + e.message); 145 return; 146 } 147 148 if(d&&(d=parse_CSV(d)))for(;i<d.length;i++) 149 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]+/,'');//去除前空白 150 //else sl('Can not parse ZIP5 data of ['+(z3>99?'':z3>9?'0':'00')+z3+']!'); 151 152 //sl('getZIP5: ['+(z3>99?'':z3>9?'0':'00')+z3+'] '+d.length+' records.'); 153 154 return Z; 155 }, 156 157 158 addFunc=function(t,f){ 159 var _t=this,_p=pv(_t); 160 create_DO(0,_p.container,' ['); 161 (create_DO('span',_p.container,t,_.classNameSet.clearMark)) 162 // TODO: do not use arguments 163 .onclick=arguments.callee.caller===initI?function(){f.apply(_t);}:f;//function(){f(_p.zipI,_p.cityI,_p.districtI,_p.addressI);}; 164 create_DO(0,_p.container,']'); 165 }, 166 167 168 // instance constructor --------------------------- 169 instanceL=[], 170 initI=function(o,prefix){ 171 if(typeof o!='object')o=document.getElementById(o); 172 if(!o){ 173 //throw new Error('Can not get outter document object!'); 174 return; 175 } 176 177 if(!prefix)prefix='adr_'; 178 179 var _t=this,_p=pv(_t),a; 180 instanceL.push(_t); // for destructor 181 _p.container=o; // 容器 182 183 // initial instance object 184 _t.nameSet={ 185 zip:'zip', 186 city:'city', 187 district:'district', 188 address:'address' 189 }; 190 191 // layout setup 192 _p.fullAdr=create_DO('input',o); // 最後送出時用 193 try{_p.fullAdr['type']='hidden';}catch(e){} // 低版本 JScript: error 194 _p.fullAdr.style.display='none'; 195 196 var zipOTrg,zipT=create_DO('span',o,'郵遞區號'); // TODO: <label> 197 a=_p.zipI=new select_input(o,ZIP_to_cd); 198 a.setClassName(_.classNameSet.zipI); 199 a.setTitle('郵遞區號'); 200 201 _p.zipI.dInputted=function(){ 202 return '['+this.setValue()+']'; 203 }; 204 205 zipOTrg=_p.zipI.triggerToInput; 206 _p.zipI.triggerToInput=function(y){ 207 if(y=y||typeof y=='undefined'){ 208 zipT.style.display='inline'; 209 zipOTrg.call(_p.zipI,y); 210 }else{ 211 zipT.style.display='none'; 212 zipOTrg.call(_p.zipI,y); 213 } 214 }; 215 216 create_DO(0,o,' ');//地址: 217 (_p.cityI=new select_input(o,cityL)).setTitle('縣市'); 218 (_p.districtI=new select_input(o,districtL)).setTitle('鄉鎮市區'); 219 a=_p.addressI=new select_input(o); 220 a.autoShowArrow=1; 221 a.setClassName(_.classNameSet.addressI); 222 223 /* 224 addFunc.call(_t,'全關閉',function(){ 225 var _p=pv(this); 226 //sl('全關閉: clear all value.'); 227 _p.zipI.showList(0); 228 _p.cityI.showList(0); 229 _p.districtI.showList(0); 230 _p.addressI.showList(0); 231 }); 232 */ 233 addFunc.call(_t,'全清除',function(){ 234 var _p=pv(this); 235 //sl('全清除: clear all value.'); 236 _p.zipI.setValue(''), _p.zipI.showList(0), _p.zipI.triggerToInput(); 237 _p.cityI.setValue(''), _p.cityI.showList(0), _p.cityI.triggerToInput(); 238 _p.districtI.setValue(''), _p.districtI.showList(0), _p.districtI.triggerToInput(); 239 _p.addressI.setValue(''), _p.addressI.showList(0), _p.addressI.triggerToInput(); 240 _p.addressI.setAllList(null); 241 }); 242 243 // 功能設定 244 var zipF=_p.zipI.setSearch('startWith'), 245 cityF=_p.cityI.setSearch('includeKey'), 246 districtF=_p.districtI.setSearch('includeKey') 247 ; 248 249 a=_p.zipI; 250 a.maxList=20, 251 a.setMaxLength(5), 252 a.onInput=function(k){ 253 zipF.apply(_p.zipI,arguments); 254 _p.addressI.showList(0); 255 if(k in ZIP_to_cd){ 256 var a=ZIP_to_cd[k]; 257 if(a[0])_p.cityI.setValue(a[0]),_p.cityI.showList(0); 258 if(a[1])_p.districtI.setValue(a[1]),_p.districtI.showList(0); 259 }else if((k in ZIP5_to_town)&&!_p.addressI.setValue()) 260 _p.addressI.setValue(ZIP5_to_town[k]),_p.addressI.showList(0); 261 }; 262 a.verify=function(k){ 263 if(!k&&k!==0)return 1; 264 var z; 265 if(!/^\d+$/.test(k))return 2; 266 //sl('zipI.verify: '+(_t.useZIP5?'Use':'Do not use')+' zip5.'); 267 if(k.length>=3) 268 if(getZIP5(z=k.slice(0,3)), !(z in ZIP_to_cd) || _t.useZIP5&&getZIP5(z)&&(k.length==5&&!(k in ZIP5_to_town))) 269 return 1; 270 }; 271 272 a=_p.cityI; 273 a.maxList=0, // unlimited 274 a.setMaxLength(8), 275 a.onInput=function(k){ 276 cityF.apply(_p.cityI,arguments); 277 _p.addressI.showList(0); 278 var c=districtL[_p.districtI.setValue()]; 279 if(!c||!(k in c)){ 280 // 選了不同的 city 281 _p.zipI.setValue(''); 282 _p.districtI.setValue(''); 283 _p.addressI.setAllList([]); 284 _p.zipI.showList(0),_p.districtI.showList(0); 285 } 286 if(!isNaN(ZIP[k]))_p.zipI.setValue(ZIP[k]); 287 _p.districtI.setAllList(k in c_d?c_d[k]:districtL); 288 }; 289 a.verify=function(k){ 290 if(!k || k&&!(k in ZIP))return 1; 291 }; 292 293 a=_p.districtI; 294 a.maxList=20, 295 a.setMaxLength(20), 296 a.onList=function(l,i){ 297 if(l instanceof Array) 298 return [l[i],l[i]]; 299 for(var d in l[i])break; 300 return [i,d]; 301 }; 302 a.onInput=function(k){ 303 districtF.apply(_p.districtI,arguments); 304 _p.addressI.showList(0); 305 var c=districtL[k],i=_p.cityI.setValue(); 306 if(c && !(i in c)){ 307 for(var i in c){c=i;break;} 308 _p.cityI.showList(0); 309 _p.cityI.setValue(c); 310 }else c=i; 311 if(c in ZIP){ 312 _p.cityI.showList(0); 313 var z=ZIP[c]; 314 if(typeof z=='object') 315 z=_p.districtI.setValue() in z?ZIP[c][_p.districtI.setValue()]:0; 316 if(z)_p.zipI.setValue(z),_p.zipI.showList(0); 317 318 //sl('ZIP['+c+']['+_p.districtI.setValue()+']=['+z+']'); 319 //if(!z){var i;z=ZIP[c];for(var i in z)sl('* ['+i+']='+z[i]);} 320 } 321 }; 322 a.verify=function(k){ 323 var c=_p.cityI.setValue(); 324 if(!k || k&& ((c in ZIP)&&typeof ZIP[c]=='object'&&!(k in ZIP[c])) || !c_d[c])return 1; 325 }; 326 327 a=_p.addressI; 328 a.maxList=40, 329 a.onList=function(l,i){ 330 return [l[i]||i,l instanceof Array?l[i]:i,_p.addressI.setValue()]; 331 }; 332 a.onSelect=function(l,i){ 333 var c=i.indexOf(','); 334 _p.zipI.setValue(i.slice(0,c)); 335 _p.zipI.triggerToInput(0); 336 return i.slice(c+1); 337 }; 338 a.setSearch('includeKey'); 339 a.setProperty('onfocus', function() { 340 var c = _p.cityI.setValue(), d = _p.districtI.setValue(); 341 if (c && (c in ZIP) && typeof ZIP[c] === 'object' && (d in ZIP[c])) { 342 _p.zipI.triggerToInput(0); 343 _p.cityI.triggerToInput(0); 344 _p.districtI.triggerToInput(0); 345 // sl('addressI.onfocus: '+(_t.useZIP5?'Use':'Do not use')+' zip5.'); 346 if (_p.addressI.doFunc) 347 _p.addressI.doFunc = 0; 348 else if (_t.useZIP5) 349 _p.addressI.setAllList(getZIP5(ZIP[c][d])), _p.addressI.onInput(); 350 } 351 }); 352 a.verify = function(k) { 353 if (!k || k.length < 5) 354 return 1; 355 }; 356 357 _t.setNamePrefix(prefix); 358 359 _t.loaded=1; 360 361 },_=function(){initI.apply(this,arguments);}, 362 363 364 // (instance private handle) 不要 instance private 的把這函數刪掉即可。 365 _p='_'+(Math.random()+'').replace(/\./,''), 366 // get private variables (instance[,destroy]), init private variables (instance[,access function list[, instance destructor]]) 367 // TODO: do not use arguments 368 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:{};}; 369 370 // class destructor --------------------------- 371 /* 372 please call at last (e.g., window.unload) 373 374 usage: 375 classT=classT.destroy(); 376 or if you has something more to do: 377 classT.destroy()&&classT=null; 378 */ 379 380 _.destroy=function(){for(var i=0;i<instanceL.length;i++)instanceL[i].destroy();_p();}; 381 382 // (instance private handle, continue) 383 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+'={};}})();'); 384 _p.toString=function(){return'';}; 385 386 387 // class public interface --------------------------- 388 389 390 // 預設 className 391 _.classNameSet={ 392 clearMark:'adr_clear', 393 zipI:'adr_zip', 394 addressI:'adr_address' 395 }; 396 397 398 // 初始設定並讀取郵局提供之 CSV file。 399 // 這應該在所有 new 之前先作! 400 _.readData=function(url){ 401 if(!url)return; 402 path=url.match(/^(.+\/)?([^\/]+)$/)[1]; 403 404 var data,i=0,a,b; 405 try{ 406 a=library_namespace.get_file(url); 407 }catch(e){ 408 library_namespace.warn('interact.form.address.readData: Can not get data: ['+url+']! <em>本 module 須以 Ajax 載入資料!</em> ' + e.message); 409 return; 410 } 411 //library_namespace.log('readData: Get data from ['+url+']:<br/>['+a.length+'] '+a.slice(0,200)+'..'); 412 if(!a||!(data=parse_CSV(a))||data.length<9||data[0].length<3){ 413 //sl('readData: Can not read data from ['+url+']!'); 414 return; 415 } 416 417 // reset 418 ZIP={},ZIP5=[],ZIP_to_cd={},ZIP5_to_town={},ENG={},c_d={},cityL={},districtL={}; 419 420 //sl('readData: Get '+data.length+' data from ['+url+']:<br/>['+data[0]+']<br/>['+data[1]+']<br/>['+data[2]+']'); 421 for(;i<data.length;i++){ 422 a=data[i][1].match(/^([^縣市島]{1,3}[縣市島])(.{2,5})$/); 423 if(!a){ 424 //sl('Can not parse: ['+data[i][1]+']');//continue; 425 cityL[a=data[i][1]]=''; 426 //districtL[a]=''; 427 ZIP_to_cd[ZIP[a]=data[i][0]]=[a],ENG[a]=data[i][2]; 428 }else{ 429 b=a[2],a=a[1]; 430 if(!(b in districtL))districtL[b]={}; 431 districtL[b][a]=1; // districtL[_district_name_]={_city_name_:1} 432 /* 433 if(b in districtL) 434 sl('readData: duplicate district: '+a+','+b), 435 districtL[b+','+a]=[b,a]; 436 else 437 //sl('readData: set district: '+a+','+b), 438 districtL[b]=[b,a]; 439 */ 440 if(a in c_d)c_d[a].push(b);else c_d[a]=[b]; 441 442 if(!(a in ZIP))ZIP[a]={},ENG[a]={}; 443 ZIP_to_cd[ZIP[a][b]=data[i][0]]=[a,b],ENG[a][b]=data[i][2]; 444 445 //sl('ZIP['+a+']['+b+']=['+data[i][0]+']'); 446 } 447 } 448 449 a=cityL,cityL={}; 450 for(i in c_d)c_d[i].sort(),cityL[i]=i+' ('+c_d[i].length+')'; // cityL[_city_name_]='_city_name_ (districts number)' 451 for(i in a)cityL[i]=a[i]; // 將不常用(沒district)的放後面 452 }; 453 454 // class constructor --------------------------- 455 456 /* 預先讀取同目錄下的 county.txt 457 這些檔案由臺灣郵政全球資訊網下載專區取得。 458 459 */ 460 _.readData(path+'zip/county.txt'); 461 462 463 _.prototype={ 464 // 應該盡量把東西放在 class,instance少一點…? 465 466 // instance public interface ------------------- 467 468 // 使用 ZIP5 469 useZIP5:1, 470 471 // ** important ** 這邊不能作 object 之 initialization,否則因為 object 只會 copy reference,因此 new 時東西會一樣。initialization 得在 _() 中作! 472 //nameSet:{}, 473 474 setNamePrefix:function(p){ 475 var _t=this,_p=pv(_t); 476 if(typeof p!='undefined'){ 477 _p.fullAdr.name=_p.namePrefix=p; 478 _p.zipI.setName(p+_t.nameSet.zip); 479 _p.cityI.setName(p+_t.nameSet.city); 480 _p.districtI.setName(p+_t.nameSet.district); 481 _p.addressI.setName(p+_t.nameSet.address); 482 } 483 return _p.namePrefix; 484 }, 485 486 setAddress:function(adr){ 487 var _p=pv(this),r; 488 if(typeof adr==='object') 489 _p.zipI.setValue(adr.zip),_p.cityI.setValue(adr.city),_p.districtI.setValue(adr.district),_p.addressI.setValue(adr.address); 490 491 r={zip:_p.zipI.setValue(),city:_p.cityI.setValue(),district:_p.districtI.setValue(),address:_p.addressI.setValue()}; 492 r.fullAddress=(r.zip?'['+r.zip+'] ':'')+r.city+r.district+r.address;//'台灣'+ 493 return r; 494 }, 495 496 497 // use instance.submit() to check 498 submit : function(n) { 499 var _t = this, _p = pv(_t); 500 if (!_t.loaded) 501 return true; 502 503 if (typeof n != 'undefined') { 504 _p.zipI.setName(''); 505 _p.cityI.setName(''); 506 _p.districtI.setName(''); 507 _p.addressI.setName(''); 508 if (n) 509 _p.fullAdr.name = n; 510 } 511 512 _p.fullAdr.value = _t.setAddress().fullAddress; 513 514 return !_p.zipI.verify(_p.zipI.setValue()) 515 && !_p.cityI.verify(_p.cityI.setValue()) 516 && !_p.districtI.verify(_p.districtI.setValue()) 517 && !_p.addressI.verify(_p.addressI.setValue()); 518 }, 519 520 521 // 增加功能 button 522 addFunc:function(t,f){ // (text, function) 523 addFunc.apply(this,arguments); 524 }, 525 526 527 // (focus on what <input>, focus or blur) 528 focus : function(i, f) { 529 var j, _p = pv(this), alias = { 530 a : 'addressI', 531 z : 'zipI', 532 d : 'districtI', 533 c : 'cityI' 534 }; 535 536 if (i in alias) 537 i = alias[i]; 538 else if (i + 'I' in _p) 539 i += 'I'; 540 if (i in _p) 541 _p[i].focus(f); 542 else if (!i) 543 // to all 544 for (j in alias) 545 _p[alias[j]].focus(f); 546 }, 547 548 549 // instance destructor --------------------------- 550 /* 551 usage: 552 instance=instance.destroy(); 553 or if you has something more to do: 554 instance.destroy()&&instance=null; 555 */ 556 destroy:function(){ 557 var _t=this,_p=pv(_t); 558 _p.zipI.destroy(); 559 _p.cityI.destroy(); 560 _p.districtI.destroy(); 561 _p.addressI.destroy(); 562 pv(_t,1); 563 } 564 }; // _.prototype= 565 566 return _; 567 }).call(this); // (function(){ 568 569 // =================================================== 570 571 572 573 /** 574 * 不 extend 的 member. 575 * '*': 完全不 extend. 576 * this: 連 module 本身都不 extend 到 library name-space 下. 577 * @ignore 578 */ 579 CeL.interact.form.address 580 . 581 no_extend = 'TW'; 582 583 584 return ( 585 CeL.interact.form.address 586 ); 587 } 588 589 590 }); 591 592