// 
// Google Big Maps v8
// Copyright (C) Oliver Azeau
// http://barrejadis.azeau.com/section/Google-Big-Maps
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// ==UserScript==
// @name           Google Big Maps v8
// @namespace      azeau.com
// @description    Generates a big map by selecting top-left and bottom-right coordinates
// @include        http://maps.google.*/*
// ==/UserScript==


function mydebug(txt)
{
  //alert(txt);
}

function QueryStringToArray(querystring) {
  var allvars = [];
  if(querystring==0)
    return allvars;
  var vars = querystring.split("&");
  for (var i=0; i<vars.length; i++) {
 		var pair = vars[i].split("=");
    allvars[pair[0]] = pair[1];
  }
  return allvars;
} 

//=== image searching : base class of all searches ===

function ImageSearcher()
{
  this.host = 0;
  this.querystring = 0;
  this.zoom = 999;


  this.GoThroughImages = function()
  {
    var params;
    var r = new RegExp();
    var urlPattern = this.hostPattern+'(.*)';
    r.compile(urlPattern);
    var allImages = document.getElementsByTagName('img');
    for (var i = 0; i < allImages.length; i++) {
      var src = allImages[i].src;
      var urlparams = r.exec(src);
      if( urlparams == null )
        continue;
      params = QueryStringToArray(urlparams[2]);
      if( !this.Update(params) )
        continue;
      this.host = urlparams[1];
      this.querystring = urlparams[2];
    }
    this.GMSetValues();
  };
  
  this.Update = function(params)
  {
    var nzoom = params['z'];
    if( this.zoom !=999 && this.zoom != nzoom ) // skip miniature images
      return false;
    this.zoom = nzoom;
    this.UpdateXY(params['x'],params['y']);
    return true;
  };
  
  this.GMSetValues = function()
  {
    mydebug('GMSetValues host='+this.host+' / querystring='+this.querystring+' / zoom='+this.zoom);
    GM_setValue(this.imageType+'host', this.host);
    GM_setValue(this.imageType+'querystring', this.querystring);
    GM_setValue(this.imageType+'zoom', this.zoom);
  };
}

TopLeftSearcher.prototype = new ImageSearcher();
function TopLeftSearcher()
{
  this.x = 10000000;
  this.y = 10000000;
  
  this.UpdateXY = function(x,y)
  {
    if( x < this.x )
      this.x = x;
    if( y < this.y )
      this.y = y;
  };
  
  this.ExecuteSearch = function()
  {
    this.GoThroughImages();
    mydebug(this.imageType+' - minx='+this.x+'/miny='+this.y);
    GM_setValue(this.imageType+'minx', this.x);
    GM_setValue(this.imageType+'miny', this.y);
  }
}

BottomRightSearcher.prototype = new ImageSearcher();
function BottomRightSearcher()
{
  this.x = -10000000;
  this.y = -10000000;
  
  this.UpdateXY = function(x,y)
  {
    if( x > this.x )
      this.x = x;
    if( y > this.y )
      this.y = y;
  };
  
  this.ExecuteSearch = function()
  {
    this.GoThroughImages();
    mydebug(this.imageType+' - maxx='+this.x+'/maxy='+this.y);
    GM_setValue(this.imageType+'maxx', this.x);
    GM_setValue(this.imageType+'maxy', this.y);
  }
}

//=== map searching ===
mapHostPattern = '(http://mt.*\.google\..*/vt.)';

function MapTopLeftSearcher() {}
MapTopLeftSearcher.prototype = new TopLeftSearcher();
MapTopLeftSearcher.prototype.imageType = 'map';
MapTopLeftSearcher.prototype.hostPattern = mapHostPattern;

function MapBottomRightSearcher() {}
MapBottomRightSearcher.prototype = new BottomRightSearcher();
MapBottomRightSearcher.prototype.imageType = 'map';
MapBottomRightSearcher.prototype.hostPattern = mapHostPattern;

//=== satellite searching ===
satelliteHostPattern = '(http://kh.*\.google\..*/kh.)';

function SatelliteTopLeftSearcher() {}
SatelliteTopLeftSearcher.prototype = new TopLeftSearcher();
SatelliteTopLeftSearcher.prototype.imageType = 'satellite';
SatelliteTopLeftSearcher.prototype.hostPattern = satelliteHostPattern;

