/* Code for park resorts widget
 Version 1.0.12.20110828
 ----------------------------- */

/* Page event handlers */
var events = {
  ///<summary>Page onload handler</summary>
  ///<param name="eEvt" type="Event object">The event object</param>
  onLoad: function(eEvt) {
    try {
      // onload code
    }
    catch (e) { }
  },

  searchBtn: {
    ///<summary>Search button onclick handler</summary>
    ///<param name="eEvt" type="Event object">The event object</param>
    click: function(eEvt) {
      try {
        // Local variables
        var oMapWrapperDiv = document.getElementById("mapWrapper"),
            oPoweredDiv = document.getElementById("powered"),
            oSearchBtnDiv = document.getElementById("searchBtn"),
            oDropDown = document.getElementById("ddCounty"),
            iItems = oJSON.ParkResorts.Items.length,
            iZIndex = 0;

        // Check dropdown's value
        if (oDropDown.value === "") {
          // Collapse widget to 200x208 and reposition some elements if no selection is made
          oMapWrapperDiv.className = "hidden";
          oPoweredDiv.style.top = "-2px";
          oSearchBtnDiv.style.top = "28px";
        }
        else {
          // Expand widget to 200x507 and reposition some elements
          oMapWrapperDiv.className = "";
          oPoweredDiv.style.top = "-11px";
          oSearchBtnDiv.style.top = "22px";

          // [[[Manolo note]]] Declare these here to keep Google's Maps API code, and because initializing earlier causes issues
          var mapOptions = {
                zoom: 8,
                center: new google.maps.LatLng(0, 0),
                disableDefaultUI: true,
                mapTypeId: google.maps.MapTypeId.ROADMAP
              },
              map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);

          // Clear parkResorts array to include only new ones for selected county
          parkWidget.parkResorts = [];

          // Get all park resorts from JSON file that match selected county
          while (iItems--) {
            // If there is a match add it to array for google maps
            if (oJSON.ParkResorts.Items[iItems].County === oDropDown.value) {
              // Ignore those park resorts with no latitude or no longitude
              if (oJSON.ParkResorts.Items[iItems].Latitude.length > 0 && oJSON.ParkResorts.Items[iItems].Longitude.length > 0) {
                parkWidget.parkResorts.push([oJSON.ParkResorts.Items[iItems].Attraction, oJSON.ParkResorts.Items[iItems].Latitude, oJSON.ParkResorts.Items[iItems].Longitude, ++iZIndex, oJSON.ParkResorts.Items[iItems].Address, oJSON.ParkResorts.Items[iItems].Details, oJSON.ParkResorts.Items[iItems].Website, oJSON.ParkResorts.Items[iItems].PRFlag]);
              }
            }
          }

          // Set map markers
          parkWidget.addMarkers(map, parkWidget.parkResorts);
        }
      }
      catch (e) { }
    }
  }
};

