/*
 * ssw_mod.js
 *
 * 12/07/2008
 *
 * Javascript required by the various smart tags.
 */


/**
 * If your template implements an onload/onresize handler which adjusts the layout
 * for example the document height so it takes up any spare space,
 * set this to your layout adjust function
 */
var ss_adjust_layout_func = null;

/**
 * Some functions cause dynamic content to show or hide.
 * Once they have shown their content, they should call this.
 * As such, we need to adjust for the "to top" links and to make the page grow to take up the blank space.
 *
 * Since the CMS is template specific, this will call the hook you set in variable:
 * ss_page_height_adjust_func
 */
function ssAdjustLayout()
{
  if (typeof ss_adjust_layout_func == 'function') {
    ss_adjust_layout_func();
  }
}

//----------------------------------------------------------------------------------------------------

/*
 * Functions required by the ssw_children_browser tag
 *
 * Functions for showing and revealing the content of a list of children.
 * It is recomended that the javascript, if any, used by the children content is not too hard core
 * and that it is tested before deployment.
 *
 * The way to use these is to make a select element which contains options whose values are the id of the item you want to show or hide.
 */

/**
 * ssw_children_browser
 *
 * Shows the child with the given id number and hides all others
 */
function cbShowChild(sel)
{
  for (i = 0; i < sel.length; i++) {
    var id = sel.options[i].value;
    var el = document.getElementById(id);
    if (el != null) {
      if (i == sel.selectedIndex) {
	el.style.display = "";
      } else {
	el.style.display = "none";
      }
    }
  }

  // this does not always work:
  // blank space is not always consumed
  ssAdjustLayout();

  // maybe we need a slight delay until the doc has had time to change
  // nope: same result...
  //setTimeout('ssAdjustLayout()', 100);
  
}

/**
 * ssw_children_browser
 *
 * When you have made all elements visible, call this to collapse all but the selected one.
 */
function cbShowSelected(id_sel)
{
  var sel = document.getElementById(id_sel);
  if (sel == null) return;

  cbShowChild(sel);

  // not needed, in cdShowChild()
  //ssAdjustLayout();
}

/**
 * ssw_children_browser
 *
 * Selects the option on the given select and makes sure the content is shown.
 */
function cbSelectIndex(sel, index)
{
  if (index >= 0) {
    sel.selectedIndex = index;
    cbShowChild(sel);
  }
}

/**
 * ssw_children_browser
 *
 * Shows all the children
 */
function cbShowAll(id_sel)
{
  var sel = document.getElementById(id_sel);
  if (sel == null) return;

  for (i = 0; i < sel.length; i++) {
    var id = sel.options[i].value;
    var el = document.getElementById(id);
    if (el != null) {
      el.style.display = "";
    }
  }

  ssAdjustLayout();
}



/**
 * ssw_children_browser
 *
 * Selects the child with the given id from the select with the given name.
 * It assumes the option value holds the ids of the elements the select is switching between.
 */
function cbSelectChild(id_sel, id)
{
  var index = -1;
  var sel = document.getElementById(id_sel);
  if (sel != null) {
    for (i = 0; i < sel.length; i++) {
      if (sel.options[i].value == id) {
	index = i;
	break;
      }
    }
  }

  cbSelectIndex(sel, index);
}

/**
 * ssw_children_browser
 *
 * Selects the next child
 */
function cbNextChild(id_sel)
{
  var index = -1;
  var sel = document.getElementById(id_sel);
  if (sel != null) {
    index = sel.selectedIndex + 1;
    if (index >= sel.length) {
      index = 0;
    }
    cbSelectIndex(sel, index);
  }
}

/**
 * ssw_children_browser
 *
 * Selects the previous child
 */
function cbPrevChild(id_sel)
{
  var index = -1;
  var sel = document.getElementById(id_sel);
  if (sel != null) {
    index = sel.selectedIndex - 1;
    if (index < 0) {
      index = sel.length - 1;
    }
    cbSelectIndex(sel, index);
  }
}


