//
// Copyright 2011 by Kelvin Thompson. All Rights Reserved.
//
// FILE: mfutil.js
//

//==========================================================================//

function isPosInteger ( inputStr )
{
  if ( inputStr == null )
    return false;

  var regexp = /^\d*$/;
  inputStr = inputStr.toString();
  return inputStr.match(regexp) ? true : false;
}

function isNumber ( inputStr )
{
  if ( inputStr == null )
    return false;

  var regexp = /^[-]?\d*\.?\d*$/;
  inputStr = inputStr.toString();
  return inputStr.match(regexp) ? true : false;
}

function isAllWhite ( inputStr )
{
  var regexp = /\S/;
  return inputStr.search(regexp) < 0;
}

function isElemType ( obj, name )
{
  return obj  &&
         obj.nodeType == 1  &&       //Node.ELEMENT_NODE==1
         obj.nodeName.toLowerCase() == name.toLowerCase();
}

function isADiv ( obj )
{
  return isElemType( obj, "div" );
}

function isATextNode ( obj )
{
  return obj && obj.nodeType==3;       //Node.TEXT_NODE==3
}

function mfAttachOnclickListener ( elem, func )
{
  if ( elem.addEventListener )
    elem.addEventListener( "click", func, false );
  else
    elem.attachEvent( "onclick", func );
}

//==========================================================================//

function mfOrderedArray ( )
{
  this.array = new Array();
  this.count = 0;
}

mfOrderedArray.prototype.getArray = function ( )
{
  this.array.length = Math.min( this.count, this.array.length );
  return this.array;
}

mfOrderedArray.prototype.getCount = function ( )
{
  return this.count;
}

mfOrderedArray.prototype.getElement = function ( num )
{
  return this.array[num];
}

mfOrderedArray.prototype.getLast = function ( )
{
  return this.count ? this.array[this.count-1] : null;
}

mfOrderedArray.prototype.push = function ( obj )
{
  this.array[this.count++] = obj;
  return obj;
}

mfOrderedArray.prototype.deleteLast = function ( )
{
  if ( this.count > 0 )
    delete this.array[--this.count];
}

//==========================================================================//

var g_BlogIndexWindow = null;

function handleIndexButton ( )
{
  if ( !g_BlogIndexWindow || g_BlogIndexWindow.closed )
    g_BlogIndexWindow = window.open('http://mapfrappe.blogspot.com');
  else
    g_BlogIndexWindow.focus();
}
  
function handleExamplesButton ( )
{
  var exampleDiv = document.getElementById("exampleDiv");
  var button     = document.getElementById("exampleButton");
  var buttText   = button.firstChild;
  var idxButton  = document.getElementById("indexButton");

  if ( exampleDiv.style.display == "block" )
  {
    exampleDiv.style.display = "none";
    buttText.nodeValue = buttText.nodeValue.replace( /Hide/, "Show" );
    
    if (idxButton)
      idxButton.style.display = "inline";
  }
  else
  {
    exampleDiv.style.display = "block";
    buttText.nodeValue = buttText.nodeValue.replace( /Show/, "Hide" );
    
    if (idxButton)
      idxButton.style.display = "none";
  }
}

function scrubExampleList ( )
{
  var pendingContainers = new Array();
  var pendingHeaders    = new Array();
  var pendingLinks      = new Array();

  var listTop = document.getElementById("listTop");
  listTop.style.display = "block";

  pendingContainers.push( listTop );
  while ( pendingContainers.length || pendingHeaders.length || pendingLinks.length )
  {
    while ( pendingContainers.length )
    {
      var obj = pendingContainers.shift();
      obj.className = "listContainer";

      for ( var child=obj.firstChild; child; child=child.nextSibling )
      {
        if ( isADiv(child) )
        {
          var textNode = child.firstChild;
          if ( isATextNode(textNode)  &&  ! isAllWhite(textNode.nodeValue) )
          {
            if ( textNode.nodeValue.charAt(0) == "+" )
              pendingHeaders.push( child );
            else if ( child.getAttribute("id")  &&  child.id.substring(0,5) == "link-" )
              pendingLinks.push( child );
          }
        }
      }
    }

    while ( pendingHeaders.length )
    {
      var obj = pendingHeaders.shift();
      obj.className = "listHeader";
      
      // remove "+ " from start of string
      var txtNode = obj.firstChild;
      if ( txtNode )
        txtNode.nodeValue = txtNode.nodeValue.substring(2);

      // add "+" icon
      var imgElem = document.createElement("img");
      imgElem.src = "plus.png";
      imgElem.alt = "+";
      imgElem.hspace = 2;
      obj.insertBefore( imgElem, txtNode );
      
      // add listeners
      mfAttachOnclickListener( obj, handleListHeaderClick );

      // kill margin on top-level headers
      if ( obj.parentNode  &&  obj.parentNode.id == "listTop" )
        obj.style.marginLeft = "0";
      
      for ( var sib=obj.nextSibling; sib; sib=sib.nextSibling )
      {
        if ( isADiv(sib) )
        {
          pendingContainers.push( sib );
          break;
        }
      }
    }

    while ( pendingLinks.length )
    {
      var obj = pendingLinks.shift();
      obj.className = "listLink";
      mfAttachOnclickListener( obj, handleListLinkClick );
    }
  }

  document.getElementById("listTop").style.marginLeft = "0";
}