/* Widget object */
var parkWidget = {
  parkResorts: [],
  lastInfoBox: null,

  ///<summary>Adds markers to map</summary>
  ///<param name="map" type="DOM Object" required="true">The event object</param>
  ///<param name="location" type="Array" required="true">Array containing locations' information</param>
  addMarkers: function(map, locations) {
    try {
      // My local variables
      var sURL = "";

      // [[[Manolo note]]] Kept code similar to Google Maps API for easy maintenance
      // Add markers to the map
      // Marker sizes are expressed as a Size of X,Y where the origin of the image (0,0) is located in the top left of the image.
      // Origins, anchor positions and coordinates of the marker increase in the X direction to the right and in the Y direction down.
      var imageI = new google.maps.MarkerImage('http://www.caravanningamigos.co.uk/Widgets/images/pinI.png',
      // This marker is 20 pixels wide by 32 pixels tall.
      new google.maps.Size(24, 41),
      // The origin for this image is 0,0.
      new google.maps.Point(0, 0),
      // The anchor for this image is the base of the flagpole at 0,32.
      new google.maps.Point(10, 32));

      var shadowI = new google.maps.MarkerImage('http://www.caravanningamigos.co.uk/Widgets/images/pinI_Shadow.png',
      // The shadow image is larger in the horizontal dimension while the position and offset are the same as for the main image.
        new google.maps.Size(24, 41),
        new google.maps.Point(0, 0),
        new google.maps.Point(10, 32));

      // Shapes define the clickable region of the icon. The type defines an HTML <area> element 'poly' which 
      // traces out a polygon as a series of X,Y points. The final coordinate closes the poly by connecting to the first coordinate.
      var shapeI = {
        coord: [1, 1, 17, 1, 19, 3, 19, 23, 10, 32, 1, 23, 0, 3],
        type: 'poly'
      };

      // Add another marker image and shadow for PR pins
      var imagePR = new google.maps.MarkerImage('http://www.caravanningamigos.co.uk/Widgets/images/pinPR.png',
      // This marker is 20 pixels wide by 32 pixels tall.
      new google.maps.Size(30, 41),
      // The origin for this image is 0,0.
      new google.maps.Point(0, 0),
      // The anchor for this image is the base of the flagpole at 0,32.
      new google.maps.Point(17, 33));

      var shadowPR = new google.maps.MarkerImage('http://www.caravanningamigos.co.uk/Widgets/images/pinPR_Shadow.png',
      // The shadow image is larger in the horizontal dimension while the position and offset are the same as for the main image.
        new google.maps.Size(30, 41),
        new google.maps.Point(0, 0),
        new google.maps.Point(17, 33));

      // Shapes define the clickable region of the icon. The type defines an HTML <area> element 'poly' which 
      // traces out a polygon as a series of X,Y points. The final coordinate closes the poly by connecting to the first coordinate.
      var shapePR = {
        coord: [1, 1, 24, 1, 26, 3, 26, 25, 17, 33, 11, 26, 1, 26, 0, 3],
        type: 'poly'
      };

      // Bounds needed to make sure all markers are contained within viewport
      var bounds = new google.maps.LatLngBounds();
      // Loop through locations and create markers
      for (var i = 0; i < locations.length; i++) {
        var pin = locations[i];
        var myLatLng = new google.maps.LatLng(pin[1], pin[2]);
        if (pin[7] === "false") {
          marker = new google.maps.Marker({
            position: myLatLng,
            map: map,
            shadow: shadowI,
            icon: imageI,
            shape: shapeI,
            title: pin[0],
            zIndex: pin[3],
            address: pin[4],
            details: pin[5],
            url: pin[6],
            prPin: pin[7]
          });
        }
        else {
          // PR pin
          marker = new google.maps.Marker({
            position: myLatLng,
            map: map,
            shadow: shadowPR,
            icon: imagePR,
            shape: shapePR,
            title: pin[0],
            zIndex: pin[3],
            address: pin[4],
            details: pin[5],
            url: pin[6],
            prPin: pin[7]
          });
        }

        // Add custom InfoWindow
        google.maps.event.addListener(marker, 'click', function() {
          // Remove last opened infoBox
          if (parkWidget.lastInfoBox != null) {
            parkWidget.lastInfoBox.setMap(null);
          }
          // Clean url
          sURL = this.url;
          if (this.url.length > 0 && this.url.toLowerCase().indexOf("http://") < 0) {
            sURL = "http://" + sURL;
          }
          // Create new infoBox
          var infoBox = new InfoBox({ latlng: this.getPosition(), map: map, content: "<div class='iwOtherWrapper'><div class='iwTitle'>" + this.title + "</div><div class='iwAddress'>" + this.address + "</div><div class='iwDetails'>" + this.details + "</div><div class='iwWebsite'><a href='" + sURL + "' title='" + this.title + "' target='_blank'>" + sURL + "</a></div></div>" });
          // Set new infoBox as last opened
          parkWidget.lastInfoBox = infoBox;
        });

        // Make sure all markes fit inside the viewport and they are zoomed properly
        bounds.extend(myLatLng);
        map.fitBounds(bounds);
      }
    }
    catch (e) { }
  },

  ///<summary>Loads counties to dropdown</summary>
  loadDropDown: function() {
    try {
      // Local variables
      var oDropDown = document.getElementById("ddCounty"),
          iItems = oJSON.ParkResorts.Items.length,
          aUniqueCounties = new Array(),
          sCounty = "",
          iIndex = 0,
          oOption = null;

      // Read JSON object and build values for dropdown
      while (iItems--) {
        sCounty = oJSON.ParkResorts.Items[iItems].County;
        // Only add unique values, use array polyfill
        if (aUniqueCounties.indexOf(sCounty) < 0) {
          aUniqueCounties.push(sCounty);
        }
      }

      // Sort counties and add them to dropdown
      aUniqueCounties.sort();
      for (; iIndex < aUniqueCounties.length; iIndex++) {
        oOption = document.createElement("option");
        oOption.setAttribute("value", aUniqueCounties[iIndex]);
        oOption.innerHTML = aUniqueCounties[iIndex];
        oDropDown.appendChild(oOption);
        oOption = null;
      }
    }
    catch (e) { }
  }
};