function SatelliteBottomRightSearcher() {}
SatelliteBottomRightSearcher.prototype = new BottomRightSearcher();
SatelliteBottomRightSearcher.prototype.imageType = 'satellite';
SatelliteBottomRightSearcher.prototype.hostPattern = satelliteHostPattern;

//=== result document generation ===
function ImageGenerator(host,addparam,querystring,zoom)
{
  mydebug('ImageGenerator host='+host+' / addparam='+addparam+' / querystring='+querystring+' / zoom='+zoom);
  this.querystring = querystring;
  this.yBase = 70;
  this.host = host;
  this.apn = addparam;
  this.zoom = zoom;
  this.gWidth = 0;
  this.gHeight = 0;
  
  this.Execute = function(display)
  {
    var html = '';
    if( this.host == 0 )
      return html;
    var params = QueryStringToArray(this.querystring);
    var side = 256/display.displayFactor;
    for(var x=display.minx; x<=display.maxx; x++) {
      for(var y=display.miny; y<=display.maxy; y++) {
        var left = (x-display.minx)*side;
        var top = (y-display.miny)*side+this.yBase;
        html = html + "\n<img src=\""+this.host+this.apn+"="+params[this.apn]+"&hl="+params["hl"]+"&x="+x+"&y="+y+"&z="+this.zoom+"\" style=\"position: absolute; left: "+left+"px; top: "+top+"px; width: "+side+"px; height: "+side+"px;\"/>";
      }
    }
    this.gWidth = (display.maxx-display.minx+1)*side;
    this.gHeight = (display.maxy-display.miny+1)*side;
    return html;
  }

}

function ImagesDisplay()
{
  this.displayFactorElement = false;
  this.pictureElement = false;
  this.controlPanelElement = false;
  this.logZoneElement = false;
  this.mapGenerator = new ImageGenerator(GM_getValue('maphost'),'lyrs',GM_getValue('mapquerystring'),GM_getValue('mapzoom'));
  this.satelliteGenerator = new ImageGenerator(GM_getValue('satellitehost'),'v',GM_getValue('satellitequerystring'),GM_getValue('satellitezoom'));
  this.minx = GM_getValue('mapminx');
  this.miny = GM_getValue('mapminy');
  this.maxx = GM_getValue('mapmaxx');
  this.maxy = GM_getValue('mapmaxy');
  if( this.minx > this.maxx ) {
    this.minx = GM_getValue('satelliteminx');
    this.miny = GM_getValue('satelliteminy');
    this.maxx = GM_getValue('satellitemaxx');
    this.maxy = GM_getValue('satellitemaxy');
  }
  this.displayFactor = 4;
  
  this.ResizeTopPlus = function() { this.miny--; this.Refresh(); }
  this.ResizeTopMinus = function() { this.miny++; this.Refresh(); }
  this.ResizeLeftPlus = function() { this.minx--; this.Refresh(); }
  this.ResizeLeftMinus = function() { this.minx++; this.Refresh(); }
  this.ResizeBottomPlus = function() { this.maxy++; this.Refresh(); }
  this.ResizeBottomMinus = function() { this.maxy--; this.Refresh(); }
  this.ResizeRightPlus = function() { this.maxx++; this.Refresh(); }
  this.ResizeRightMinus = function() { this.maxx--; this.Refresh(); }

  this.Refresh = function()
  {
    this.displayFactor = this.displayFactorElement.options[this.displayFactorElement.selectedIndex].label;
    this.pictureElement.innerHTML = this.GetHTML();
    this.pictureElement.style.width = this.mapGenerator.gWidth;
    this.pictureElement.style.height = this.mapGenerator.gHeight;
  }

  this.ClosePanel = function() {
    this.controlPanelElement.style.display = 'none';
    this.logZoneElement.style.display = 'none';
    this.mapGenerator.yBase = 0;
    this.satelliteGenerator.yBase = 0;
    this.Refresh();
  }
  
  this.GetHTML = function() {
    return this.satelliteGenerator.Execute(this)+this.mapGenerator.Execute(this);
  }
  
  this.EncodeHtml = function(html) {
   encodedHtml = html;
   encodedHtml = encodedHtml.replace(/</g,"&lt;");
   encodedHtml = encodedHtml.replace(/>/g,"&gt;");
   return encodedHtml;
  }
     
  this.LogImages = function()
  {
    this.ClearLog();
    this.Log('minx='+minx+' / maxx='+maxx);
    this.Log('miny='+miny+' / maxy='+maxy);
    this.Log('topLeft='+topLeft);
    this.Log('bottomRight='+bottomRight);
    this.Log(EncodeHtml(this.pictureElement.innerHTML));
  }

  this.ClearLog = function()
  {
    this.logZoneElement.innerHTML = '';
  }

  this.Log = function(txt)
  {
    this.logZoneElement.innerHTML = this.logZoneElement.innerHTML+'<br/>'+txt;
  }
}