//----------------------------------------------------------------------------------------------------

/*
 * Functions for relating to the ssw_dealer_search tag.
 *
 *
 * {PRE: the variables pf_product, pf_country have been defined}
 *
 * All functions operate on a single module id and find the items they need based on it.
 * PHP generates the prefixes for each element and they are in the javascript variables:
 * pf_product: product menu prefix string variable
 * pf_country: country menu prefix string variable
 *
 * The country array names are encoded by readable product name as follows:
 * pf_product + product name + mod_id
 * so to use these arrays you need to eval() a statement to asign the array.
 * You will then need to down-case the country name and replace its spaces with underscores to use it as a value in the options using dsEncodeCountry()
 *
 * While this sounds like terrible overkill, I decided to be consistent with the module ID so the CMS is more robust and less restrictive.
 *
 * UPDATE: I am removing mod_id until it is required. The google map initialisation would become overkill for no reason.
 */

// country select takes us to dealer/country/product

/**
 * Will drop the given string to lower case and replace spaces with underscore.
 * 
 * PHP equivalent: SSWContent::countryURL()
 *
 * @param p the country string
 * @return the encoded string
 */
function dsCountryURL(s)
{
  s = s.toLowerCase();
  s = s.replace("\ ", "_");
  return s;
}

/**
 * Selecting a product fills the country select.
 *
 * @param $mod_id with this we discover the array of products, countries and the menus
 */
function dsFillCountries()
{
  var p_sel = document.getElementById(pf_product + "menu");
  if (p_sel == null) return;

  var c_sel = document.getElementById(pf_country + "menu");
  if (c_sel == null) return;

  var product = p_sel.options[p_sel.selectedIndex].value;
  // grab the countries array
  eval("var a = " + pf_product + product + ";");


  // if the selected product also has dealers in the newly selected country already selected, go there
  var country = c_sel.options[c_sel.selectedIndex].value; // already lowercased with underscores
  var found_country = false;
  if (country != null) {
    var cv = null;
    for (var i = 0; i < a.length; i++) {
      cv = dsCountryURL(a[i]);
      if (cv == country) {
	found_country = true;
	break;
      }
    }
  }
 

  if (found_country) {
    //jump to product/country without even refilling the menu
    self.location.replace("dealers/" + product + "/" + country + "/");
  } else {

    // clear the countries
    c_sel.options.length = 0;

    // refil from array a
    c_sel.options[0] = new Option("select country:", "-1");
    for (var i = 0; i < a.length; i++) {
      c_sel.options[i+1] = new Option(a[i], dsCountryURL(a[i]));
    }

    c_sel.disabled = false;

  }
}


function dsRelocate(product, country)
{
  /********* HARDCODED SITE SECTION *****************/
  var section = "dealers";
  /********* HARDCODED SITE SECTION *****************/  


  //alert(product + "/" + country);


  // On IE, calling location replace with a relative URL, does not respect the document base
  // and appends the relative URL to the current URL. We must get the base URL.
  // We have to get the document <base>

 // RESTRICTION: works on all browsers I have but IE
  //self.location.replace(section + "/" + product + "/" + country + "/");

  // getting document <base>
  var ba = document.getElementsByTagName("base"); // find the document base
  if (ba.length > 0) {
    self.location.replace(ba[0].href + section + "/" + product + "/" + country + "/"); // works on all so far
  }

}


/**
 * If we have both a product and a dealer, make a URL and go to that location
 */
function dsLoadDealers()
{
  var p_sel = document.getElementById(pf_product + "menu");
  if (p_sel == null) return;
  var product = p_sel.options[p_sel.selectedIndex].value;
  if (product == -1) return;

  var c_sel = document.getElementById(pf_country + "menu");
  if (c_sel == null) return;
  var country = c_sel.options[c_sel.selectedIndex].value;
  if (country == -1) return;

  dsRelocate(product, country);
}