/* Helper methods (utilities) */
var util = {
  addEvent: null,
  removeEvent: null,

  ///<summary>Stops event bubbling and prevents default action on caller object from running</summary>
  ///<param name="eEvt" type="Event object" required="true">The event object</param>
  stopEvent: function(eEvt) {
    try {
      if (typeof eEvt.preventDefault === "function") { // Non-IE browsers
        eEvt.preventDefault();
        eEvt.stopPropagation();
      }
      else {
        eEvt.returnValue = false;
        eEvt.cancelBubble = true;
      }
    }
    catch (e) { }
  }
};

// Init-time branching for event handling methods
if (typeof window.addEventListener === "function") {
  util.addEvent = function(oElement, sEventName, sFunctionName) {
    oElement.addEventListener(sEventName, sFunctionName, false);
  };
  util.removeEvent = function(oElement, sEventName, sFunctionName) {
    oElement.removeEventListener(sEventName, sFunctionName, false);
  };
} else if (typeof document.attachEvent === "function") { // <=IE8
  util.addEvent = function(oElement, sEventName, sFunctionName) {
    oElement.attachEvent("on" + sEventName, sFunctionName);
  };
  util.removeEvent = function(oElement, sEventName, sFunctionName) {
    oElement.detachEvent("on" + sEventName, sFunctionName);
  };
} else { // older browsers
  util.addEvent = function(oElement, sEventName, sFunctionName) {
    oElement["on" + sEventName] = sFunctionName;
  };
  util.removeEvent = function(oElement, sEventName, sFunctionName) {
    oElement["on" + sEventName] = null;
  };
}

({
  ///<summary>Immediate function to initialize page</summary>
  init: function() {
    try {
      // Local variables
      var oLink = document.createElement("LINK"),
          oAddToSite = document.getElementById("lnkAddToSite"),
          oRefresh = document.getElementById("lnkRefresh");

      // Add CSS
      oLink.setAttribute("href", "http://www.caravanningamigos.co.uk/Widgets/styles.css");
      oLink.setAttribute("rel", "stylesheet");
      oLink.setAttribute("type", "text/css");
      oLink.setAttribute("media", "all");
      document.getElementsByTagName("HEAD")[0].appendChild(oLink);

      // Define array search polyfill (minified) (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf)
      if(!Array.prototype.indexOf)Array.prototype.indexOf=function(d){if(this===void 0||this===null)throw new TypeError;var c=Object(this),b=c.length>>>0;if(b===0)return-1;var a=0;arguments.length>0&&(a=Number(arguments[1]),a!==a?a=0:a!==0&&a!==1/0&&a!==-(1/0)&&(a=(a>0||-1)*Math.floor(Math.abs(a))));if(a>=b)return-1;for(a=a>=0?a:Math.max(b-Math.abs(a),0);a<b;a++)if(a in c&&c[a]===d)return a;return-1};    

      // Load counties to dropdown
      parkWidget.loadDropDown();

      // Add page load event
      util.addEvent(window, "load", function(eEvt) { events.onLoad(eEvt); });

      // Add page load event
      util.addEvent(document.getElementById("btnSearch"), "click", function(eEvt) { events.searchBtn.click(eEvt); });
    }
    catch (e) { }
  }
}).init();

