      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