//----------------------------------------------------------------------------------------------------

/*
 * Globals initialisation.
 * These used to be in dealer_search.inc pluggin but I have moved them out here.
 * If we ever need more than one map, move them back so they can be tagged with mod_ID
 *
 * NOTE:
 * All the map related objects must exist in the same window as their map for IE to work with maps.
 */
var map = null;
var geocoder = null;	// for looking up addresses
var fallback = null;
var frm = null; // the frame with our dealer listing divs
var ds_init_country = null; // country with which the init was invoked
var da = null;	// dealer addresses to be used with the geocoder
var ca = null;	// html content for each dealer to be show in the info bubble, formated by cm_php/dealers.php
var ma = null;	// markers
var last_clicked_dealer_index = -1;	// this dealer index (not id) was clicked last, helps in dsAddToMap

/**
 *  Puts all our markers in view.
 *
 * {PRE: there is at least one marker in the markers array}
 */
function dsZoomToFit()
{
  if (map == null) return;

  var cp = null;
  var zoom = null;
  if (ma.length == 1) {
    cp = ma[0].getPoint();
    zoom = map.getZoom();
  } else if (ma.length > 1) {
    var b = new GLatLngBounds();
    for (var j = 0; j < ma.length; j++) {
      if (ma[j] == null) continue;
      if (!b.containsLatLng(ma[j].getPoint())) {
	b.extend(ma[j].getPoint());
      }
    }

    cp = b.getCenter();
    zoom = map.getBoundsZoomLevel(b);
  }


  // snappy, centre and zoom display
  // map.setCenter(cp, zoom);

  // this is nicer, smoothly animated if all is cached
  map.setZoom(zoom);
  map.panTo(cp);
}



/**
 * Create a marker.
 * Update to hold full details.
 */
function dsCreateMarker(point, name)
{
  var marker = new GMarker(point, {icon:G_DEFAULT_ICON, title:name});
  var html = "<b>" + name + "</b><br />Hey there";
  GEvent.addListener(marker, 'click', function() {
    marker.openInfoWindowHtml(html);
  });
  return marker;
}


/**
 * Displays the info overlay for dealer addresses the geocoder failed the find
 */
function dsResponseFallback(name)
{
  var cm = map.getContainer();
  //var cm = parent.document.getElementById("ds_map");

  if (cm == null) return;


  //
  // GEO-ERROR fallback, parented to the map for easy positioning
  //
  if (fallback == null) {
    /*
     * IE6 does not let us absolute position our dynamically generated div.
     * The div must be in the HTML.
     */
    /*
    fallback = document.createElement("div");
    fallback.setAttribute("id", "ds_fallback");
    fallback.setAttribute("class", "dealerResultsFallback");
    fallback.style.display = "none";
    cm.appendChild(fallback);
    */

    /*
     * These reparenting shenanigans work on IE
     */
    fallback = document.getElementById("ds_fallback");
    cm = document.getElementById("ds_map_container");
    var rem = cm.removeChild(fallback);
    fallback = rem;
    cm = map.getContainer();
    cm.appendChild(fallback);
  }


  // find marker and close all info windows
  var f = -1;
  for (var i = 0; i < da.length; i++) {
    if ((f < 0) && (da[i] == name)) {
      f = i;
    }
    if (ma[i] != null) {
      ma[i].closeInfoWindow();
    }
  }
  

  // position the fallback in absolute position, (0,0) being its parent's top left
  if (f >= 0) {
    // display data from found marker
    // note: close button is from google maps
    //fallback.style.display = "block";
    fallback.style.visibility = "visible";
    fallback.innerHTML = "<div style='position:absolute; right: 2ex; top: 2ex'><a href='javascript:dsCloseFallback()'><img src='cm_img/iw_close.gif' /></a></div>"
      + ca[f] + "\n"
      + "<p style='text-align:center; padding-top:3ex'>"
      + "<i>- not found on the map -</i></p>";
    fallback.style.left = ((cm.offsetWidth - fallback.offsetWidth) / 2) + "px";
    fallback.style.top = ((cm.offsetHeight - fallback.offsetHeight) / 3) + "px";
  } else {
    // last resort notification method
    alert("We were unable to translate \"" + name + "\" to map coordinates.");
  }

}