/* Pamela Fox - Google - Custom info window (modified by Manolo to fit this requirements - somewhat painful but worthwhile) */
/* An InfoBox is like an info window, but it displays
 * under the marker, opens quicker, and has flexible styling.
 * @param {GLatLng} latlng Point to place bar at
 * @param {Map} map The map on which to display this InfoBox.
 * @param {Object} opts Passes configuration options - content,
 *   offsetVertical, offsetHorizontal, className, height, width
 */
function InfoBox(opts) {
  google.maps.OverlayView.call(this);
  this.latlng_ = opts.latlng;
  this.map_ = opts.map;
  this.content_ = opts.content;
  this.offsetVertical_ = -196;
  this.offsetHorizontal_ = -40;
  this.height_ = 172;
  this.width_ = 167;
  // New [Start]
  this.enableEventPropagation_ = false;
  this.eventListener1_ = null;
  this.eventListener2_ = null;
  this.eventListener3_ = null;
  this.moveListener_ = null;
  this.contextListener_ = null;
  // New [End]

  var me = this;
  this.boundsChangedListener_ =
    google.maps.event.addListener(this.map_, "bounds_changed", function() {
      return me.panMap.apply(me);
    });

  // Once the properties of this OverlayView are initialized, set its map so
  // that we can display it.  This will trigger calls to panes_changed and
  // draw.
  this.setMap(this.map_);
}

/* InfoBox extends GOverlay class from the Google Maps API
 */
InfoBox.prototype = new google.maps.OverlayView();

/* Creates the DIV representing this InfoBox
 */
InfoBox.prototype.remove = function() {
  if (this.div_) {
    this.div_.parentNode.removeChild(this.div_);
    this.div_ = null;
  }
};

/* Redraw the Bar based on the current projection and zoom level
 */
InfoBox.prototype.draw = function() {
  // Creates the element if it doesn't exist already.
  this.createElement();
  if (!this.div_) return;

  // Calculate the DIV coordinates of two opposite corners of our bounds to
  // get the size and position of our Bar
  var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
  if (!pixPosition) return;

  // Now position our DIV based on the DIV coordinates of our bounds
  this.div_.style.width = this.width_ + "px";
  this.div_.style.left = (pixPosition.x + this.offsetHorizontal_) + "px";
  this.div_.style.height = this.height_ + "px";
  this.div_.style.top = (pixPosition.y + this.offsetVertical_) + "px";
  this.div_.style.display = 'block';
};

/* Creates the DIV representing this InfoBox in the floatPane.  If the panes
 * object, retrieved by calling getPanes, is null, remove the element from the
 * DOM.  If the div exists, but its parent is not the floatPane, move the div
 * to the new pane.
 * Called from within draw.  Alternatively, this can be called specifically on
 * a panes_changed event.
 */
