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