/**
 * Handler for dsShowOnMap() geocoding request.
 *
 * TO-DO:
 * decide what to do with places which do not geocode
 * - write a tool to store coordinates in a separate table in ssw or masterdb ?
 * - manually add them ?
 *
 * Solution:
 * Display their info in "fallback" since the info is more essential than the marker.
 * @see: dsResponseFallback()
 */
function dsAddToMap(response)
{
  //map.clearOverlays();
  if (!response || (response.Status.code != 200)) {
    dsResponseFallback(response.name);
  } else {
    
    if (ma[last_clicked_dealer_index] != null) return;

    var place = response.Placemark[0];
    var point = new GLatLng(place.Point.coordinates[1],
			    place.Point.coordinates[0]);
    var marker = new GMarker(point);

    // find the bubble text
    // {PRE: bubble text exists}
    for (var i = 0; i < da.length; i++) {
      if (da[i] == response.name) {
	GEvent.addListener(marker, 'click', function() {
			     marker.openInfoWindowHtml(ca[i]);
			   });
	map.addOverlay(marker);
	marker.openInfoWindowHtml(ca[i]);
	ma[last_clicked_dealer_index] = marker;
	break;
      }
    }
    

  }

}


/**
 * Shows the specified dealer on the map
 */
function dsShowOnMap(index)
{
  if (!da) return;

  if ((index < 0) || (index >= da.length)) {
    alert("Index out of bounds");
    return;
  }

  var div = null;
  var spn = null;

  // deactivate all listings, activate clicked
  for (var i = 0; i < da.length; i++) {
    div = frm.document.getElementById("di_" + i);
    spn = frm.document.getElementById("di_hi_" + i); //home icon
    if (div != null) {
      div.className = "dealerResults";
      spn.style.visibility = "hidden";
    }
  }
  div = frm.document.getElementById("di_" + index);
  spn = frm.document.getElementById("di_hi_" + index);
  if (div != null) {
    div.className = "dealerResultsOn";
    spn.style.visibility = "visible";
  }


  //if (fallback != null) fallback.style.display = "none";
  if (fallback != null) fallback.style.visibility = "hidden";


  // show existing marker or geocode it
  if (ma[index] != null) {
    map.panTo(ma[index].getPoint());
    ma[index].openInfoWindowHtml(ca[index]);
  } else {
    geocoder.getLocations(da[index], dsAddToMap);
    last_clicked_dealer_index = index;
  }

  return true;
}


/**
 * Gets all dealer geocodes and shows them on the map.
 * Interactive only.
 */
function dsShowAll()
{
  for (var i = 0; i < da.length; i++) {
    if (ma[i] != null) {
      ma[i].closeInfoWindow();
    }
  }

  dsZoomToFit();
}


//-----------------------
/*
 * Custom button taken from the google maps example
 */
function FitZoomControl()
{
}
// need the try block for pages which do not include the google maps library
// ie. everything othe than the dealers' page
try {
  FitZoomControl.prototype = new GControl();
  FitZoomControl.prototype.initialize = function(map) {
    var container = document.createElement("div");

    this.setButtonStyle_(container);
    container.appendChild(document.createTextNode("Fit All"));
    GEvent.addDomListener(container, "click", function() {
			    dsShowAll();
			  });

    map.getContainer().appendChild(container);
    return container;
  }

  // By default, the control will appear in the top left corner of the
  // map with 7 pixels of padding.
  FitZoomControl.prototype.getDefaultPosition = function() {
    return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 7));
  }

  // Sets the proper CSS for the given button element.
  FitZoomControl.prototype.setButtonStyle_ = function(button) {
    button.style.textDecoration = "none";
    button.style.color = "#0000cc";
    button.style.backgroundColor = "white";
    button.style.font = "small Arial";
    button.style.border = "1px solid black";
    button.style.padding = "2px";
    button.style.marginBottom = "3px";
    button.style.textAlign = "center";
    button.style.width = "3em";
    button.style.cursor = "pointer";
  }
} catch(e) {
}
//--------------------