InfoBox.prototype.createElement = function() {
  var panes = this.getPanes();
  
  // New [Start]
  // Event propagation
  var cancelHandler = function (e) {
    e.cancelBubble = true;
    if (e.stopPropagation) {
      e.stopPropagation();
    }
  };
  
  // This handler ignores the current event in the InfoBox and conditionally prevents
  // the event from being passed on to the map. It is used for the contextmenu event.
  var ignoreHandler = function (e) {
    e.returnValue = false;
    if (e.preventDefault) {
      e.preventDefault();
    }
    if (!me.enableEventPropagation_) {
      cancelHandler(e);
    }
  };
  // New [End]
  
  var div = this.div_;
  if (!div) {
    // This does not handle changing panes.  You can set the map to be null and
    // then reset the map to move the div.
    div = this.div_ = document.createElement("div");
    div.style.border = "0px none";
    div.style.position = "absolute";
    div.style.background = "url('http://www.caravanningamigos.co.uk/Widgets/images/InfoBoxContainer.png')";
    div.style.width = this.width_ + "px";
    div.style.height = this.height_ + "px";
    var contentDiv = document.createElement("div");
    contentDiv.className = "iwContentDiv";
    contentDiv.style.padding = "0 15px 10px 15px";
    contentDiv.innerHTML = this.content_;

    var topDiv = document.createElement("div");
    topDiv.style.textAlign = "right";
    var closeImg = document.createElement("img");
    closeImg.style.padding = "15px 16px 1px 0";
    closeImg.style.width = "14px";
    closeImg.style.height = "13px";
    closeImg.style.cursor = "pointer";
    closeImg.src = "http://www.caravanningamigos.co.uk/Widgets/images/closeBigger.gif";
    topDiv.appendChild(closeImg);

    function removeInfoBox(ib) {
      return function() {
        if (this.contextListener_) {
          google.maps.event.removeListener(this.contextListener_);
          this.contextListener_ = null;
        }
        ib.setMap(null);
      };
    }

    google.maps.event.addDomListener(closeImg, 'click', removeInfoBox(this));

    div.appendChild(topDiv);
    div.appendChild(contentDiv);
    div.style.display = 'none';
    panes.floatPane.appendChild(div);
    this.panMap();

    // New [Start]
    if (!this.enableEventPropagation_) {
      // Cancel event propagation.
      this.eventListener1_ = google.maps.event.addDomListener(this.div_, "mousedown", cancelHandler);
      this.eventListener2_ = google.maps.event.addDomListener(this.div_, "click", cancelHandler);
      this.eventListener3_ = google.maps.event.addDomListener(this.div_, "dblclick", cancelHandler);
      this.eventListener4_ = google.maps.event.addDomListener(this.div_, "mouseover", function (e) {
        this.style.cursor = "default";
      });
    }
    this.contextListener_ = google.maps.event.addDomListener(this.div_, "contextmenu", ignoreHandler);   
    // New [End]
    
  } else if (div.parentNode != panes.floatPane) {
    // The panes have changed.  Move the div.
    div.parentNode.removeChild(div);
    panes.floatPane.appendChild(div);
  } else {
    // The panes have not changed, so no need to create or move the div.
  }
}

/* Pan the map to fit the InfoBox.
 */
InfoBox.prototype.panMap = function() {
  // if we go beyond map, pan map
  var map = this.map_;
  var bounds = map.getBounds();
  if (!bounds) return;

  // The position of the infowindow
  var position = this.latlng_;

  // The dimension of the infowindow
  var iwWidth = this.width_;
  var iwHeight = this.height_;

  // The offset position of the infowindow
  var iwOffsetX = this.offsetHorizontal_;
  var iwOffsetY = this.offsetVertical_;

  // Padding on the infowindow
  var padX = 0;
  var padY = 0;

  // The degrees per pixel
  var mapDiv = map.getDiv();
  var mapWidth = mapDiv.offsetWidth;
  var mapHeight = mapDiv.offsetHeight;
  var boundsSpan = bounds.toSpan();
  var longSpan = boundsSpan.lng();
  var latSpan = boundsSpan.lat();
  var degPixelX = longSpan / mapWidth;
  var degPixelY = latSpan / mapHeight;

  // The bounds of the map
  var mapWestLng = bounds.getSouthWest().lng();
  var mapEastLng = bounds.getNorthEast().lng();
  var mapNorthLat = bounds.getNorthEast().lat();
  var mapSouthLat = bounds.getSouthWest().lat();

  // The bounds of the infowindow
  var iwWestLng = position.lng() + (iwOffsetX - padX) * degPixelX;
  var iwEastLng = position.lng() + (iwOffsetX + iwWidth + padX) * degPixelX;
  var iwNorthLat = position.lat() - (iwOffsetY - padY) * degPixelY;
  var iwSouthLat = position.lat() - (iwOffsetY + iwHeight + padY) * degPixelY;

  // calculate center shift
  var shiftLng =
      (iwWestLng < mapWestLng ? mapWestLng - iwWestLng : 0) +
      (iwEastLng > mapEastLng ? mapEastLng - iwEastLng : 0);
  var shiftLat =
      (iwNorthLat > mapNorthLat ? mapNorthLat - iwNorthLat : 0) +
      (iwSouthLat < mapSouthLat ? mapSouthLat - iwSouthLat : 0);

  // The center of the map
  var center = map.getCenter();

  // The new map center
  var centerX = center.lng() - shiftLng;
  var centerY = center.lat() - shiftLat;

  // center the map to the new shifted center
  map.setCenter(new google.maps.LatLng(centerY, centerX));

  // Remove the listener after panning is complete.
  google.maps.event.removeListener(this.boundsChangedListener_);
  this.boundsChangedListener_ = null;
};