//=== buttons callbacks ===
function StoreTopLeft(event)
{
  var mapSearchMin = new MapTopLeftSearcher();
  mapSearchMin.ExecuteSearch();
  var satelliteSearchMin = new SatelliteTopLeftSearcher();
  satelliteSearchMin.ExecuteSearch();
}

function StoreBottomRight(event)
{
  var mapSearchMax = new MapBottomRightSearcher();
  mapSearchMax.ExecuteSearch();
  var satelliteSearchMax = new SatelliteBottomRightSearcher();
  satelliteSearchMax.ExecuteSearch();
}

function GetDocument(event)
{
  var imagesDisplay = new ImagesDisplay();
  var newwindow = window.open("about:blank", "_blank");
  var ndoc = newwindow.document;
  var plusImg = '<img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00%07tIME%07%D7%09%10%11%1B%03%B3%3D%E0%BE%00%00%007IDATx%DAc%FC%FF%FF%3F%03)%80%89%81D%80%5D%03%23%23%23%23%23%23-m%18P%0D%8C%90%60%C5%E5Ed%00QI%AE%0D%98%C1%0A7r%F8%06%2B%0Dm%00%00%F1%C4%12%17%AAXq)%00%00%00%00IEND%AEB%60%82"/>';
  var minusImg = '<img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00%07tIME%07%D7%09%10%11%1D%2F%D7%BF%2B%DB%00%00%00(IDATx%DAc%FC%FF%FF%3F%03)%80%89%81D0%225%B0%A0%F1%19%19%19%D1D%D0%C2%9Dd%1B%18G%23%8E%16%1A%00%15F%09%1B%03%F3%069%00%00%00%00IEND%AEB%60%82"/>';
  var idSource = imagesDisplay.toSource();
  ndoc.write('\n\
<html>\n\
  <head>\n\
    <title>Google Big Map</title>\n\
    <script>\n'
    +QueryStringToArray+'\n\
    var imagesDisplay='+idSource.substring(1,idSource.length-1)+';\n\
    window.onload = function() { \n\
      imagesDisplay.displayFactorElement = document.getElementById("displayFactor");\n\
      imagesDisplay.pictureElement = document.getElementById("picture");\n\
      imagesDisplay.controlPanelElement = document.getElementById("controlPanel");\n\
      imagesDisplay.logZoneElement = document.getElementById("logzone");\n\
    };\n\
  </script>\n\
  </head>\n\
  <body>\n\
  <div id="controlPanel">\n\
    <select id="displayFactor" onChange="javascript:imagesDisplay.Refresh();">\n\
      <option label="1">100%</option>\n\
      <option label="2">50%</option>\n\
      <option label="4" selected=selected">25%</option>\n\
      <option label="8">12.5%</option>\n\
      <option label="16">6.25%</option>\n\
    </select>\n\
    <div style="position: absolute; left: 200px; top: 0px;">\n\
      <a onClick="javascript:imagesDisplay.ResizeTopPlus();">'+plusImg+'</a>\n\
      <a onClick="javascript:imagesDisplay.ResizeTopMinus();">'+minusImg+'</a>\n\
    </div>\n\
    <div style="position: absolute; left: 160px; top: 20px;">\n\
      <a onClick="javascript:imagesDisplay.ResizeLeftPlus();">'+plusImg+'</a>\n\
      <a onClick="javascript:imagesDisplay.ResizeLeftMinus();">'+minusImg+'</a>\n\
    </div>\n\
    <div style="position: absolute; left: 200px; top: 40px;">\n\
      <a onClick="javascript:imagesDisplay.ResizeBottomPlus();">'+plusImg+'</a>\n\
      <a onClick="javascript:imagesDisplay.ResizeBottomMinus();">'+minusImg+'</a>\n\
    </div>\n\
    <div style="position: absolute; left: 240px; top: 20px;">\n\
      <a onClick="javascript:imagesDisplay.ResizeRightPlus();">'+plusImg+'</a>\n\
      <a onClick="javascript:imagesDisplay.ResizeRightMinus();">'+minusImg+'</a>\n\
    </div>\n\
    <a style="position: absolute; left: 100px; top: 10px;" onClick="javascript:imagesDisplay.ClosePanel();">\n\
    <img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00%08tEXtComment%00%F6%CC%96%BF%00%00%00%2BIDAT(%CFc%D4s%D9%CC%40%0A%60b%20%11%D0%5E%03%0B2%E7%E2n%1F%AC%8A%F4%5D%B7%0Cf%3F%0C%07%0D%8C%C3%20-%01%002%CF%05k%84%82%07%05%00%00%00%00IEND%AEB%60%82"></a>\n\
    <div style="display: none;">\n\
      <canvas>empty</canvas>\n\
    </div>\n\
  </div>\n\
  <div id="picture">');

  ndoc.write(imagesDisplay.GetHTML());
  
  ndoc.write('</div>\n\
  <div id="logzone"><br/><br/></div>\n\
</body>');
  ndoc.close();
}

