      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