function handleListHeaderClick ( evt )
{
  // get browser-independent event
  evt = evt ? evt : (window.event ? window.event : null);
  if ( ! evt )
    return false;

  // get broswer-independent target element
  var elem = evt.target ? evt.target : evt.srcElement;
  if ( ! elem )
    return false;

  // if we hit the +/- IMG element, move up to the parent
  if ( isElemType(elem,"img") )
    elem = elem.parentNode;

  // find the next sibling that is a DIV
  var container;
  for ( container=elem.nextSibling;
        container && !isADiv(container);
        container=container.nextSibling )
    {
      // no-op
    }

  // bail out if no sibling found
  if ( ! container )
    return false;

  // See if the container is displayed or not. Note that
  // 'container.style.display' is initially the empty string.
  if ( container.style.display == "block" )
  {
    // hide items below this header
    container.style.display = "none";
    
    // switch icon to "+"
    elem.firstChild.src = "plus.png"
    elem.firstChild.alt = "+";
  }
  else
  {
    // show items below this header
    container.style.display = "block";

    // switch icon to "-"
    elem.firstChild.src = "minus.png";
    elem.firstChild.alt = "-";
  }

  return false;
}

function handleListLinkClick ( evt )
{
  evt = evt ? evt : window.event;
  if ( ! evt )
    return false;
  
  var elem = evt.target ? evt.target : evt.srcElement;
  if ( ! elem )
    return false;

  var saveNum = elem.id.substring(5);
  mfuf_fetchURL( saveNum );

  return false;
}

//==========================================================================//

var g_initialSizeNum = -1;

function handleMapSizeChange ( )
{
  var map1Div  = document.getElementById("map1div");
  var map2Div  = document.getElementById("map2div");
  var chg1Div  = document.getElementById("sizeChanger1");
  var chg2Div  = document.getElementById("sizeChanger2");
  var chg1Span;
  var chg2Span;

  // find the child span
  if (chg1Div.childNodes[1]  &&  chg1Div.childNodes[1].nodeType == 1)   //Node.ELEMENT_NODE==1
  {
    chg1Span = chg1Div.childNodes[1];
    chg2Span = chg2Div.childNodes[1];
  }
  else if (chg1Div.firstElementChild)
  {
    chg1Span = chg1Div.firstElementChild;
    chg2Span = chg2Div.firstElementChild;
  }
  else if (chg1Div.firstChild  &&  chg1Div.firstChild.nodeType == 1)   //Node.ELEMENT_NODE==1
  {
    chg1Span = chg1Div.firstChild;
    chg2Span = chg2Div.firstChild;
  }
  
  if (g_initialSizeNum < 0)
  {
    g_initialSizeNum = parseInt(map1Div.style.height);
  }
  
  if (parseInt(map1Div.style.height) <= g_initialSizeNum)
  {
    map1Div.style.height = "600px";
    chg1Div.title        = "Make maps smaller (shorter)";
    chg1Span.innerText   = "Shrink Maps ↑";
    chg1Span.innerHTML   = "Shrink Maps ↑";
  }
  else
  {
    map1Div.style.height = g_initialSizeNum + "px";
    chg1Div.title        = "Make maps larger (taller)";
    chg1Span.innerText   = "Enlarge Maps ↓";
    chg1Span.innerHTML   = "Enlarge Maps ↓";
  }
  
  map2Div.style.height = map1Div.style.height;
  chg2Div.title        = chg1Div.title;
  chg2Span.innerText   = chg1Span.innerText;
  chg2Span.innerHTML   = chg1Span.innerHTML;
  handleMapResize();
}

//==========================================================================//

// "mfum" = MAPfrappe URL Make

// REQUIRED to be assigned by page-specific code:
// mfum_phpURL        - string containing URL for PHP
// mfum_makeXML       - function to build XML from current page state
// mfum_urlCallback   - function called with persistent URL
  
// OPTIONAL to be assigned by page-specific code
var mfum_abortCallback = null;  // function called when request aborted

// "private" variables
var mfum_abortTimeoutId = null;  // ID for timeout
var mfum_request        = null;  // in-progress request object