// initialize button bar
var buttonBar = document.createElement('span');
buttonBar.innerHTML = '<a><img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00%08tEXtComment%00%F6%CC%96%BF%00%00%00*IDAT(%CFc%D4s%D9%CC%40%08%5C%DC%ED%03g31%90%08h%AF%81%05%97%5B%07%CEI%23R%03%E3%FF%FF%FF%87%BA%1F%00%7D%CA%07%F3%CA%05)%94%00%00%00%00IEND%AEB%60%82"/></a><a><img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00%08tEXtComment%00%F6%CC%96%BF%00%00%00%2BIDAT(%CFc%D4s%D9%CC%40%0A%60b%20%11%D0%5E%03%0B2%E7%E2n%1F%AC%8A%F4%5D%B7%0Cf%3F%0C%07%0D%8C%C3%20-%01%002%CF%05k%84%82%07%05%00%00%00%00IEND%AEB%60%82"/></a><a><img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%09pHYs%00%00%0B%12%00%00%0B%12%01%D2%DD~%FC%00%00%00%08tEXtComment%00%F6%CC%96%BF%00%00%00(IDAT(%CFc%FC%FF%FF%3F%03)%80%89%81D%40%7B%0D%2C%C4(%D2w%DD2%98%FD0%1C4%B0%E0%0A%EF!%E4%07%00%A9%10%05q%EB%3D%C8%13%00%00%00%00IEND%AEB%60%82"/></a>';
/*
var paneltabs = document.getElementById('paneltabs');
if( paneltabs != null ) {
  var theRow = paneltabs.childNodes[0].childNodes[0];
  var theCell = theRow.childNodes[theRow.childNodes.length-1];
  theCell.appendChild(buttonBar);
} else {
  var mainmap = document.getElementById('main_map');
  if( mainmap != null ) {
    var leaflinks = mainmap.childNodes[0].childNodes[0];
    leaflinks.appendChild(buttonBar);
  }
}
*/
var holder = document.getElementById('endedge-links');
if( holder != null ) {
  holder.appendChild(buttonBar);
}
buttonBar.style.marginLeft = '10px';

// attach callbacks
var topLeftLink = buttonBar.childNodes[0];
topLeftLink.addEventListener('click', StoreTopLeft, true );
var docLink = buttonBar.childNodes[1];
docLink.addEventListener('click', GetDocument, true );
var bottomRightLink = buttonBar.childNodes[2];
bottomRightLink.addEventListener('click', StoreBottomRight, true );