/**
 * @return a GLatLngBounds which contains the country with the given code or null if we cannot find it
 */
function dsCountryBounds(alpha2)
{
  if (!ds_ba) return;

  for (var i = 0; i < ds_ba.length; i++) {
    if (ds_ba[i][0] == alpha2) {
      //alert("1: " + ds_ba[i][1] +", 2:"+ ds_ba[i][2] +", 3:" + ds_ba[i][3] +", 4:"+ ds_ba[i][4]);
      return new GLatLngBounds(new GLatLng(ds_ba[i][2], ds_ba[i][1]), //sw
			       new GLatLng(ds_ba[i][4], ds_ba[i][3])); //ne
    }
  }

  return null;
}


/**
 * Wraps up the initialisation.
 */
function dsInitializeMap_response(response)
{
  var zoom = 5;
  var alpha2 = "AU";
  var p = null;


  if (!response || (response.Status.code != 200)) {
    p = new GLatLng(-33.885522,151.216249); // Serendipity Software offices
    alert("No geo for " + ds_init_country);
  } else {
    var place = response.Placemark[0];
    p = new GLatLng(place.Point.coordinates[1], // Note: they are reversed
		    place.Point.coordinates[0]);

    //
    // get the bounding box
    //
    // Note: 26/11/2008
    // For some reason, probably political, direct geocoding of the Taiwan island does not produce a country code.
    // I guess Google could not bring themselves to use either TW or CN
    //
    if ((place.AddressDetails.Country == null) && (place.address.toLowerCase() == "taiwan")) {
      alpha2 = "TW";
    } else {
      alpha2 = place.AddressDetails.Country.CountryNameCode;
    }
  }

  
  map.setCenter(p, zoom);

  var b = dsCountryBounds(alpha2);
  if (b) {
    zoom = map.getBoundsZoomLevel(b);
    map.setZoom(zoom);
  }


  map.addControl(new GLargeMapControl());
  //map.addControl(new GMapTypeControl());
  map.addControl(new FitZoomControl());
}


/**
 * Make sure you call this after the page containing the map and the iframe has loaded.
 *
 * Adds our load event function on top of the already assigned one.
 *
 * From: http://simonwillison.net/2004/May/26/addLoadEvent/
 */
function dsAttachOnLoadMapInit(country)
{
  var old_onload = window.onload;

  window.onload = function() {

    // preserve previous onload handler
    if (typeof old_onload == 'function') {
      old_onload();
    }

    frm = window.frames["ds_iframe"];
    if (frm == null) return;
    da = frm.da;
    ca = frm.ca;
    ma = frm.ma;


    // IMPORTANT:
    // GBrowserIsCompatible() must be called from the window containing the map
    // and NOT the iframe outherwise, the map does not scroll as you drag your mouse over it,
    // it only scrolls as you start your drag over the map but move your mouse over the iframe.
    // In fact, all map objects and functions which are related must be.
    if (GBrowserIsCompatible()) {
      map = new GMap2(document.getElementById("ds_map"));
      if (map == null) return;

      geocoder = new GClientGeocoder();

      ds_init_country = country;

      var cll = false;
      if (ds_init_country) {
	geocoder.getLocations(ds_init_country, dsInitializeMap_response);
      } else {
	dsInitializeMap_response(null);
      }

    } 
  }
}


/**
 * Closes the geocoding fallback div
 */
function dsCloseFallback()
{
  var div = document.getElementById("ds_fallback");
  if (div == null) return;
  //div.style.display = "none";
  div.style.visibility = "hidden";
}


//----------------------------------------------------------------------------------------------------