// invoked via Function.call() - so 'this' is an instance of 'urlGen'
function mfum_checkReqState ( req )
{
  // Safari 3.0.4 (and probably other versions) doesn't seem to invoke
  // 'onreadystatechange' for 'readyState==1', so we must call setRequestHeader()
  // elsewhere. 
  //if ( req.readyState == 1 )
  //{
  //  req.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  //  return;
  //}

  if ( !req  ||  !mfum_request  ||  req.readyState != 4 )
    return;

  // clear pending timeout - server has responded in a timely manner
  if ( mfum_abortTimeoutId != null )
  {
    window.clearTimeout( mfum_abortTimeoutId );
    mfum_abortTimeoutId = null;
  }
  
  // Clear our saved reference to the XMLHttpRequest object. Only
  // mfum_abortTimeoutFunc() requires that reference, and now we know
  // that won't be necessary.
  mfum_request = null;

  // parse the XML text that came back from POST
  var xmlNode = GXml.parse(req.responseText);
  
  // look for 'rowid' element in XML node set
  var idElement = xmlNode.documentElement.getElementsByTagName("rowid");
  if ( idElement.length != 1 )
  {
    alert( "Unfortunately, an error occurred attempting to create the permanent URL." );
    return;
  }
  
  // get value of 'rowid' element - it should be a positive integer
  var idStr = idElement[0].getAttribute('value');
  if ( ! isPosInteger(idStr) )
  {
    alert( "Unfortunately, an error occurred attempting to create the permanent URL." );
    return;
  }

  // get URL of the current page - truncate anything after '?'
  var baseURL = location.href;
  var qmarkIdx = location.href.indexOf( '?' );
  if ( qmarkIdx > 0 )
    baseURL = location.href.slice( 0, qmarkIdx );
  
  // assemble the new URL
  var newURL = baseURL + "?show=" + idStr;
  
  // call page-specific method to display the new URL
  mfum_urlCallback( newURL );
}

function mfum_abortTimeoutFunc ( )
{
  mfum_abortTimeoutId = null;
  
  var req = mfum_request;
  mfum_request = null;
  
  // kill the pending XMLHttpRequest
  if ( req )
    req.abort();

  // call page-specific handler for request that timed out
  if ( (typeof mfum_abortCallback) == "function" )
    mfum_abortCallback();
}

function mfum_requestURL ( )
{
  if ( mfum_request )
    return false;

  mfum_request = GXmlHttp.create();
  
  mfum_request.open( "POST", mfum_phpURL, true );
  mfum_request.onreadystatechange = function(){mfum_checkReqState(mfum_request);};
  mfum_request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  mfum_request.send( "xml=" + mfum_makeXML() );
  
  mfum_abortTimeoutId = window.setTimeout( "mfum_abortTimeoutFunc()", 17000 );
  
  return true;
}

//==========================================================================//

// "mfuf" = MAPfrappe URL Fetch

// REQUIRED to be assigned by page-specific code:
// mfuf_phpURL        - string containing URL for PHP
// mfuf_parseXMLNode  - function to parse saved XML
  
// OPTIONAL to be assigned by page-specific code
var mfuf_abortCallback = null;  // function called when request aborted

// "private" variables
var mfuf_abortTimeoutId = null;  // ID for timeout
var mfuf_request        = null;  // in-progress request object

function mfuf_checkReqState ( req )
{
  // Safari 3.0.4 (and probably other versions) doesn't seem to invoke
  // 'onreadystatechange' for 'readyState==1', so we must call setRequestHeader()
  // elsewhere. 
  //if ( req.readyState == 1 )
  //{
  //  req.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  //  return;
  //}

  if ( !req  ||  !mfuf_request  ||  req.readyState != 4 )
    return;

  // clear pending timeout - server has responded in a timely manner
  if ( mfuf_abortTimeoutId != null )
  {
    window.clearTimeout( mfuf_abortTimeoutId );
    mfuf_abortTimeoutId = null;
  }

  // Clear our saved reference to the XMLHttpRequest object. Only
  // mfuf_abortTimeoutFunc() requires that reference, and now we know
  // that won't be necessary.
  mfuf_request = null;

  // parse the XML text that came back from POST
  var xmlNode = GXml.parse( req.responseText );

  // 'error' element in XML indicates a problem
  var errElem = xmlNode.documentElement.getElementsByTagName("error");
  if ( errElem.length != 0 )
  {
    var msg = "Unfortunately, an error occurred attempting to retrieve the saved map.";
    var textNode = errElem[0].firstChild;
    var text = textNode ? textNode.nodeValue : null;
    if ( textNode && textNode.nodeType==3 && textNode.nodeValue )
      msg += "\n\n" + textNode.nodeValue;
    alert( msg );
    return;
  }
  
  // call page-specific XML parser
  mfuf_parseXMLNode( xmlNode );
}

function mfuf_abortTimeoutFunc ( )
{
  mfuf_abortTimeoutId = null;
  
  var req = mfuf_request;
  mfuf_request = null;
  
  // kill the pending XMLHttpRequest
  if ( req )
    req.abort();

  // call page-specific handler for request that timed out
  if ( (typeof mfuf_abortCallback) == "function" )
    mfuf_abortCallback();
}

mfuf_fetchURL = function ( idnum )
{
  if ( mfuf_request )
    return false;

  mfuf_request = GXmlHttp.create();
  mfuf_request.open( "POST", mfuf_phpURL, true );
  mfuf_request.onreadystatechange = function(){mfuf_checkReqState(mfuf_request);};
  mfuf_request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  mfuf_request.send( "id=" + idnum );
  
  mfuf_abortTimeoutId = window.setTimeout( "mfuf_abortTimeoutFunc()", 17000 );
  
  return true;
}


