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