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