// JSON minified (http://www.JSON.org/json2.js)
if(!this.JSON)this.JSON={};
(function(){function k(b){return b<10?"0"+b:b}function o(b){p.lastIndex=0;return p.test(b)?'"'+b.replace(p,function(b){var c=r[b];return typeof c==="string"?c:"\\u"+("0000"+b.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+b+'"'}function l(b,i){var c,d,h,m,g=e,f,a=i[b];a&&typeof a==="object"&&typeof a.toJSON==="function"&&(a=a.toJSON(b));typeof j==="function"&&(a=j.call(i,b,a));switch(typeof a){case "string":return o(a);case "number":return isFinite(a)?String(a):"null";case "boolean":case "null":return String(a);
case "object":if(!a)return"null";e+=n;f=[];if(Object.prototype.toString.apply(a)==="[object Array]"){m=a.length;for(c=0;c<m;c+=1)f[c]=l(c,a)||"null";h=f.length===0?"[]":e?"[\n"+e+f.join(",\n"+e)+"\n"+g+"]":"["+f.join(",")+"]";e=g;return h}if(j&&typeof j==="object"){m=j.length;for(c=0;c<m;c+=1)d=j[c],typeof d==="string"&&(h=l(d,a))&&f.push(o(d)+(e?": ":":")+h)}else for(d in a)Object.hasOwnProperty.call(a,d)&&(h=l(d,a))&&f.push(o(d)+(e?": ":":")+h);h=f.length===0?"{}":e?"{\n"+e+f.join(",\n"+e)+"\n"+
g+"}":"{"+f.join(",")+"}";e=g;return h}}if(typeof Date.prototype.toJSON!=="function")Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+k(this.getUTCMonth()+1)+"-"+k(this.getUTCDate())+"T"+k(this.getUTCHours())+":"+k(this.getUTCMinutes())+":"+k(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()};var q=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,e,n,r={"\u0008":"\\b","\t":"\\t","\n":"\\n","\u000c":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},j;if(typeof JSON.stringify!=="function")JSON.stringify=function(b,i,c){var d;n=e="";if(typeof c==="number")for(d=0;d<c;d+=1)n+=" ";else typeof c==="string"&&(n=c);if((j=i)&&typeof i!=="function"&&(typeof i!=="object"||typeof i.length!=="number"))throw Error("JSON.stringify");return l("",
{"":b})};if(typeof JSON.parse!=="function")JSON.parse=function(b,e){function c(b,d){var g,f,a=b[d];if(a&&typeof a==="object")for(g in a)Object.hasOwnProperty.call(a,g)&&(f=c(a,g),f!==void 0?a[g]=f:delete a[g]);return e.call(b,d,a)}var d;q.lastIndex=0;q.test(b)&&(b=b.replace(q,function(b){return"\\u"+("0000"+b.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(b.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return d=eval("("+b+")"),typeof e==="function"?c({"":d},""):d;throw new SyntaxError("JSON.parse");}})();
