      1 ﻿
      2 /**
      3  * @name	CeL map function
      4  * @fileoverview
      5  * 本檔案包含了 map 的 functions。
      6  * @since
      7  */
      8
      9
     10 if (typeof CeL === 'function'){
     11
     12 /**
     13  * 本 module 之 name(id)，<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。
     14  * @type	String
     15  * @constant
     16  * @inner
     17  * @ignore
     18  */
     19 var module_name = 'net.map';
     20
     21 //===================================================
     22 /**
     23  * 若欲 include 整個 module 時，需囊括之 code。
     24  * @type	Function
     25  * @param	{Function} library_namespace	namespace of library
     26  * @param	load_arguments	呼叫時之 argument(s)
     27  * @return
     28  * @name	CeL.net.map
     29  * @constant
     30  * @inner
     31  * @ignore
     32  */
     33 var code_for_including = function(library_namespace, load_arguments) {
     34
     35
     36 var _module_name = module_name;
     37 //requires
     38 if (library_namespace.use( [ 'data', 'net.web' ], module_name))
     39 	return;
     40 //	module_name 會被重設
     41 module_name = _module_name;
     42
     43 var XML_node = library_namespace.net.web.XML_node,
     44 set_attribute = library_namespace.net.web.set_attribute,
     45 remove_all_child = library_namespace.net.web.remove_all_child,
     46 set_class = library_namespace.net.web.set_class,
     47 split_String_to_Object = library_namespace.data.split_String_to_Object;
     48
     49
     50 /**
     51  * null module constructor
     52  * @class	map 的 functions
     53  */
     54 CeL.net.map
     55 = function() {
     56 	//	null module constructor
     57 };
     58
     59 /**
     60  * for JSDT: 有 prototype 才會將之當作 Class
     61  */
     62 CeL.net.map
     63 .prototype = {
     64 };
     65
     66
     67
     68
     69
     70
     71
     72
     73 //	init function
     74
     75 var SL=new Debug.log,sl=function(){SL.log.apply(SL,arguments);},err=function(){SL.err.apply(SL,arguments);},warn=function(){SL.warn.apply(SL,arguments);};
     76
     77 var gMap,mapO,mData,lostItem,gLocal,dLoc={tw:[23.7,121]};	//	台灣: 23.7,121
     78
     79
     80 addLoad(function(){
     81  var b=SL.setBoard('log');
     82  if(window.location.protocol=='file:'&&b)b.style.display='block';	//	強制顯示 log
     83
     84  if(init&&init.run)sl('Page loaded. Prepare to initial..');
     85
     86  if(typeof GLatLng=='undefined'){sl('GMap does not loaded.');return;}
     87
     88  for(var i in dLoc)
     89   dLoc[i]=new GLatLng(dLoc[i][0],dLoc[i][1]);
     90
     91  setTimeout('init(0);',0);
     92 });
     93
     94 init.run=1;
     95 function init(i){
     96  if(!arguments.callee.run)return;
     97
     98  var m=0,n;
     99  switch(i){
    100   case m++:
    101    setSize();
    102    if(typeof preLoadMap=='function')preLoadMap();
    103    break;
    104   case m++:
    105    catchFile.sn='map-files';
    106    catchFile.f=function(url,success,captureId){
    107 	sl('Capture '+(success?'succeeded':'failed')+': <a href="'+url+'">'+url+'</a>');
    108    };
    109    catchFile([
    110 	'map.js',
    111 	'map.css'
    112    ]);
    113    break;
    114   case m++:
    115    initMap();
    116    initSearch();
    117    if(window.location.protocol=='file:')setTimeout('mapO.removeTM();',3000);	//	3000: 適当。隨 client 而有不同。
    118    break;
    119   case m++:
    120    readLoc();
    121    break;
    122   case m++:
    123    loadMapData();
    124    break;
    125   case m++:
    126    placeMapItem();
    127    break;
    128   case m++:
    129    if(typeof additionalFunc=='function')additionalFunc();
    130    break;
    131  }
    132  m=['Starting initial process. Catch files..','Catch done. Initial all components..','Map loaded. Loading address records..','address loaded. Loading map data..','map data loaded. placing map items..','placing done. Do additional works..','Initial done.'];
    133  sl('init: '+m[i]);
    134  if(arguments.callee.run && ++i<m.length)
    135   setTimeout('init('+i+');',1);
    136 }
    137
    138 function setOverviewMap(i){
    139  var m=mapO.overviewMap,n;
    140  if(m && (n=m.getOverviewMap())){
    141   //	因為有時來不及反應，所以放這邊。
    142   if(!i)n.addControl(new GMenuMapTypeControl(1)),i=1,setTimeout('setOverviewMap(1);',500);
    143   else m.hide();
    144  }else /*m.show(),*/setTimeout('setOverviewMap();',500);
    145 }
    146
    147
    148 function initSearch(){
    149  if(typeof google!='undefined' && google.load)google.load("search","1",{callback:function(){
    150   gLocal=new getSearch(function(r){
    151    google.search.LocalSearch.resizeStaticMapUrl(r,100,140);
    152    mapO.setLatLng(r.address||r.titleNoFormatting,r.lat,r.lng);
    153    sA(r.address,r.titleNoFormatting,r.phone.join('<br/>')+'<br/>'+r.address);
    154    //	http://code.google.com/apis/ajaxsearch/documentation/reference.html#_class_GlocalResult
    155    var h='<div style="background-color:#fef;margin-left:3em;margin-right:3em;padding-top:.5em;padding-bottom:.5em;font-size:.8em;clear:both;margin-bottom:40px;"><b onclick="sA(\''+UnicodeToHTML(r.address,2)+'\',\''+UnicodeToHTML(r.titleNoFormatting,2)+'\',\''+UnicodeToHTML(r.phone.join('<br/>')+'<br/>'+r.address,2)+'\');" title="'+r.titleNoFormatting+'" style="color:#94e;cursor:pointer;"><img style="margin-top:-.5em;float:left;margin-right:1em;" src="'+r.staticMapUrl+'"/>'+r.title
    156 	+'</b><br/>'+r.address+'<br/>'+r.phone.join('<br/>')+(r.phone.length?'<br/>':'')/*+r.listingType+'<br/>'*/+r.content+'<br/>('+r.lat+','+r.lng+') <a href="'+r.url+'" target="_blank">Use Google Maps</a></div>';
    157    if(sA2.c)sA2.c.innerHTML+=h;else sl(h);
    158   },'Local');
    159   sl('initSearch: local search initialed.');
    160  }});
    161  else sl('initSearch: Can not initial local search. Please load API.');
    162 }
    163
    164
    165 var _map_tmp_message;
    166 initMap.flag={backgroundColor:'#DDE'};
    167 function initMap(){
    168  var a,m,i,_f=arguments.callee;
    169  mapO=new gMap('map_canvas',dLoc.tw,_f.flag);
    170  //if(!mapO)return 1;
    171
    172  //mapO.geocoder.setBaseCountryCode('TW');
    173  sl('initMap: set geocoder country code: '+mapO.geocoder.getBaseCountryCode());
    174
    175  //	要先 show 才能得到 getOverviewMap()
    176  if(m=mapO.overviewMap)
    177   //	IE7 上 .hide() 時 .show() 會出錯
    178   //	2008/9/6 22:37:33	IE6 也會出錯了
    179   if(navigator.userAgent.indexOf('MSIE')==-1)
    180    m.show(),setOverviewMap();
    181
    182  //	small mark template	http://econym.googlepages.com/custom.htm	http://mapki.com/wiki/Available_Images	http://econym.googlepages.com/geicons.htm	http://code.google.com/apis/maps/documentation/overlays.html#Icons_overview
    183  //	iconSize 的處理還是有問題。
    184  mapO.icon(_f.iconOption||{
    185 	shadow:'http://labs.google.com/ridefinder/images/mm_20_shadow.png',
    186 	iconSize:new GSize(12,20),
    187 	shadowSize:new GSize(22,20),
    188 	iconAnchor:new GPoint(6,20),
    189 	infoWindowAnchor:new GPoint(5,1)
    190  },1);
    191  mapO.icon(_f.iconArray||[
    192 	{image:'http://labs.google.com/ridefinder/images/mm_20_green.png'},//http://www.google.com/intl/en_us/mapfiles/ms/micons/blue-dot.png//{shadow:'http://maps.google.com/mapfiles/kml/pal5/icon14s.png',image:'http://maps.google.com/mapfiles/kml/pal5/icon14.png'},//
    193 	{image:'http://labs.google.com/ridefinder/images/mm_20_yellow.png'},
    194 	{image:'http://labs.google.com/ridefinder/images/mm_20_green.png'},//{icon:G_DEFAULT_ICON},
    195 	{image:'http://labs.google.com/ridefinder/images/mm_20_orange.png'},//'http://maps.google.com/mapfiles/arrow.png',
    196 	''
    197  ]);
    198  mapO.runAfterAdd=function(o){getO.add(o.address,o.name);showML.sel(o);showML();};
    199  mapO.runOnClick=function(o){showML.sel(o);showML();};
    200  mapO.runAfterRemove=function(o){if(showML.sel()==o)showML.sel(null,1);showML();};
    201  mapO.notFound=function(a,d){sl('<em>沒找到 ['+a+'] '+d.name+'</em>');return false;};
    202  //	在 unload 的時候呼叫 GUnload 以避免瀏覽器 memory leak。
    203  addListener(0,'unload',function(){try{GUnload();}catch(e){}});
    204
    205  a=function(htm,t,js,ico){
    206   return '<div class="cm_line" title="'+t+'" onclick="'+js+'" onmouseover="this.className=\'cm_line_mo\';" onmouseout="this.className=\'cm_line\';"><img style="height:1em;width:1em;" src="'+(ico||'http://www.google.com/mapfiles/markerTransparent.png')+'"/> '+htm+'</div>';
    207  };
    208  if(m=mapO.map)
    209   //	use google's message
    210   i=function(i){try{
    211 	eval('_map_tmp_message=p('+i+');',m);
    212 	var a=_map_tmp_message;
    213 	//sl('setContextMenu '+i+': '+a);
    214 	return a&&a.length<8?a:'';
    215   }catch(e){}},
    216   mapO.setContextMenu([
    217 	a(i(10985)||'Zoom in','將地圖於此放大','mapO.showContextMenu(0),mapO.map.zoomIn(mapO.clickLatLng,true);','http://www.google.com/mapfiles/zoom-plus.png')
    218 	,a(i(10986)||'Zoom out','將地圖於此縮小','mapO.showContextMenu(0),mapO.map.zoomOut(mapO.clickLatLng,true);','http://www.google.com/mapfiles/zoom-minus.png')
    219 	,a(/*i(11047)||*/'Set Center','將地圖於此置中','mapO.showContextMenu(0),mapO.setCenter(mapO.clickLatLng);','http://www.google.com/mapfiles/center.png')
    220 	,'<hr/>'
    221 	,a('Search near','搜尋附近','mapO.showContextMenu(0),showNeighbor(mapO.clickLatLng);','http://maps.google.com/mapfiles/kml/pal2/icon13.png')
    222   ].join('')),
    223   GEvent.addListener(m,"click",function(){
    224    mapO.showContextMenu(0);
    225   }),GEvent.addListener(m,"dragstart",function(){
    226    mapO.showContextMenu(0);
    227   });
    228 }
    229
    230
    231 function readLoc(){
    232  if(loadMapData.dataF)catchFile([loadMapData.dataF,mapO.locFP=loadMapData.dataF+'.adr.csv']);
    233  mapO.readLoc();
    234 }
    235
    236
    237 //loadMapData.baseD='';
    238 //	預設可使用之模板
    239 loadMapData.getHTM=function(d,type){
    240  var l=mData[d.name||d.address],a=[mapO.getPoint(d,type)],r=[];
    241  if(d.address||'')a.unshift(d.address);
    242  l=l?l.link:'';
    243  if(!loadMapData.baseD)loadMapData.baseD='';
    244  if(d.name)		//	不能用 this.baseD
    245   r.push('<em>'+(l?'<a href="'+loadMapData.baseD+l+'" target="_blank">'+d.name+'</a>':d.name)+'</em>');
    246  if(d.dscr)r.push(d.dscr);
    247  r.push('<div class="adr">'+a.join('<br/>')+'</div>');
    248  if(typeof d.getLength=='function')r.push('length: '+(d.getLength()/1000).toFixed(3)+'km');
    249  if(typeof d.getArea=='function')r.push('面積: '+d.getArea().toFixed(3)+'m²');
    250  //if(showNeighbor.pointer&&d.getLatLng)r.push('距離 '+showNeighbor.pointer.distanceFrom(d.getLatLng()));
    251  //r.push(,'<hr class="sp"/><span onclick="">search near</span>');
    252  return r.join('<br/>').replace(/(<\/(div|p)>)\s*<br\/?>/g,'$1');
    253 };
    254 loadMapData.lessItems=3;
    255 loadMapData.forEach=function(mapData,index){};
    256 /*
    257 loadMapData.zIndexP=function(){
    258  sl('click '+this.name);
    259  return -1;
    260 };
    261 */
    262 function loadMapData(){
    263  /*	load: 這裡假設 data 中標題可作為id（獨一無二）
    264 	map data: [標題	敘述(description)	link	_data_]
    265 	_data_: search string/kind	additional description/data of different kind of data
    266 		search string/additional description (用括弧框起來的)
    267 		'marker'	lat	lng
    268 		'polyline'	points	levels
    269 		'polygon'	points	levels
    270
    271 	mData[標題||address]={};
    272  */
    273  mData={};
    274  var i=0,m,t,_f=arguments.callee;
    275  _f.run=1;
    276
    277  try{t=getU(_f.dataF);}catch(e){sl('Get data file ['+_f.dataF+'] error: '+e.message);}
    278  if(t)t=parseCSV(t);//t.replace(/\r+/g,'').replace(/\n+$/,'').replace(/^\n+/,'').split('\n');
    279  else t=[],showML.write('No list got of ['+_f.dataF+'].');
    280
    281  showML.write('總共 '+t.length+' 筆資料正處理中，請稍候…');
    282  sl('loadMapData: Found '+t.length+' lines. Parsing.. (<em onclick="if(loadMapData.run)loadMapData.run=0,sl(\'User stopped.\');" style="cursor:pointer;">stop</em>)');
    283  for(;i<t.length&&_f.run;i++){
    284   //sl(t[i][7]);
    285   //if(t[i] && (m=t[i].replace(/\\n/g,'<br/>').split('\t')).length>_f.lessItems)
    286   if(t[i].length>_f.lessItems)
    287    _f.forEach(t[i],i);
    288  }
    289  _f.run=0;
    290 }
    291
    292
    293 placeMapItem.done=function(i){
    294  //sl('placing done.');
    295  showML.refresh=1,showML();
    296  //	workaround 權宜之計: iconSize 的處理還是有問題。
    297  setTimeout('if(mapO)mapO.iconA[2].image=G_DEFAULT_ICON.image;',500);
    298 };
    299 placeMapItem.step=function(i){
    300  var _t=this;
    301  if(_t.run&&i<_t.stepA.length&&(!_t.loadMax||i<_t.loadMax))
    302   mapO.add(mData[_t.stepA[i++]]),setTimeout('placeMapItem.step('+i+');',0/*i>40?400:i+i*i/4*/);
    303  else if(_t.done)_t.done(i);
    304 };
    305 placeMapItem.loadMax=80;
    306 function placeMapItem(){
    307  var _f=arguments.callee,i;
    308  if(_f.noPlace){	//	一開始不列出點
    309   if(typeof _f.noPlace=='function')_f.noPlace();
    310   return;
    311  }
    312
    313  lostItem={};
    314  _f.run=1;
    315  //showML();
    316  showML.refresh=0;
    317  if(_f.step){
    318   _f.stepA=[];
    319   for(i in mData)
    320    lostItem[i]=mData[i],_f.stepA.push(i);//,mapO.add(mData[i]);
    321   setTimeout('placeMapItem.step(0);',0);
    322  }else{
    323   for(i in mData)mapO.add(mData[i]);
    324   if(_f.done)_f.done();
    325  }
    326 }
    327
    328
    329
    330 //	interface
    331
    332 //	2008/9/6 15:7:34
    333 //setSize.size=[880,320,240];	//	width, height, menuWidth
    334 setSize.size='95%,70%,25%'.split(',');	//	width, height, menuWidth
    335 setSize.setContainer=function(){
    336  try{
    337   this.menuC=(this.menu=document.getElementById('markerList')).parentNode;	//	menuC: menu container
    338   return this.container=document.getElementById('map_canvas').parentNode;
    339  }catch(e){}
    340 };
    341 function setSize(){
    342  var _f=arguments.callee,s=_f.size,_s=s.join('\0').split('\0'),m,a=function(i,k){
    343 	if(!isNaN(s[i]))s[i]+='px';
    344 	if(typeof _s[i]=='string'&&(m=_s[i].match(/^(\d+)%$/)))
    345 	 k=k?'Height':'Width',
    346 	 _s[i]=Math.floor((window.innerHeight?window['inner'+k]:document.documentElement&&document.documentElement.clientHeight?document.documentElement['client'+k]:document.body['offset'+k])*m[1]/100);
    347  };
    348
    349 /*
    350  //	from getWinStatus()
    351  with(document.body.style)width=height='100%';
    352  sl('setSize: window.inner: '+window.innerWidth+','+window.innerHeight);
    353  sl('setSize: window.page: '+window.pageXOffset+','+window.pageYOffset);
    354  sl('setSize: screen: '+screen.width+','+screen.height);
    355  sl('setSize: document.documentElement.client: '+document.documentElement.clientWidth+','+document.documentElement.clientHeight);
    356  sl('setSize: document.body.scroll: '+document.body.scrollWidth+','+document.body.scrollHeight);
    357  sl('setSize: document.body.offset: '+document.body.offsetWidth+','+document.body.offsetHeight);
    358  sl('setSize: document.body.client: '+document.body.clientWidth+','+document.body.clientHeight);
    359 */
    360
    361  a(0);
    362  a(1,1);
    363  a(2);
    364  //sl('setSize: '+_s);
    365
    366  a=_f.container;
    367  if(!a&&!(a=_f.setContainer()))return;
    368
    369  with(a.style)width=s[0],height=s[1];
    370  with(_f.menuC.style)width=s[2],height=s[1];
    371
    372  _f.menu.style.height=(_s[1]-_f.menu.offsetTop)+'px';
    373
    374  //sl('setSize: '+a.offsetWidth+'-'+_f.menuC.offsetWidth+'='+(a.offsetWidth-_f.menuC.offsetWidth));
    375  //sl('setSize: '+_s[0]+'-'+_s[2]+'='+(_s[0]-_s[2]));
    376  initMap.flag.size=[
    377 	a.offsetWidth-_f.menuC.offsetWidth-(navigator.userAgent.indexOf('MSIE')==-1?13:1),	//	13: 和 scrollbar 有關嗎??
    378 	(navigator.userAgent.indexOf('MSIE')==-1?_s[1]:_s[1])	//	_s[1] 不能改成 a.offsetHeight
    379  ];
    380 }
    381
    382
    383
    384 showML.refresh=1;
    385 showML.closeMark='×';
    386 showML.indexA='iA';
    387 showML.selClass='sel';
    388 showML.specialKind='sp';
    389 showML.isSelR=new RegExp('(^|\\s+)'+showML.selClass);
    390 //showML.selO=null;
    391 showML.sel=function(o,removed){
    392  var _t=this;
    393  if(typeof o!='undefined'){
    394   //	GMarker.setImage(src)
    395   if(!removed&&_t.selO&&_t.selO.setImage)
    396    try{_t.selO.setImage(mapO.icon().image);}catch(e){}	//	用 try 是因為有時被刪了可是卻還是在（動作太快？），這時要 setImage 會出錯。
    397   _t.selO=o;
    398   if(o&&o.setImage)o.setImage(mapO.icon(showSP.selectedI).image);
    399  }
    400  return _t.selO;
    401 };
    402 showML.getName=function(o){
    403  var name=o.parentNode.attributes.getNamedItem(this.indexA),type;
    404  if(name&&(name=name.nodeValue)&&(type=name.match(/^([a-z]+),(.+)$/i))){
    405   //sl('showML.getName: ['+type[1]+'] '+type[2]+'('+getO.alias(type[2])+','+mapO.getO(type[1],getO.alias(type[2]))+')');
    406   //name=,type=type[1];
    407   return [getO.alias(type[2]),type[1]]//[name,type];
    408  }
    409 };
    410 showML.write=function(t){
    411  try{document.getElementById('markerList').innerHTML=t;}catch(e){}
    412 };
    413 function showML(o){
    414  if(!mapO)return;	//	尚未 initial?
    415
    416  var _f=arguments.callee;
    417  if(o){
    418   var name,type;
    419   if(type=_f.getName(o)){
    420    name=type[0],type=type[1];
    421    if(o.className=='closeMark')
    422     mapO.remove(name,type);
    423    else{
    424     showML.sel(mapO.getO(type,name));//.setImage(mapO.icon(2).image);
    425     mapO.show(name,type);
    426     o=o.parentNode;
    427     for(var i=0,LI=o.parentNode.parentNode.getElementsByTagName('li');i<LI.length;i++)
    428      if(_f.isSelR.test(LI[i].className))LI[i].className=LI[i].className.replace(_f.isSelR,'');
    429     o.className+=' '+_f.selClass;
    430    }
    431   }
    432   return false;
    433  }
    434
    435  if(!_f.refresh)return;
    436  //alert(mapO.getOArray('type').join('\n'));
    437
    438  var t=['<ol>'],a,i,j,id,OK={};
    439  for(j in mapO.supportKind()){
    440   a=mapO.getO(j);
    441   for(i in a){
    442    //sl(a[i].name+','+a[i].address);
    443    if(a[i].name in lostItem)delete lostItem[a[i].name];
    444    else if(('address' in a[i]) && (a[i].address in lostItem))delete lostItem[a[i].address];
    445
    446    id=typeof a[i].getLatLng=='function'?a[i].getLatLng():a[i].name;
    447    if(!(id in OK))
    448     OK[id]=1,
    449 	t.push('<li '+_f.indexA+'="'+j+','+i+'" class="'+(j=='marker'?'':_f.specialKind)+' '+(_f.sel()==a[i]?_f.selClass:'')+'"'
    450      +'><a href="#" title="'+UnicodeToHTML((
    451 		((a[i].name+'').indexOf(id)!=-1||(a[i].dscr+'').indexOf(id)!=-1?'':id+'\n')
    452 		+(a[i].dscr||i)
    453 		+(showNeighbor.pointer&&typeof a[i].getLatLng=='function'?'\n距離定點 '+(a[i].getLatLng().distanceFrom(showNeighbor.pointer.getLatLng())/1000||0).toFixed(3)+' km':'')
    454 	  ).replace(/<br\/?>/gi,'\n').replace(/\n{2,}/g,'\n'),2)+'" onclick="return showML(this);" onmouseover="showSP(this,1);" onmouseout="showSP(this);">'
    455      +(a[i].name||i).replace(/<[^>]+>/g,'')
    456      +'</a> [<a href="#" class="closeMark" onclick="return showML(this);" title="關閉">'+_f.closeMark+'</a>]</li>');// title="remove marker"
    457   }
    458  }
    459  for(i in lostItem)t.push('<li class="lost"><a href="#" onclick="mData['+dQuote(i,0,"'")+'].show=1;mapO.add(mData['+dQuote(i,0,"'")+']);return false;" title="'+UnicodeToHTML(mData[i].name||mData[i].type||'')+'">'+i+'</a></li>');
    460  t.push('</ol>');
    461  //sl(UnicodeToHTML(t.join('')),1);
    462  _f.write(t.join(''));
    463  if(typeof dealLink=='function')setTimeout('dealLink(0,1);',0);
    464  return false;
    465 }
    466
    467
    468 //	show spot
    469 showSP.defaultI=0;	//	default icon index
    470 showSP.selectedI=2;	//	selected icon index
    471 function showSP(o,i){
    472  var _f=arguments.callee;
    473  if(o){
    474   var name,type;
    475   if(type=showML.getName(o)){
    476    name=type[0],type=type[1];
    477    o=mapO.getO(type,name);
    478    if(o&&o.setImage&&o!=showML.sel())
    479     //sl('showSP: set icon of ['+o.name+'] to '+i),
    480     o.setImage(mapO.icon(i).image);
    481   }
    482   return false;
    483  }
    484
    485 }
    486
    487 //	應用
    488
    489 function removeAll(){
    490  showML.refresh=0,lostItem={};
    491  if(mapO)mapO.removeAll();	//	尚未 initial?
    492  if(showML)showML.sel(null,1),showML.refresh=1,showML();
    493 }
    494
    495
    496 //	不能造成 type 改變!
    497 function normalize_address(t) {
    498 	var _f = arguments.callee, a = '０１２３４５６７８９';
    499 	if (!_f.dc) {
    500 		_f.nr = new RegExp('[' + a + ']', 'g');
    501 		_f.dc = {};
    502 		for ( var i = 0; i < a.length; i++)
    503 			_f.dc[a.charAt(i)] = i;
    504 	}
    505 	return t.replace(/[\s　]+/g, '').replace(_f.nr, function($0) {
    506 		return _f.dc[$0];
    507 	})
    508 	//.replace(/號([^樓]+樓|之.{1,2})$/,'號');
    509 	;
    510 };
    511
    512 /*
    513 http://www.post.gov.tw/post/internet/f_searchzone/sz_a_b_ta.jsp#a
    514 縣市
    515 鄉鎮市區	【市】為縣轄市
    516 村里	直轄市：台北市、高雄市及省轄市：基隆市、新竹市、台中市、嘉義市、台南市以外地區由於路名結構變化較大，有些地區沒有路、街，僅有「村」、「里」名稱
    517 鄰
    518 路街	路名中不可使用（全、半形）阿拉伯數字，必須一律使用中文數字，如中正二路等，惟門牌號不必輸入。	新村、山莊、新城、工業區等與街、路同級之名稱
    519 段
    520 巷	文字巷
    521 弄
    522 號
    523 \d(之\d)樓(之\d)
    524
    525 縣市鄉鎮區村里鄰路街段巷弄號樓
    526
    527 TODO:
    528 806台灣高雄市前鎮區光華二路413號
    529
    530 */
    531 CeL.net.map
    532 .
    533 /**
    534  * 解析地址
    535  * @param {String} address	地址
    536  * @returns
    537  * @memberOf	CeL.net.map
    538  */
    539 parse_address = function (address) {
    540 	var a = {}, i, v, w = '縣市鄉鎮區村里鄰路街段巷弄號樓',
    541 	r = new RegExp('[^' + w + ']+([' + w + '])', 'g');
    542
    543 	var _ = address.replace(/[　\s]+/g, '').replace(/號([^樓F]{1,6})F$/, '號$1樓')
    544 			.replace(/^(\d{3,5})?[台臺]灣/, '$1').replace(/^\d+/, function($0) {
    545 				r.zip = $0;
    546 				return '';
    547 			}).replace(r, function($0, $1) {
    548 				v = $0;
    549 				if (/縣市/.test(i = $1) && !r.zip)
    550 					v.replace(/^\d{3}\d{2}?}/, function($0) {
    551 						r.zip = $0;
    552 						return '';
    553 					});
    554 				a[i] = v;
    555 				//sl(v);
    556 				return '';
    557 			});
    558 	// sl('['+address+'] --- ['+_+']');
    559 	return _ ? 0 : address;
    560 };
    561
    562
    563 //	地址轉 index
    564 getO.n={};	//	name(address) to mapO.getO's name
    565 getO.add=function(alias,name,type){
    566  if(alias&&name&&alias!=name){
    567   if(type&&mapO.getO(type,alias)&&!mapO.getO(type,name)){
    568    var t=name;name=alias,alias=t;
    569   }
    570   //sl('getO.add: ['+alias+'] set to ['+name+']'),
    571   this.n[alias]=name;
    572  }
    573 };
    574 getO.alias=function(name){return this.n[name]||name;};
    575 function getO(name){
    576  return mapO.getO(arguments.callee.n[name]||name);
    577 }
    578
    579
    580
    581
    582 /*	search address
    583 sA(index of mData)
    584 sA({type(where):'',name:'',description:''})
    585 sA(type(where),name,description)
    586 */
    587 function sA(i,a,b){
    588  if(!mData)return;	//	尚未 initial?
    589  if(!(i in mData)||arguments.length>1)
    590   //sl('sA: search '+i),
    591   mapO.searchPoint.apply(mapO,arguments);
    592  else
    593   //sl('sA: call mData['+i+']('+mData[i].type+')'),
    594   mapO.searchPoint(mData[i]);
    595  return false;
    596 }
    597
    598 //	先找尋現有資料
    599 function sA2(adr,noFit){
    600  var _f=arguments.callee;
    601
    602  if(!noFit){
    603   showFit(adr
    604 	,function(k,o){return k&&(o.name+o.description).indexOf(k)!=-1;}
    605 	,function(k){_f(adr,1);}
    606   );
    607   return;
    608  }
    609
    610  if(typeof adr_to_mData!='object'||mapO.LatLngR.test(adr))return sA(adr);
    611
    612  //return sA(adr_to_mData[adr]||adr);
    613
    614
    615  if(!_f.c)_f.c=document.getElementById('guess');
    616
    617  if(!(adr in adr_to_mData)&&(adr.toLowerCase() in adr_to_mData))
    618   //sl('sA2: ['+adr+']→['+adr.toLowerCase()+']'),
    619   adr=adr.toLowerCase();
    620  if(adr_to_mData[adr] in mData){
    621   var a=adr_to_mData[adr];
    622   mData[a].show=1;	//	置中
    623   sA(a);
    624   if(showML.selO)mapO.showWindow(showML.selO);
    625  }else if(!_f.c)sA(adr);
    626  else mapO.getLocations(adr,function(r){
    627 	 var cM='<div style="float:right;cursor:pointer;" title="close" onclick="sA2.c.style.display=\'none\';">[×]</div>';
    628 	 if(gLocal){gLocal.searcher.setCenterPoint(showNeighbor.pointer.getLatLng()||mapO.setCenter()||'台灣');gLocal.s(adr);}
    629 	 if(!r.length){_f.c.innerHTML=cM+'使用 GClientGeocoder.getLocations 沒找到 [<span style="color:#e23;">'+adr+'</span>]：<br/>'+mapO.GeoStatus(mapO.getLocations.errno),_f.c.style.display='block';return;}
    630 	 if(r.length==1&&adr==r[0][2]){sA(adr);if(showML.selO)mapO.showWindow(showML.selO);return;}
    631 	 var i=0,t=[cM+'對於 [<span style="color:#e23;">'+adr+'</span>]，您是不是指：<ol>'];
    632 	 for(;i<r.length;i++)
    633 	  if(r[i][0])
    634 	   //sl('sA2: '+r[i]),
    635 	   t.push('<li><span title="'+r[i][0]+','+r[i][1]+'">〒</span> <span class="point" onclick="sA(this.title);if(showML.selO)mapO.showWindow(showML.selO);return false;" title="'+r[i][2]+'">'+r[i][2]+'</span></li>');
    636 	 t.push('</ol>');
    637 	 _f.c.innerHTML=t.join('');
    638 	 _f.c.style.display='block';
    639 	});//sA(adr);
    640  return false;
    641 }
    642
    643
    644
    645 /*
    646 從 mData show 符合條件的
    647 */
    648 showFit.showZoom=40;	//	這以下就 zoom
    649 showFit.limit=80;	//	最多取點數
    650 function showFit(k,func,notFound){
    651  if(typeof k=='undefined'||!func)return;
    652  removeAll();
    653  showML.write('頁面資料讀取中，請稍候…');
    654
    655  var i,p=[],b,_f=arguments.callee;
    656  for(i in mData)
    657   if(func(k,b=mData[i],i) && (b=mapO.searchPoint.call(mapO,b)))	//	確定有找到才 c++
    658    if(p.push(b), _f.limit&&p.length>_f.limit)break;
    659
    660  if(p.length<_f.showZoom)
    661   if(p.length){
    662    //	zoom
    663    b=new GLatLngBounds();
    664    for(i=0;i<p.length;i++)
    665     b.extend(p[i]);
    666    mapO.setCenter({p:b.getCenter(),m:'pan',z:mapO.map.getBoundsZoomLevel(b)});
    667   }else if(notFound)notFound(k);
    668
    669  return p;
    670 }
    671
    672
    673
    674 /*	只顯示附近的spot
    675 showNeighbor('高雄市苓雅區');
    676
    677 bug:
    678
    679 TODO:
    680 setZoom: getBoundsZoomLevel
    681 addSelf:	加入此點，使之受到管控。預設 false
    682 不在管控內
    683 */
    684 showNeighbor.pointer=null;	//	default GMarker
    685 showNeighbor.addPointer=function(a){
    686  var _m=mapO.map,m=mapO.addMarker(
    687 	 new GLatLng(22.62,120.33)//dLoc.tw
    688 	 ,{draggable:true,title:'請拖曳我，以找出鄰近的點。',icon:mapO.icon(3)}
    689 	);
    690  GEvent.addListener(m,"dragend",function(){
    691   showNeighbor(m.getLatLng());
    692  });
    693
    694  if(this.l)GEvent.removeListener(this.l);
    695  this.l=GEvent.addListener(_m,"click",function(overlay,point){
    696   var i,p=[5,10,15,20,30,50],s='<br/><a href="#" onclick="return showNeighbor.byD();" title="search near">搜尋附近</a>';
    697   for(i=0;i<p.length;i++)s+=' <a href="#" onclick="return showNeighbor.byD('+p[i]+');">'+p[i]+'</a>';
    698   s+=' km';
    699   if(overlay==m)m.openInfoWindowHtml('經緯度: '+m.getLatLng()+s);
    700   else if(!overlay&&point)
    701    p=m.getLatLng(),m.setLatLng(point),
    702    m.openInfoWindowHtml(point+'<br/>from: '+p+',<br/>distance: '+(p.distanceFrom(point)/1000).toFixed(3)+'km'+s);
    703  });
    704
    705  return this.pointer=m;
    706 };
    707 showNeighbor.arg={	//	傳給 .getNeighbor() 的參數
    708 	//f:'n[i]=n[i][1][2];',	//	不能改變結構！因為需要 getBoundsZoomLevel
    709 	d:30,
    710 	c:9
    711 };
    712 showNeighbor.forEach=function(a){
    713  //sl('showNeighbor.forEach: ['+(typeof a[1][2])+'] '+a[1][2]);
    714  sA(a[1][2]);
    715 };
    716 showNeighbor.byD=function(d){
    717  var _t=this;
    718  if(d)_t.arg.d=d;
    719  if(_t.pointer)_t(_t.pointer.getLatLng());
    720  return false;
    721 };
    722 showNeighbor.notFound=function(address,address_not_found){
    723  showML.write('抱歉，找不到 <em>'+address+'</em>'+(address_not_found?'':' 附近的點')+'。');
    724 };
    725 function showNeighbor(l,f){//(location | [location, address], addSelf)
    726  if(!l)return;
    727  showML.write('頁面資料讀取中，請稍候…');
    728  var _f=arguments.callee,i,o,adr;
    729  if(l instanceof Array)adr=l[1],l=l[0];
    730  //sl('showNeighbor: ['+l+','+adr+'] '+(l instanceof GLatLng)+', '+mapO.getLatLng(l+''));
    731  if(!(l instanceof GLatLng))
    732   // 無此資料。嘗試取得 loc..
    733   return mapO.getLatLng(l+'',[function(p){_f([p,adr||l]);},function(p){if(p)return _f([p,adr||l]);_f.notFound&&_f.notFound(adr||l,1);}]);
    734  o=mapO.getNeighbor(l,_f.arg);
    735  if(o.length){
    736   removeAll();
    737   var bounds=new GLatLngBounds();
    738   bounds.extend(l);
    739   for(i=0;i<o.length;i++){
    740    if(!isNaN(o[i][1][1]))
    741     //sl('Add to bound: '+o[i][1][0]+','+o[i][1][1]),
    742     bounds.extend(new GLatLng(o[i][1][0],o[i][1][1]));
    743    //sl('add ['+o[i][1][2]+'] by '+(_f.forEach+'').slice(0,20)+'..');
    744    _f.forEach(o[i]);
    745   }
    746   if(!mapO.searchPoint.show)	//	若設了 searchPoint.show，還是會被其他的奪去…
    747    mapO.setCenter({p:bounds.getCenter(),m:'pan',z:mapO.map.getBoundsZoomLevel(bounds)});
    748  }else _f.notFound(l);
    749  if(_f.pointer)_f.pointer.setLatLng(l),_f.pointer.openInfoWindowHtml('搜尋 '+(adr?'<em title="'+l+'">'+adr+'</em> ':l)+' 四周 '+_f.arg.d+' km<br/>找到 '+o.length+'/(最多 '+_f.arg.c+') 個點。');
    750  //sl('showNeighbor: search around '+l+' (四周 '+_f.arg.d+' km) get '+o.length+'/(max '+_f.arg.c+') results.');
    751 }
    752
    753
    754
    755
    756
    757
    758
    759
    760
    761
    762
    763
    764
    765
    766 //	===================================================
    767 /*
    768 	** use Yahoo! Map to get position of a address
    769
    770 _=this
    771
    772 TODO:
    773
    774
    775 HISTORY:
    776 2008/7/31 19:56:29	create
    777
    778
    779 http://tw.developer.yahoo.com/maps/
    780 http://developer.yahoo.com/maps/ajax/V3.8/index.html
    781 */
    782 var
    783 getLatLon=
    784
    785 (function(){
    786
    787 var
    788
    789 //	class private	-----------------------------------
    790
    791 //	class name
    792 n='getLatLon',
    793
    794 //	running now
    795 r=0,
    796
    797 //	interval/timeout seed
    798 s=''+Math.random()*1e12,
    799
    800
    801 //	{ address: [function(lat, lng, address), not found function(address)], .. }
    802 o={},
    803
    804 //	queue: [ adr, .. ]
    805 q=[],
    806
    807
    808 //	map object
    809 m,
    810
    811 //	initial
    812 i=function(){
    813  if(typeof YMap!='function'){
    814   //sl(n+': Please include YMap first!');
    815   return 1;
    816  }
    817
    818  var o=document.createElement('div');
    819  document.body.appendChild(o);
    820  o.style.width=o.style.height='1px';	//	=0 會造成 .getArea() 出問題
    821  YEvent.Capture(m=new YMap(o),EventsList.onEndGeoCode,c);
    822  o.style.visibility='hidden';
    823  //o.style.display='none';	//	會造成 .getArea() 出問題
    824
    825  //s=Math.random()*1e12+'';
    826 },
    827
    828
    829 //	do query
    830 d=function(){
    831  //sl(n+'.do query: '+q[0]);
    832  if(!q||!q.length)r=0;
    833  else m.geoCodeAddress(q.shift());
    834 },
    835
    836
    837 //	catch function
    838 c=function(r){
    839  var a=r.Address,f;
    840
    841  if(f=_.interval)
    842   setTimeout(n+'("'+s+'");',f);
    843  else d();
    844
    845  if(a in o)
    846   if(f=o[a],r.success)f[0](r.GeoPoint.Lat,r.GeoPoint.Lon,a);
    847   else f[1]&&f[1](a);
    848  delete o[a];
    849 },
    850
    851
    852 //	instance constructor	---------------------------
    853 _=function(a,f,nf){	//	address, function, not found function
    854  if(a===s)return d();
    855
    856  if(!a||!f || !m&&i())return 1;
    857
    858  o[a+='']=[f,nf];
    859
    860  //sl(n+': ('+q.length+')'+[a,f,nf]);
    861  q.push(a);
    862
    863  if(!r)r=1,d();
    864 };
    865
    866
    867 //	class public interface	---------------------------
    868
    869 //	interval (ms)
    870 _.interval=0;
    871
    872
    873 //	class constructor	---------------------------
    874
    875 i();
    876
    877
    878 return _;
    879 })();	//	(function(){
    880
    881 //	===================================================
    882
    883
    884 /*	old
    885 //	use Yahoo! Map
    886
    887 //	interval
    888 //getLatLon.t=200;
    889
    890 //	{ address: [function, not found function], .. }
    891 getLatLon.o={};
    892
    893 //	queue: [ adr, .. ]
    894 getLatLon.q=[];
    895
    896 //	initial
    897 getLatLon.i=function(){
    898  if(typeof YMap!='function'){
    899   //sl('getLatLon: Please include YMap first!');
    900   return 1;
    901  }
    902  var c=document.createElement('div');
    903  document.body.appendChild(c);
    904  c.style.width=c.style.height=0;
    905  YEvent.Capture(this.m=new YMap(c),EventsList.onEndGeoCode,this.c);
    906  c.style.display='none';
    907  this.T=Math.random()*1e12+'';
    908 };
    909
    910 //	catch function
    911 getLatLon.c=function(r){
    912  var t=getLatLon;
    913  if(t.t)setTimeout('getLatLon.d("'+t.T+'");',t.t);else getLatLon.d();
    914  var f=t.o;
    915  if(r.Address in f)
    916   if(f=f[r.Address],r.success)f[0](r.GeoPoint.Lat,r.GeoPoint.Lon,r.Address);
    917   else f[1]&&f[1](r.Address);
    918  delete t.o[r.Address];
    919 }
    920
    921 //	do query
    922 getLatLon.d=function(){
    923  var _f=arguments.callee,_t=this,a,f,n;
    924  //sl('getLatLon.d: '+_t.q[0]);
    925  if(!_t.q||!_t.q.length)_t.r=0;
    926  else _t.m.geoCodeAddress(_t.q.shift());
    927 };
    928
    929
    930 //	running
    931 //getLatLon.r=0;
    932
    933 function getLatLon(adr,f,nf){
    934  var _f=arguments.callee;
    935  if(adr===_f.T)return _f.d();
    936  if(!adr||!f || !_f.m&&_f.i())return;
    937
    938  if(!_f.q)_f.q=[];
    939  //sl('getLatLon: ('+_f.q.length+')'+[adr,f,nf]);
    940  _f.o[adr]=[f,nf];
    941  _f.q.push(adr);
    942
    943  if(!_f.r){_f.r=1;_f.d();}
    944 }
    945 */
    946
    947
    948
    949
    950
    951
    952
    953
    954 //	===================================================
    955
    956 /*
    957 	main map function
    958
    959 _=this
    960
    961 TODO:
    962
    963 */
    964
    965 //var
    966 gMap=
    967
    968 (function(){
    969
    970 //	class private	-----------------------------------
    971 var
    972
    973 //	class interface	-----------------------------------
    974 _=function(){
    975  //	Dynamic Loading	http://code.google.com/apis/ajax/documentation/#Dynamic
    976  //if(typeof GMap=='undefined')google.load("maps","2",{language:"ja_JP",callback:mapsLoaded});
    977
    978  //	init member
    979  var _t=this,i;
    980  //	initial instance object
    981  _t.locArray=[],_t.locArray2=[],_t.locArray_u={},_t.locArray2_u={},_t.iconA=[],_t.iconO={},_t.dMarkerO={};
    982  _t.kinds={marker:GMarker,polyline:GPolyline,polygon:GPolygon,xml:GGeoXml};	//	If this failed, maybe GMap didn't loaded?
    983  for(i in _t.kinds)
    984   if(i in _t)throw 'Error: ['+i+'] is already a member of me!';
    985   else _t[i]={};
    986
    987
    988  //	調整 GLatLng 的顯示
    989  GLatLng.prototype.toS=function(p){
    990   if(!p)p=_t.precision||0;
    991   return Number(this.lat()).toFixed(p)+','+Number(this.lng()).toFixed(p);
    992  };
    993  GLatLng.prototype.toString=function(p){return '('+this.toS(p)+')';};
    994
    995
    996 /*
    997 	http://blog.wctang.info/2007/07/use-google-map-api-without-api-key.html
    998 	Geocode 查詢每天有 50000 次的限制	使用 Geocoder 就是要連到 Google 去做查詢，而現在 Google 在做 Geocode 查詢時會在 Server 端做 API key 的檢查，這個就躲不掉了
    999 	http://blog.wctang.info/2007/07/use-google-map-geocoder-without-api-key.html
    1000
    1001
    1002 */
    1003  if(GBrowserIsCompatible()&&!_t.geocoder)
    1004   with(_t.geocoder=new GClientGeocoder())
    1005 	setCache(null),	//	disable cache, 因為找到的都被管控了。
    1006 	setBaseCountryCode('tw');	//	語系
    1007
    1008  //_t.readLoc();
    1009  _t.initMap.apply(_t,arguments);//return _t.initMap.apply(_t,arguments);
    1010 };
    1011
    1012
    1013 //	class public	-----------------------------------
    1014
    1015
    1016 //	prototype	-----------------------------------
    1017 _.prototype={
    1018
    1019 //	這些函數可重寫
    1020 notFound:function(address,data){
    1021  return 1;//throw 'Address ['+address+'] not found!';
    1022 },
    1023 //	增加 overlay 後
    1024 runAfterAdd:function(obj,type,data,name){},
    1025 //	移除 overlay 後
    1026 runAfterRemove:function(obj,type){},
    1027 //	按 overlay 時
    1028 runOnClick:function(obj,type){},
    1029
    1030 precision:6,	//	精度，算到小數點下第幾位。GMap 2008:6
    1031
    1032 defaultZoom:14,	//	預設縮放
    1033
    1034 /*
    1035 map,	//	GMap obj
    1036
    1037 TODO:
    1038 use GMarkerManager,	http://code.google.com/apis/maps/documentation/overlays.html#Marker_Manager
    1039
    1040
    1041 //handle array:
    1042 marker={'lat,lng':GMarker},	//	GMarker 地圖標記
    1043 polyline={points:GPolyline},	//	GPolyline 折線
    1044 polygon={points:GPolygon},	//	GPolygon 多邊形
    1045 xml={URL:GGeoXml},		//	GGeoXml: xml/kml
    1046 */
    1047 kinds:{},
    1048 supportKind:function(k){
    1049  return k?k in this.kinds:this.kinds;
    1050 },
    1051
    1052 getKind:function(o){
    1053  for(var i in this.kinds)
    1054   if(o instanceof this.kinds[i])return i;
    1055 },
    1056
    1057
    1058 //	讀入先前 catch 的經緯度，存loc而不必每次search
    1059 readLoc:function(){
    1060  var _t=this,t,i=0,l,a;
    1061  if(!_t.locFP)_t.locFP='map_loc.dat';	//	紀錄 LatLng/地址 可供 searchPoint() 使用
    1062  //	GDownloadUrl(url,callback)
    1063  try{t=getU(_t.locFP);}catch(e){}
    1064  _t.adr_to_loc={};
    1065  if(!t||!(t=t.replace(/\r/g,'').split('\n')).length)return;
    1066
    1067  sl('Get '+t.length+' catched address records from ['+_t.locFP+'].');
    1068  for(;i<t.length;i++)
    1069   if((a=t[i].split('	')).length>1 && (l=a[0].split(',')).length==2)
    1070    _t.adr_to_loc[a[1]]=new GLatLng(l[0],l[1])
    1071    //,sl('readLoc: ['+a[1]+'] '+_t.adr_to_loc[a[1]])
    1072    ;
    1073   else sl('readLoc: error data: '+t[i]);
    1074 },
    1075
    1076 //	** important ** 這邊不能作 object 之 initialization，否則因為 object 只會 copy reference，因此 new 時東西會一樣。initialization 得在 _() 中作！
    1077 //	locArray[]=[lat,lng,adr] sort by lat	給 writeLoc() & getNeighbor() 用，僅包含需要 search 的。
    1078 locArray:[],
    1079 //	寫入 catched 的經緯度
    1080 writeLoc:function(s){
    1081  var _t=this,i,t=[],l,a=_t.precision,b,c;
    1082  //sl('writeLoc: We will write data to ['+_t.locFP+'].');
    1083  if(!_t.locFP)return;
    1084
    1085  for(i in _t.adr_to_loc)
    1086   if(l=_t.adr_to_loc[i])try{
    1087    if(isNaN(b=Number(l.lat()))||isNaN(c=Number(l.lng())))throw new Error(1,'經緯度非數字');
    1088    t.push([b.toFixed(a),c.toFixed(a),i]);
    1089   }catch(e){sl('writeLoc: Error: '+e.message+': ['+l+'] '+i+', ('+l.lat()+','+l.lng()+')');}
    1090
    1091  sl('writeLoc: '+_t.locArray.length+'→'+t.length);
    1092  //	不相同時才作處理
    1093  if(t.length!=_t.locArray.length){
    1094   t.sort(function(l,r){return l[0]-r[0]||l[1]-r[1];});
    1095   for(a=_t.locArray=[],b=_t.locArray_u={},c=[],i=0;i<t.length;i++)
    1096    if(l=t[i]){
    1097     if((l[0]+','+l[1]) in b)sl('writeLoc: 重複住址@ '+l[0]+','+l[1]+': '+b[l[0]+','+l[1]][2]+' , '+l[2]);
    1098     a.push(b[l[0]+','+l[1]]=l);
    1099     c.push(l[0]+','+l[1]+'	'+l[2]);
    1100     //sl('writeLoc: '+i+'/'+t.length+' '+c[c.length-1]);	//	多!!
    1101    }
    1102   //sl('writeLoc: '+(typeof simpleWrite)+','+_t.locFP);
    1103   if(typeof simpleWrite=='function'){
    1104    c=c.join('\n');
    1105    if(typeof simpleRead=='function' && simpleRead(_t.locFP,'utf-8')==c)
    1106     sl('writeLoc: 欲寫入之內容('+c.length+' chars)與標的檔相同。檔案並未變更。');
    1107    else
    1108     //sl('writeLoc: Write '+c.length+' chars to ['+_t.locFP+'].'),
    1109     simpleWrite(_t.locFP,c,'utf-8');
    1110   }//else sl('writeLoc: <em>Warning: function.js is not included?</em>');
    1111  }
    1112  if(s)sl('<textarea>'+t.join('\n')+'</textarea>');
    1113  return t;
    1114 },
    1115 //	locArray2[]=[lat,lng,adr] sort by lat	給 getNeighbor() 用，包括所有不需要 search 的地址。
    1116 locArray2:[],
    1117 locArray_u:{},locArray2_u:{},	//	預防重複: locArray_u['lat,lng']=obj of locArray or locArray2
    1118 /*	取得鄰近的地點: 經緯度, 最大距離(km)	http://blog.ben.idv.tw/2007/06/blog-post.html	http://hk.geocities.com/hk_weather/big5/others/calculators.html	http://blog.xuite.net/joy715/blog/9285691	http://iask.sina.com.cn/b/6263160.html
    1119 mapO.getNeighbor([22.620096,120.333381],"sl(n[i][1][2]);");
    1120 return:
    1121 	f.d:	距離(km)
    1122 	f.c:	最多取用點數，<=0：全取，未設：default
    1123 	f.s:	最後時選取與否的篩選設置之函數	傳回數值越大越後面
    1124 	f.D:	計算距離之函數，將用來比較
    1125 		default			[ [較準確的距離, [lat,lng,adr]],.. ]
    1126 		求得較大概的距離(以距離平方比計算,比較快)
    1127 					[ [距離, [lat,lng,adr]],.. ]	f.D=function(p,l){var a=l.lat()-p[0],b=l.lng()-p[1];return a*a+b*b;}
    1128
    1129 	f.f:	對選出之 spot 作最後處置之函數
    1130 		default			[ [較準確的距離, [lat,lng,adr]],.. ]
    1131 		傳回地址		[ adr1, adr2,.. ]		f.f=	'n[i]=n[i][1][2];'
    1132 		傳回 obj		[ [lat,lng,adr],.. ]		f.f=	'n[i]=n[i][1];'
    1133 		求得較準確的距離	[ [距離, [lat,lng,adr]],.. ]	f.f=	'n[i][1]=l.distanceFrom(n[i][1]);'
    1134 */
    1135 getNeighbor:function(l,f){
    1136  var _t=this,lat,lng,i,n=[],p=function(A){
    1137 	//	計算最接近上限mLat之loc
    1138 	//	c: 誤差
    1139 	var i=0,j=A.length,a,b,c=f._d,mLat=lat-c;
    1140 	if(!j)return;
    1141 	//sl(mLat+'~'+(lat+c));
    1142 	do{
    1143 	 //sl(Math.floor((i+j)/2)+'/'+A.length+','+A[a=Math.floor((i+j)/2)]);
    1144 	 b=A[a=Math.floor((i+j)/2)][0];
    1145 	 if(b>mLat)j=a;else if(b<mLat)i=a;
    1146 	}while(i<j-1&&Math.abs(b-mLat)>c);
    1147 	//sl('start: from ['+a+'/'+A.length+'] '+A[a].join(':')+' to '+(lat+c));var tt=[];
    1148 	for(i=a,mLat=lat+c,a=lng-c,b=lng+c;i<A.length&&A[i][0]<mLat;i++)
    1149 	 if(/*tt.push('- '+A[i]+': '+a+','+c+','+b),*/c=A[i][1],a<c&&c<b)
    1150 	  //n.push([j=f.D(A[i],l),A[i]]),sl('distance: '+j.toFixed(2)+' to '+A[i]);
    1151 	  n.push([f.D(A[i],l),A[i]]);
    1152 	//sl(tt.join('<br/>'));
    1153  };
    1154  if(typeof l=='string')l=l.split(',');
    1155  //if(typeof l=='function'&&l.lat&&l.lng)lat=typeof l.lat=='function'?l.lat():l.lat,lng=typeof l.lng=='function'?l.lng():l.lng;
    1156  if(l instanceof GLatLng)lat=l.lat(),lng=l.lng();
    1157  else lat=l[0],lng=l[1],l=new GLatLng(lat,lng);
    1158  //	這邊起 l 為原始點之 GLatLng
    1159  if(typeof f!='object')
    1160   f=isNaN(f)?{f:f}:{d:f};
    1161  f._d=(f.d>0?f.d:20)/111;	//	1度的實際長度~111公里。
    1162  if(!f.D)f.D=function(p,l){
    1163 	//var a=lat-p[0],b=lng-p[1];return a*a+b*b;	//	大概的，比較快。
    1164 	return l.distanceFrom(new GLatLng(p[0],p[1]));	//	real distance
    1165  };
    1166
    1167  //sl(lng+', '+lat+'; '+f._d);
    1168  _t.writeLoc();
    1169  p(_t.locArray);
    1170  _t.locArray2.sort(function(l,r){return l[0]-r[0]||l[1]-r[1];});
    1171  //sl('['+_t.locArray2.length+']<br/>* '+_t.locArray2.join('<br/>* '));
    1172  p(_t.locArray2);
    1173  //sl('Get '+n.length+' records near ('+lat+','+lng+').');
    1174
    1175  //	由近至遠 sort
    1176  if(typeof f.s=='undefined')f.s=function(l,r){return l[0]-r[0];};	//	l, r: [distance by f.D,[lat,lng,adr]]
    1177  if(f.s)n.sort(f.s);
    1178  //for(i=0;i<n.length;i++)sl('getNeighbor '+i+': '+n[i][0]+' '+n[i][1][2]);
    1179  if(typeof f.c=='undefined'||f.c&&!isNaN(f.c)&&n.length>f.c)
    1180   n=n.slice(0,f.c>0?f.c:9);	//	預設取 9 個
    1181
    1182  if(typeof f.f=='string')f.f=new Function('n','i','l',f.f);
    1183  //sl('Run: [~'+n.length+'] by '+f.f);
    1184  if(typeof f.f=='function')
    1185   for(i=0;i<n.length;i++)f.f(n,i,l);
    1186
    1187  return n;
    1188 },
    1189 /*
    1190 http://econym.googlepages.com/geomulti.htm
    1191 http://econym.googlepages.com/didyoumean.htm
    1192 enum GGeoStatusCode	http://code.google.com/intl/zh-CN/apis/maps/documentation/reference.html#GGeoStatusCode
    1193 */
    1194 GeoStatus:function(c){
    1195  var m=this.GeoStatusM;
    1196  if(!m){
    1197   this.GeoStatusM=m={
    1198 	G_GEO_SERVER_ERROR:'伺服器錯誤。',
    1199 	G_GEO_MISSING_QUERY:'輸入空地址。',
    1200 	G_GEO_UNKNOWN_ADDRESS:'找不到指定地址的對應地理位置。可能地址比較新，無法解析地址，地址不正確，或者缺少該地址的數據。',
    1201 	G_GEO_UNAVAILABLE_ADDRESS:'由於合法性或合同原因，無法返回給定地址的地理位置信息。',
    1202 	G_GEO_BAD_KEY:'給定的密鑰無效或與給定的 host ('+window.location.host+') 不匹配。',
    1203 	G_GEO_TOO_MANY_QUERIES:'給定的密鑰超出了 24 小時的請求限制。',
    1204 	//404:'沒找到網頁',
    1205 	403:'Probably an incorrect error caused by a bug in the handling of invalid JSON',
    1206 	G_GEO_SUCCESS:'查詢成功'
    1207   };
    1208   var i,v;
    1209   for(i in m)try{
    1210    if(!isNaN(v=eval(i))&&v!=i)m[v]=m[i];
    1211   }catch(e){}
    1212  }
    1213  return m[c]||'';
    1214 },
    1215 /*	mapO.getLocations('taiwan',function(r){});
    1216 	arguments:
    1217 		address, function([[lat,lng,adr], ..]), data object
    1218 		address, [deal function, (預設0: 傳入 [[lat,lng,adr], ..], 1: 傳入 GClientGeocoder.getLocations)], data object
    1219
    1220 http://code.google.com/apis/maps/documentation/services.html#Geocoding_Structured
    1221 http://code.google.com/apis/kml/documentation/kmlreference.html#placemark
    1222 http://www.step1.cn/googleapi/map/kml.htm#Placemark
    1223
    1224 to use:
    1225 mapO.getLocations('宿舍',function(r){for(var i=0;i<r.length;i++)sl(i+'/'+r.length+' '+r[i]);});
    1226 */
    1227 getLocations:function(adr,func,d){
    1228  var _t=this,_f=arguments.callee,a
    1229   //	country code
    1230   ,cc={TW:'台灣',US:'United States',JP:'日本',CN:'中国',KR:'大韓民國',KP:'朝鮮',UK:'United Kingdom'}
    1231   ,ga=function(p){	//	從 Placemark 得到住址(address)資料
    1232 	 var c,a,b;
    1233 	 if(c=p.AddressDetails.Country){
    1234 	  b=c.CountryNameCode;
    1235 	  a=b in cc?[cc[b]]:[];
    1236 	  if(c=c.AdministrativeArea){
    1237 	   if(b=c.AdministrativeAreaName)a.push(b);
    1238 	   if(c=c.SubAdministrativeArea){
    1239 	    if(b=c.SubAdministrativeAreaName)a.push(b);
    1240 	    if(c=c.Locality){
    1241 	     if(b=c.PostalCode)a.unshift('['+b.PostalCodeNumber+']');
    1242 	     if(b=c.LocalityName)a.push(b);
    1243 	     if((c=c.Thoroughfare)&&(b=c.ThoroughfareName))
    1244 	      a.push(b);
    1245 	    }
    1246 	   }
    1247 	  }
    1248 	  a=a.join(' ');
    1249 	 }
    1250 	 return p.address?p.address+' ('+a+')':a;
    1251   },
    1252   //	預設代為處理函數組
    1253   f=[
    1254 	//	代為處理傳入 [lat,lng,adr]
    1255 	function(r){
    1256 	 _f.errno=r.Status.code;
    1257 	 if(r instanceof GLatLng)
    1258 	  r=[[r.lat(),r.lng(),adr]];
    1259 	 else if(r.Status.code==G_GEO_SUCCESS){
    1260 	   //sl('getLocations: Get '+r.Placemark.length+' place(s) of ['+r.name+'].');
    1261 	   //sl('getLocations: [0]: '+r.Placemark[0].name);
    1262 	   var i=0,n=r.name,p=r.Placemark,l,a;
    1263 	   for(r=[];i<p.length;i++)
    1264 	    a=ga(p[i])||n+'('+i+')',
    1265 	    //sl('getLocations: ('+p[i].Point.coordinates+') '+a),
    1266 	    l=p[i].Point.coordinates,r.push([l[1],l[0],a]),
    1267 	    _t.adr_to_loc[a]=new GLatLng(l[1],l[0]);
    1268 	 }else r=[];
    1269 	 return func(r);
    1270 	}
    1271    ,
    1272 	//	代為處理傳入 Placemark
    1273 	function(r){
    1274 	 _f.errno=r.Status.code;
    1275 	 if(r instanceof GLatLng)
    1276 	  r={Status:{code:G_GEO_SUCCESS},Placemark:[{Point:{coordinates:[r[0],r[1]]}}]};
    1277 	 else if(r.Status.code==G_GEO_SUCCESS){
    1278 	  //sl('getLocations: find '+r.Placemark.length+' records, '+r.Placemark[0].Point.coordinates+': '+r.Placemark[0].address);
    1279 	  //if(r.Placemark.length==1){var l=r.Placemark[0].Point.coordinates;_t.adr_to_loc[r.Placemark[0].address]=new GLatLng(l[1],l[0]);}
    1280 	  for(var i=0,n=r.name,p=r.Placemark,l;i<p.length;i++)l=p[i].Point.coordinates,_t.adr_to_loc[ga(p[i])||n+'('+i+')']=new GLatLng(l[1],l[0]);
    1281 	 }//else sl('getLocations: search ['+adr+'] fault: ['+r.Status.code+'] '+_t.GeoStatus(r.Status.code));
    1282 	 return func(r);
    1283 	}
    1284   ];
    1285
    1286  if(func instanceof Array)
    1287   a=func[1],
    1288   f=	typeof a=='function'?a
    1289 	:typeof a=='number' && f[a]? f[a]
    1290 	:f[0],
    1291   func=func[0];
    1292  else f=f[0];
    1293
    1294  return _t.getLatLng(adr+='',[f,function(){return _t.geocoder.getLocations(adr,f);}],d);
    1295 },
    1296
    1297 //	直接手動設定
    1298 setLatLng:function(adr,lat,lng){
    1299  if(!(lat instanceof GLatLng))lat=new GLatLng(lat,lng);
    1300  this.adr_to_loc[adr]=lat;
    1301  sl('setLatLng: '+lat+' '+adr);
    1302  return lat;
    1303 },
    1304
    1305 //	經緯度 RegExp
    1306 LatLngR:/^\s*(\d{1,3}(\.\d+)?)\s*,\s*(\d{1,3}(\.\d+)?)\s*$/,
    1307
    1308 /*	未設定 func 則僅回傳 catched 的位置
    1309 	You can define your method by .f(adr,c), for example: search by specified SQL server.
    1310
    1311 TODO:
    1312 繼承 Geocoding cache
    1313 */
    1314 getLatLng:function(adr,func,d){	//	string address, (function(GLatLng) | [func:function(GLatLng), deal_func:function(GLatLng, address)]), data object
    1315  //if(!adr)return;
    1316  var _t=this,m,a,f,c=function(p){
    1317  	 if(!p)sl('getLatLng: Search failed: '+adr);
    1318 	 if(p)_t.adr_to_loc[adr]=p;
    1319 	 return func(p,adr)||p;
    1320 	};
    1321
    1322  if(func instanceof Array)
    1323   f=func[1],func=func[0];
    1324
    1325  //sl('getLatLng: search '+adr);
    1326  //	檢測是否為經緯度
    1327  if(m=adr.match(_t.LatLngR)){
    1328   if(a=(d&&d.description||adr).replace(/<[/]?([bh]r|p)[^>]*>/ig,'\n').match(/[^\r\n]+[市區街路][^\r\n]+/))
    1329    //sl('('+m[1]+','+m[3]+') ['+a[0]+']'),
    1330    if(!((m[1]+','+m[3]) in _t.locArray2_u))_t.locArray2.push(_t.locArray2_u[m[1]+','+m[3]]=[m[1],m[3],a[0]]);
    1331   m=new GLatLng(m[1],m[3]);
    1332   return func?func(m):m;
    1333  //	搜尋已知地址
    1334  }else if((m=_t.adr_to_loc) && (m=m[adr]))//sl('getLatLng: deal adr_to_loc['+adr+']='+m[adr]+' get '+(func?c(m):m)/*+' by '+func*/),
    1335   return func?c(m):m;
    1336  //	搜尋未知地址
    1337  else if(func)
    1338   //sl('<em>Not catched: '+adr+'</em>'),
    1339   return _t.geocoder.getLatLng(adr,typeof f=='function'?function(m){f(m,adr);}:c);	//	 原來需要用 arguments.callee.f，但若已經用 var 定義則可直接使用。
    1340 },
    1341
    1342 /*
    1343 d={
    1344  name:'',	//	這邊 name 被當作 id, title
    1345  description:'HTML',
    1346  type:'',
    1347  data:['','']
    1348 //選用 optional:
    1349  htm:'HTML' / function(obj){return 'HTML';},
    1350 //尚未用到︰
    1351  link:'',
    1352 };
    1353 */
    1354 add:function(d,force){
    1355  if(!d||typeof d!='object')return this;
    1356  var _t=this,o=_t.supportKind(d.type);
    1357  if(!o){_t.searchPoint(d);return this;}
    1358
    1359  //if(!(d.type in _t))_t[d.type]={};
    1360  var _S=_t[d.type],_m=this.map,a;
    1361  //if(typeof _S!='object')sl('add: typeof ['+d.type+'] = '+(typeof _S));
    1362  if((d.name in _S)&&!force)return this;	//	已存在
    1363
    1364  o=_t.kinds[d.type];
    1365  if(d.type=='marker'){
    1366   o=new o(new GLatLng(d.data[0],d.data[1]),_t.getMarkerO(d));
    1367   //if(d.zIndexP)o._zIndexProcess=o._zIndexProcess,o.zIndexProcess=d.zIndexP;	//	** 可以利用 zIndexP 來在 infowindow is opened 時設定 z-index.. 沒用 @ 2008/6/30 19:43:43
    1368  }else a={points:d.data[0],levels:d.data[1],numLevels:4,zoomFactor:16},
    1369 	o=new o.fromEncoded(d.type=='polyline'?a:{polylines:[a],fill:true,outline:true});//geodesic:true	Geodesic means 'along great circle'
    1370
    1371  _t._add(o,d);
    1372
    1373  return this;
    1374 },
    1375 searchPoint:function(adr,name,description){
    1376  var _f=arguments.callee,_t=this,type='marker',_M=_t[type],_m=_t.map,latlng,d;
    1377  if(typeof adr=='object')d=adr,adr=adr.type;else d={type:adr,show:_f.show};	//	預設 searchPoint.show
    1378  if(!d.name)d.name=name||adr;
    1379  if(typeof d.description=='undefined')
    1380   if(description)d.description=description;
    1381   else if(d.name!=adr)d.description=adr;
    1382  if(!d.type)return;
    1383  if(isNaN(d.retry)&&!_t.geocoder.getCache())d.retry=2;	//	找不到時重試次數
    1384
    1385 try{
    1386  if(adr in _M)
    1387   //sl('We already have ['+adr+']'),
    1388   _t.show(_M[adr],type);
    1389  else _t.getLatLng(adr+='',function(point){
    1390 	if(!point){
    1391 	 if(d.retry){
    1392 	  //sl('try once more('+d.retry+'): ['+adr+']');
    1393 	  d.retry--,_f.call(_t,d);return false;
    1394 	 }else{
    1395 	  //sl('searchPoint: not found function: '+_t.notFound);
    1396 	  return _t.notFound(adr,d);
    1397 	 }
    1398 	}else{
    1399 	 if(adr in _M)return;	//	可能經過太久才被 load?
    1400
    1401 	 // ** 注意：這邊沒設 _M[adr]=_M[point.toUrlValue(_t.precision)]
    1402 	 var p=new GMarker(latlng=point,_t.getMarkerO(d));
    1403 	 if(!d.name)d.name=p+'';
    1404 	 //sl('found '+point+' '+d.name+', icon: '+p.getIcon().iconSize);
    1405 	 //point='loc: '+point;
    1406 	 if(typeof d.description=='undefined')d.description=point;
    1407 	 else if(d.description==adr)d.description+='<br/>'+point;
    1408 	 //_M=_M[adr];
    1409 	 if(!('address' in p))p.address=adr;else throw 'GMarker.address was used: ['+p.address+']!';
    1410 	 //sl('Last add '+adr+'..');
    1411 	 _t._add(p,d,type);
    1412 	}
    1413   },d);
    1414 }catch(e){sl('searchPoint: Error: '+adr+', '+_M+': '+e.message);}//throw e;
    1415  return latlng;
    1416 },
    1417 // private:	註冊 o 成為內容 d={}，並設定 click 等 event
    1418 _add:function(o,d,type){
    1419  if(!o)return;
    1420  if(!d)d=o;
    1421  if(!type)type=d.type;
    1422  var _t=this,_S=_t[type],_m=this.map;
    1423
    1424  //if(d.name)_S[d.name]=o;	//	或許已經設定過了，這邊就需要跳過。
    1425  if(((d.name||d)+'') in _S)
    1426   sl('_add: Warning: Type '+type+' 已存在 ['+(d.name||d)+']'+(_S[d.name||d].getLatLng?' '+_S[d.name||d].getLatLng():'')+('address' in _S[d.name||d]?' '+_S[d.name||d].address:'')+'!'
    1427 	//+'<br/>_add: 將以 ['+(o.name||d.name||d)+']'+(o.getLatLng?' '+o.getLatLng():'')+(o.address?' '+o.address:'')+' 覆寫。'
    1428 	);
    1429  _S[d.name||d]=o;	//	必設!!
    1430
    1431  if(_m)_m.addOverlay(o);//_t._addOverlay(o);//
    1432  //if(o.getIcon)sl('_add: show '+o.getIcon().iconSize+' '+o.getIcon().image);
    1433  o.name=d.name,o.dscr=d.description;	//	GMarker 中這兩個本來就有被用，偵測也只會發現已使用。
    1434  //	another way to add tooltip: GControlPosition
    1435  if(!('sHtm' in o)){
    1436   if('htm' in d)o.sHtm=typeof d.htm=='function'?d.htm.call(d,o,type):d.htm;
    1437   else o.sHtm=(d.name?'<em>'+d.name+'</em>'+(d.description?'<br/>':''):'')+(d.description||'');
    1438  }else throw '['+type+'].sHtm was used: ['+o.sHtm+']!';
    1439  if(!('sHtmF' in o)){
    1440   if('htmF' in d)
    1441    o.sHtmF=typeof d.htmF=='function'?d.htmF.call(d,o,type):d.htmF;
    1442  }else throw '['+type+'].sHtmF was used: ['+o.sHtm+']!';
    1443  //	openInfoWindowTabs: http://www.geocodezip.com/mapXmlTabsPlus.asp
    1444  GEvent.addListener(o,"click",function(e){
    1445   _t.showWindow(o);
    1446   _t.runOnClick(o,type,e&&e.target||window.event&&window.event.srcElement);
    1447  });
    1448  if(d.show)_t.show(o,type);
    1449  _t.runAfterAdd(o,type,d,d.name||d);
    1450 },
    1451 _addOverlay:function(o){
    1452  var _t=this,_m=_t.map;
    1453  if(!_t._aa)_t._aa=[];
    1454  if(o){_t._aa.push(o);return;}
    1455  var i;
    1456  while(i=_t._aa.shift())
    1457   _m.addOverlay(i);
    1458 },
    1459
    1460
    1461 //	icon setup
    1462 defaultIconIndex:0,
    1463 iconA:[],
    1464 iconO:{},
    1465 icon:function(index){
    1466  var _t=this;
    1467  if(index instanceof Array){
    1468   //	設定 icon
    1469   _t.iconA=[];
    1470   for(var a,i=0,p,u;i<index.length;i++)
    1471    if(u=index[i]){
    1472     if(typeof u=='string')u={image:u};
    1473     if(u instanceof Object){
    1474      _t.iconA.push(a=new GIcon(u.icon||_t.iconA[_t.defaultIconIndex]||G_DEFAULT_ICON));
    1475      //a=new GIcon(u.icon||_t.iconA[_t.defaultIconIndex]||G_DEFAULT_ICON);
    1476 	 for(p in _t.iconO)	//	default first
    1477       //sl('icon: set icon['+(_t.iconA.length-1)+'].'+p+'='+_t.iconO[p]),
    1478 	  a[p]=_t.iconO[p];
    1479      if('temp' in u){	//	template 2
    1480       for(p in u.temp)
    1481 	   //sl('icon: template set icon['+(_t.iconA.length-1)+'].'+p+'='+u.temp[p]),
    1482 	   a[p]=u.temp[p];
    1483       delete u.temp;
    1484      }
    1485      for(p in u)	//	user set last
    1486 	  //sl('icon: specified set icon['+(_t.iconA.length-1)+'].'+p+'='+u[p]),
    1487 	  a[p]=u[p];
    1488 	 //_t.iconA.push(new GIcon(a));
    1489     }else _t.iconA.push(u);
    1490    }
    1491   return _t.iconA.length;
    1492  }
    1493
    1494  if(index instanceof Object){
    1495   //sl('icon: set default icon option');
    1496   for(i in index)
    1497    //sl('icon: set default ['+i+']=['+index[i]+']'),
    1498    _t.iconO[i]=index[i];
    1499   return;
    1500  }
    1501
    1502  //	return icon[index]
    1503  if(isNaN(index)||index<0||index>=_t.iconA.length)index=_t.defaultIconIndex;
    1504  //sl('icon: return icon['+index+'] '+(_t.iconA[index]?_t.iconA[index].iconSize+' '+_t.iconA[index].image:'G_DEFAULT_ICON'));
    1505  return _t.iconA[index]||G_DEFAULT_ICON;
    1506 },
    1507
    1508 dMarkerO:{},	//	default marker option
    1509 getMarkerO:function(mo,setMO){	//	setMO: set default, 1: add, 2:reset
    1510  var _t=this,i,a={icon:1,title:1,zIndexProcess:1,draggable:1};	//	class GMarkerOptions
    1511  _t.dMarkerO.icon=_t.icon();
    1512  if(setMO){
    1513   if(setMO==2)_t.dMarkerO={};
    1514   setMO=_t.dMarkerO;
    1515  }else{
    1516   //	複製一份
    1517   setMO={};
    1518   for(i in _t.dMarkerO)
    1519    //sl('getMarkerO: from default ['+i+']=['+_t.dMarkerO[i]+']'),
    1520    setMO[i]=_t.dMarkerO[i];
    1521  }
    1522  if(mo instanceof Object)
    1523   for(i in a)
    1524    if(a[i]&&typeof mo[i]!='undefined')
    1525     //sl('getMarkerO: set ['+i+']=['+mo[i]+']'),
    1526 	setMO[i]=mo[i];
    1527  //sl('getMarkerO: icon: '+setMO.icon.iconSize+' '+setMO.icon.image);
    1528  return setMO;
    1529 },
    1530
    1531 /*	增加自己控制的 marker，會自動顯現，但不會列入管控，得自己設定。
    1532 usage:
    1533 mapO.addMarker(dLoc.tw,{draggable:true});
    1534 */
    1535 addMarker:function(loc,opt){
    1536  var _t=this,_m=_t.map,m;
    1537  if(_m){
    1538   if(loc instanceof Array)loc=new GLatLng(loc[0],loc[1]);
    1539   m=new GMarker(loc,_t.getMarkerO(opt));
    1540   //sl('addMarker icon: '+_t.getMarkerO(opt).icon.iconSize+' '+_t.getMarkerO(opt).icon.image);
    1541   //sl('addMarker iconSize: '+_t.getMarkerO(opt).iconSize);
    1542   _m.addOverlay(m);
    1543  }
    1544  return m;
    1545 },
    1546
    1547
    1548 //	f={p:position, m:method(pan/panBy/set), z:zoom}
    1549 setCenter:function(f){
    1550  var _m=this.map;
    1551  if(f instanceof GLatLng||!(f instanceof Object))f={p:f};
    1552
    1553  //sl('setCenter: setZoom ['+(f.z||null)+'] @ '+f.p+' by method ['+(f.m||'setCenter')+'].');
    1554  if(!isNaN(f.z))_m.setZoom(f.z);
    1555  if(f.p){
    1556   if(f.p instanceof Array)f.p=new GLatLng(f.p[0],f.p[1]);
    1557   if(f.m=='pan')_m.panTo(f.p);
    1558   else if(f.m=='panBy')_m.panBy(f.p);
    1559   else _m.setCenter(f.p);
    1560  }
    1561
    1562  return _m.getCenter();
    1563 },
    1564
    1565 //	zoom above 19	You can set zoom up to 30 by using setCenter() not by setZoom() or zoomIn()	firefox: 45.1238,-123.1138	http://esa.ilmari.googlepages.com/highres.htm
    1566 /*
    1567 eval('err_noImage=p(10121);',mapO.map);
    1568 sl(err_noImage);
    1569 */
    1570 zoom:function(z){
    1571  var _t=this,_m=_t.map,m;
    1572  if(typeof z=='string'&&(m=z.match(/^[+-]/)))z=_m.getZoom()+(m[0]=='+'?z:-z);
    1573  if(z)_m.setZoom(z);//try{_m.setCenter(_m.getCenter(),z);}catch(e){}	//	中文中, enableContinuousZoom()? 這麼搞會出錯
    1574  return _m.getZoom();
    1575 },
    1576 //	show, or focus. f={noCenter:false, redraw: false}
    1577 show:function(name,type,f){
    1578  var _t=this,_S=_t[type],_m=_t.map,inC;	//	inC: in control
    1579  if(typeof name=='string')
    1580   if(name in _S)_S=_S[name],inC=1;
    1581   else _S=0;
    1582  else _S=name;
    1583  if(typeof _S!='object'||!_S)return;
    1584
    1585  if(_S.isHidden&&_S.isHidden())_S.show();
    1586  if(typeof f!='object')f={noCenter:f};	//	default: don't set to center
    1587  if(!f.noCenter){
    1588   var p=_t.getPoint(_S,type);
    1589   //sl('show: center= '+p);
    1590   if(_m){
    1591    _m.setCenter(p);
    1592    if(_m.getZoom()<9)_m.setZoom(_t.defaultZoom);
    1593    if(inC)_t.showWindow(_S,p);	//	未管控就 showWindow 會有奇妙的結果。
    1594   }
    1595  }
    1596  if(f.redraw && _S.redraw)_S.redraw(true);	//	Front/back order of markers can be messed simply by moving them in south-north direction. (v1) 	http://koti.mbnet.fi/ojalesa/exam/anim_v2.html
    1597
    1598  return _t;
    1599 },
    1600
    1601 //	show HTML window (obj, point)	o.sHtmF=show HTML flag: {maxContent:'', ..}: see class GInfoWindowOptions
    1602 showWindow:function(o,p){
    1603  //sl('showWindow: '+(p||o.openInfoWindowHtml));
    1604  if(typeof o.openInfoWindowHtml=='function')
    1605   o.openInfoWindowHtml(o.sHtm,o.sHtmF);	//	enableMaximize()
    1606  else this.map.openInfoWindowHtml(p||this.getPoint(o),o.sHtm,o.sHtmF);
    1607 },
    1608
    1609 //	get the GLatLng of the object
    1610 getPoint:function(o,type){
    1611  if(!type)type=this.getKind(o);
    1612  //sl('getPoint: ['+type+']'+o.name);
    1613  if(type=='marker'&&typeof o.getLatLng=='function')return o.getLatLng();
    1614
    1615  if(typeof o.getBounds=='function')
    1616   return o.getBounds().getCenter();
    1617
    1618  if(typeof o.getCenter=='function')
    1619   return o.getCenter();
    1620
    1621  if(typeof o.getVertexCount=='function')
    1622   return o.getVertex(Math.floor(o.getVertexCount()/2));
    1623 },
    1624
    1625 getZoom:function(o,type){
    1626  if(!type)type=this.getKind(o);
    1627  //sl('getPoint: ['+type+']'+o.name);
    1628
    1629  if(typeof o.getBounds=='function')
    1630   return this.getBoundsZoomLevel(o.getBounds());	//	得到適當的 zoom
    1631
    1632  //if(type=='marker')return _t.defaultZoom;
    1633  return _t.defaultZoom;
    1634 },
    1635
    1636
    1637 initMap:function(id,latlng,f){	//	container, center, other initial setting flags
    1638  var _t=this,_m,a;
    1639  //	檢查當前瀏覽器是否支持地圖 API 庫
    1640  if(GBrowserIsCompatible()){
    1641   //	指定GMap使用的圖層 @ id
    1642   if(typeof id=='string')id=document.getElementById(id);
    1643   if(!id)return _t;
    1644   _t.canvas=id;	//	container object
    1645
    1646   if(!f)f={};
    1647   if(!f.size)
    1648    f.size=f.x&&f.y?[f.x,f.y]:[640,320];
    1649   if(f.size instanceof Array)f.size=new GSize(f.size[0],f.size[1]);
    1650
    1651   _m=_t.map=new GMap2(id,f);//=new google.maps.Map2();
    1652   //	設定中心點座標
    1653   _m.setCenter(latlng||new GLatLng(0,0),7);	//	default center.
    1654   //_m.setMapType(G_HYBRID_MAP);
    1655   _m.addMapType(G_PHYSICAL_MAP);	//	地形圖
    1656   _m.addMapType(G_SATELLITE_3D_MAP);	//	with the Google Earth Browser Plug-in
    1657
    1658   //	控制元件	客制化: http://julian.norway.idv.tw/index.php/archives/322
    1659   //_m.addControl((new GHierarchicalMapTypeControl()).addRelationship(G_SATELLITE_MAP, G_HYBRID_MAP, "Labels", true));
    1660   //_m.removeMapType(G_HYBRID_MAP);
    1661   _m.addControl(a=_t.overviewMap=new GOverviewMapControl(new GSize(_m.getSize().width/2.5,_m.getSize().height/2)));	//	可折疊的縮小圖
    1662   a.hide();	//	show(), hide().
    1663   //a.getOverviewMap().addControl(new GMenuMapTypeControl(1));	must use setTimeout: getOverviewMap() is not available until after the module has loaded.
    1664   _m.addControl(new GLargeMapControl());	//	加入地圖縮放工具
    1665   _m.addControl(new GMenuMapTypeControl(1));//GMapTypeControl(1)	//	切換地圖型態的按鈕
    1666   _m.addControl(new GScaleControl());	//	地圖比例尺
    1667   _m.enableScrollWheelZoom();
    1668   _m.enableContinuousZoom();	//	平滑放大
    1669
    1670   GEvent.addListener(_m,'mouseover',function(){_m.showControls();});
    1671   GEvent.addListener(_m,'mouseout',function(){_m.hideControls();});
    1672  }else{
    1673   sl('<em>抱歉，您的瀏覽器不支援 Google Maps！</em>');
    1674  }
    1675  return _t;
    1676 },
    1677
    1678
    1679
    1680 /*	移除所有管控項
    1681 c.f.,	this.map.clearOverlays()
    1682 */
    1683 removeAll:function(type){
    1684  var _t=this,i,o;
    1685  if(!type)for(i in _t.kinds)
    1686   arguments.callee.call(this,i);
    1687  else{
    1688   //sl('removeAll: ('+(typeof type)+') ['+type+'], '+(typeof _t[type]));
    1689   //o=[];for(i in _t[type])o.push(i);for(i=0;i<o.length;i++)_t.remove(o[i],type);
    1690   for(i in _t[type])_t.remove(i,type);
    1691   //_t[type]={};
    1692  }
    1693 },
    1694
    1695 remove:function(n,type){
    1696  var _S=this[type];
    1697  if(n in _S){
    1698   //sl('remove '+type+' ['+n+']: '+(_S[n].name||_S[n].address||_S[n].dscr));
    1699   this.map.removeOverlay(_S[n]);
    1700   delete _S[n];
    1701   this.runAfterRemove(n,type);
    1702  }
    1703  return this;
    1704 },
    1705
    1706 //	http://econym.googlepages.com/example_context.htm
    1707 setContextMenu:function(o){
    1708  var _t=this,_m=_t.map,h;
    1709  if(!_m)return;
    1710
    1711  if(typeof o!='object'){
    1712   h=o;
    1713   o=document.createElement('div');
    1714   o.className='gMap_contextMenu';
    1715   o.innerHTML=h;
    1716  }
    1717
    1718  if(_t.contextMenu)
    1719   _t.contextMenu.replaceNode(o);
    1720  else _m.getContainer().appendChild(_t.contextMenu=o)
    1721   ,GEvent.addListener(_m,'singlerightclick',function(p,t){
    1722    _t.clickLatLng=_m.fromContainerPixelToLatLng(_t.clickPoint=p);
    1723    var x=p.x,y=p.y,w=_m.getSize().width-o.offsetWidth,h=_m.getSize().height-o.offsetHeight;
    1724    if(x>w&&w>0)x=w;
    1725    if(y>h&&h>0)y=h;
    1726    (new GControlPosition(G_ANCHOR_TOP_LEFT,new GSize(x,y))).apply(o);
    1727    _t.showContextMenu(1);
    1728   });
    1729
    1730  _t.showContextMenu(0);
    1731  return o;
    1732 },
    1733 showContextMenu:function(v){
    1734  var o=this.contextMenu;
    1735  if(o)o.style.visibility=v||typeof v=='undefined'?'visible':'hidden';
    1736 },
    1737
    1738 //	get overlay
    1739 getO:function(type,name){
    1740  var s=this[type];
    1741  return name?name in s?s[name]:null:s;
    1742 },
    1743
    1744 //	get name of the type
    1745 getOArray:function(type){
    1746  var i,a=[],o=this[type];
    1747  if(o)for(i in o)a.push(i);
    1748  return a;
    1749 },
    1750
    1751
    1752 /*
    1753 var i,t=[],o;
    1754 o=GGeoXml.prototype;//GMap2.prototype
    1755 sl('['+(typeof o)+'] '+(o+'').replace(/\n/g,'<br/>')+'<hr/>',1);for(i in o)t.push('['+(typeof o[i])+'] '+i);sl(t.sort().join('<br/>'));
    1756
    1757 TODO:
    1758 GEvent.addListener(map,"addoverlay",function(overlay){if(overlay.name){}});
    1759 */
    1760 loadXML:function(URL){
    1761  var _t=this,x=new GGeoXml(URL);
    1762  //	.getDefaultCenter(), .getDefaultBounds() 可能是 null
    1763  _t.setCenter({p:x.getDefaultCenter(),z:x.getDefaultBounds(),m:'pan'});
    1764  _t.map.addOverlay(x);
    1765  return _t.xml[URL]=x;
    1766 },
    1767
    1768 //resize map
    1769 resize:function(x,y){
    1770  with(this.map.getContainer().style)
    1771   width=x+'px',height=y+'px';
    1772 },
    1773
    1774 //	去除商標, Copyright message
    1775 removeTM:function(l){
    1776  var a=this.canvas;
    1777  if(!a)return;
    1778  a=a.getElementsByTagName('a'),i=a.length,t=1;
    1779  //sl('removeTM: '+UnicodeToHTML(document.getElementById('map_canvas').innerHTML));
    1780  for(;i>0&&(t||l);){
    1781   i--;
    1782   //	http://www.google.com/intl/en_ALL/help/terms_maps.html
    1783   if(t && a[i].href.indexOf('terms_maps')!=-1 && a[i].parentNode.tagName.toLowerCase()=='div'){
    1784    //sl('removeTM: remove copyright: '+a[i].href);
    1785    //sl('removeTM: remove copyright: '+UnicodeToHTML(a[i].parentNode.innerHTML));
    1786    removeNode(a[i].parentNode,1);	//	連這div都刪除會有奇怪現象發生
    1787    t=0;
    1788   }else if(l && a[i].innerHTML.indexOf('poweredby.png')!=0){
    1789    //sl('removeTM: remove logo: '+UnicodeToHTML(a[i].parentNode.innerHTML));
    1790    removeNode(a[i].parentNode,1);
    1791    l=0;
    1792   }
    1793  }
    1794 }
    1795
    1796 };	//	_.prototype=
    1797
    1798
    1799 return _;
    1800 })();	//	(function(){
    1801
    1802 //	===================================================
    1803
    1804
    1805 /*	2008/9-10/1
    1806 	搜尋用代理工具
    1807
    1808 usage:
    1809 google.load("search","1",{language:"ja_JP",callback:loadSearch});
    1810 function loadSearch(){
    1811  gSearch=new getSearch(function(r,p){
    1812   sl('<a href="'+r.unescapedUrl+'">'+r.title+'</a><br/><div style="margin-left:3em;font-size:.8em;">'+r.content+'</div>');
    1813  });
    1814 }
    1815
    1816
    1817 TODO:
    1818 Yahoo! Search BOSS	http://developer.yahoo.com/search/boss/
    1819
    1820 LocalSearch:
    1821 http://www.google.com/uds/samples/apidocs/static-tiles.html
    1822 http://code.google.com/apis/ajaxsearch/documentation/reference.html#_class_GlocalSearch
    1823
    1824 */
    1825 function getSearch(fn,kind){	//	deal function, kind: Web/Local
    1826  if(!kind)kind='Web';
    1827  var _t=this,_s=typeof google!='undefined'?google.search:0;
    1828  if(!_s||!_s[kind+'Search'])return;
    1829  _s=_t.searcher=new _s[kind+'Search']();
    1830
    1831  if(kind=='Local'){
    1832   //sl('Set center: '+'Taiwan');
    1833   _s.setCenterPoint('台灣');//Taiwan
    1834   _s.setResultSetSize(google.search.Search.LARGE_RESULTSET);
    1835   //_s.setCenterPoint("93108");
    1836  }else{
    1837   _s.setNoHtmlGeneration();
    1838   //.addSearcher(_s,(new google.search.SearcherOptions()).setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN));
    1839  }
    1840
    1841  _s.setResultSetSize(google.search.Search.LARGE_RESULTSET);
    1842
    1843  _s.setSearchCompleteCallback(_t,_t.searchComplete[kind],[_s]);
    1844  if(fn)_t.sf=fn;
    1845  return _t;
    1846 }
    1847 getSearch.prototype={
    1848
    1849 //	country translate
    1850 countryT:{Taiwan:'台灣'},
    1851
    1852 searchComplete:{
    1853 Local:function(searcher){
    1854  var r=searcher.results,i=0,a,b,j;
    1855  if(r&&r.length>0)for(;i<r.length;i++){
    1856   o=r[i],a=o.country;
    1857   if(a in this.countryT)a=this.countryT[a];
    1858   o.address=a+o.region+o.city+o.streetAddress;
    1859   o.phone=[];
    1860   if(a=o.phoneNumbers)
    1861    for(j=0;j<a.length;j++)
    1862     o.phone.push((a[j].type?a[j].type+': ':'')+a[j].number);
    1863   this.sf(o);
    1864  }
    1865 /*
    1866 	var imageUrl = GlocalSearch.computeStaticMapUrl(searcher.results, 350, 400);
    1867 	document.getElementById("resultsImg").src = imageUrl;
    1868 */
    1869 },
    1870 Web:function(searcher){
    1871  var s=searcher,p=s.cursor.currentPageIndex,i=0,r=s.results;
    1872  //sl('<hr/>page '+p+':');
    1873  if(r&&r.length)for(;i<r.length;i++)
    1874   this.sf(r[i],p);
    1875  s.gotoPage(p+1);	//	這會一直執行到不能執行為止。(2008/7: 0-3)
    1876 }
    1877
    1878 },	//	searchComplete
    1879
    1880
    1881 //	deal function
    1882 sf:function(r,p){
    1883 },
    1884
    1885 s:function(w){
    1886  //sl('getSearch: search ['+w+']');
    1887  if(w)this.searcher.execute(w);
    1888 }
    1889
    1890 };
    1891
    1892
    1893
    1894
    1895
    1896
    1897
    1898
    1899 return (
    1900 	CeL.net.map
    1901 );
    1902 };
    1903
    1904 //===================================================
    1905
    1906 CeL.setup_module(module_name, code_for_including);
    1907
    1908 };
    1909