// ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// 
// MapSet code by Dell Sala
// Based on tooltips code created by Travis Beckham
// http://www.squidfingers.com | http://www.podlob.com
// If want to use this code, feel free to do so, but please leave this message intact.
//
// ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||




/**
 * MapSet Class
 *
 * --------------------------------------------------------------
 *
 * EXAMPLE USE:
 *
 * map = new MapSet('mymap');   // INSTANTIATE A MAP WITH THE MAP'S DIV ID
 * map.setPoints([               // SET THE MAP POINTS
 *
 *    {
 *       name : 'Point Name',    // POINT'S NAME
 *       category : 'Category',  // POINT'S CATEGORY (OPTIONAL)
 *       x : 100,                // X COORDINATE ON MAP
 *       Y : 200,                // Y COORDINATE ON MAP
 *       notes : [               // NOTES (AS AN ARRAY OF LINES)
 *          'Note Line 1',
 *          'Note Line 2
 *       ]
 *    },
 *    ...
 *
 * ]);
 *
 * map.setShowMapText(true);     // OPTIONALLY USE THIS METHOD TO SHOW 
 *                               // OR HIDE TEXT COLUMNS ('ON' BY DEFAULT)
 *
 * MapSet.autoLoad();            // PREPARES MAPS FOR AUTO-INITIALIZATION AFTER THE PAGE LOADS
 *
 * --------------------------------------------------------------
 * 
 * EXAMPLE BASE MARKUP:
 *
 * <div id="mymap"></div>
 * <table id="mymap_text">
 *    <tr> <td></td> <td></td> <td></td> </tr>
 * </table>
 *
 * Note: The MapText component will attempt to populate a table 
 *       whos id is the map id with '_text' appended.
 *
 * --------------------------------------------------------------
 *
 * AUTO-GENERATED MARKUP
 *
 * === Map Points ===
 * <a href="#" class="mymap_point">1</a>
 *
 * === Map Message ===
 * <div id="mymap_message">
 *    <h3>Location Title</h3>
 *    <p>
 *       Line 1<b/>
 *       Line 2
 *    </p>
 * </div>
 *
 * === Map Text Blocks ===
 * <div class="mymap_textblock category">
 *    <h3><strong>1)</strong> Location Title</h3>
 *    <p>
 *       Line 1<b/>
 *       Line 2
 *    </p>
 * </div>
 *
 */
var MapSet = function (mapId) {
   this.mapId = mapId;
   MapSet.mapList[MapSet.mapIndex] = this;
   this.mapIndex = MapSet.mapIndex;
   this.messageBox = new MapMessage(this.mapId + '_message');
   this.showMapText = true;
   this.showMapKey = true;
   MapSet.mapIndex++;
}


/**
 * Static MapSet Id Counter
 */
MapSet.mapIndex = 0;

/**
 * Array of all instantiated map sets
 */
MapSet.mapList = []; 

/**
 * Flag to indicate if any MapSets have been initialized
 */
MapSet.initialized = false;

/**
 * Turns debugging on for the class 
 */
MapSet.debug = false;

MapSet.prototype = {

   setPoints : function (points) {
      this.points = points;
      for (var i=0; i < this.points.length; i++) {
         this.points[i].label = i+1;
         this.points[i].map = this;
      }
   },


   setShowMapText : function (value) {
      this.showMapText = value;
   },


   setShowMapKey : function (value) {
      this.showMapKey = value;
   },


   init : function () {
      if (!document.getElementById) return;
      this.initMessageBox();
      document.body.appendChild(this.messageBox.div);
      var map = document.getElementById(this.mapId);
      map.style.position = "relative";
      var targetMapSet = this;
      for (var i=0; i < this.points.length; i++) {
         var a = this.buildPointLink(i);
         map.appendChild(a);
      }
      if (MapSet.debug) {
         map.onmouseover = function () {
            targetMapSet.messageBox.show();
         };
         map.onmouseout = function () {
            targetMapSet.messageBox.hide();
         };
      }
      if (this.showMapText) {
         var mapText = new MapText(this.mapId + '_text');
         mapText.setPoints(this.points);
         mapText.init();
      }
      var mapKey;
      if (this.showMapKey
         && (mapKey = document.getElementById(this.mapId+'_key')))
      {
         var categoriesLookpup = {};
         var categories = [];
         for (var i=0; i < this.points.length; i++) {
            if (! this.points[i].category) { continue; }
            if (! categoriesLookpup[this.points[i].category]) {
               categoriesLookpup[this.points[i].category] = true;
               categories.push(this.points[i].category);
            }
         }
         if (categories.length) {
            var mapKeyText = '<ul>';
            for (var i=0; i < categories.length; i++) {
               mapKeyText += '<li class="'+categories[i]
                  +'"><strong>&bull;</strong> '+categories[i]+'</li>'
            }
            mapKey.innerHTML = mapKeyText;
         }
      }
   },


   buildPointLink : function (pointIndex) {
      var point = this.points[pointIndex];
      var a = document.createElement("A");
      var targetMapSet = this;
      a.href = '#';
      a.onclick = function () { return false; };
      a.innerHTML = point.label;
      a.mapIndex = this.mapIndex;
      if (! MapSet.debug) {
         a.onmouseover = function () {
            var point = targetMapSet.points[pointIndex];
            targetMapSet.messageBox.show(point);
         };
         a.onmouseout = function () { targetMapSet.messageBox.hide() };
      }
      a.className = this.mapId+"_point";
      if (point.category) {
         a.className += ' '+point.category;
      }
      a.style.position = "absolute";
      a.style.display = "block";
      a.style.left = point.x + "px";
      a.style.top = point.y + "px";
      return a;
   },
   

   initMessageBox : function () {   
      if (! MapSet.initialized && this.messageBox.div) {
         if (MapSet.debug) {
            document.onmousemove = function (evt) {
               pos = MapSet.getMapPosition(evt);
               for (var i=0; i<MapSet.mapList.length; i++) {
                  MapSet.mapList[i].messageBox.move(evt);
                  MapSet.mapList[i].messageBox.debug(pos.x+', '+pos.y);
               }
            };
         } else {
            document.onmousemove = function (evt) {
               for (var i=0; i<MapSet.mapList.length; i++) {
                  MapSet.mapList[i].messageBox.move(evt);
               }
            };
         }
         MapSet.initialized = true;
      }
   }

}


MapSet.autoLoad = function () {
   var loadFunc = function () {
      for (var i=0; i < MapSet.mapList.length; i++) {
         MapSet.mapList[i].init();
      }
   }
   if (typeof window.addEventListener != 'undefined') {
      window.addEventListener('load', loadFunc, false);
   } else if (typeof window.attachEvent != 'undefined') {
      window.attachEvent('onload', loadFunc);
   }
}


MapSet.getCursorPosition = function (evt) {
   var x=0, y=0;
   if (document.all) {// Explorer
      x = (document.documentElement && document.documentElement.scrollLeft) ? document.documentElement.scrollLeft : document.body.scrollLeft;
      y = (document.documentElement && document.documentElement.scrollTop) ? document.documentElement.scrollTop : document.body.scrollTop;
      x += window.event.clientX;
      y += window.event.clientY;
   } else {// Mozilla
      x = evt.pageX;
      y = evt.pageY;
   }
   return { x : x, y : y };
}


MapSet.getMapPosition = function (evt) {
   var offsetX;
   var offsetY;
   var el;
   if (window.event) {
      evt = window.event;
      el = evt.srcElement;
   } else {
      el = evt.target;
   }
   if (el.nodeName.toLowerCase() == 'a') {
      el = el.parentNode;
   }
   var coords = { x: 0, y: 0 };
   do {
      coords.x += el.offsetLeft;
      coords.y += el.offsetTop;
   } while (el = el.offsetParent);
   offsetX = evt.clientX - coords.x;
   offsetY = evt.clientY - coords.y;
   var doc = document.documentElement ? document.documentElement : document.body;
   if (doc.scrollTop || doc.scrollLeft ) {
      offsetX += doc.scrollLeft;
      offsetY += doc.scrollTop;
   }
   return { x : offsetX, y : offsetY };
}



/**
 * MapMessage Class
 *
 * This class is used internally by the MapSet class.
 */
var MapMessage = function (mapMessageId) {
   this.id = mapMessageId;
	this.offsetX = 20;
	this.offsetY = -10;
	this.div = document.createElement ("div");
	this.div.setAttribute ("id", this.id);
	this.div.style.position = "absolute";
	this.div.style.left = "0";
	this.div.style.top = "0";
	this.div.style.zIndex = "1000";
	this.div.style.display = "none";
}

MapMessage.prototype = {

   move : function (evt) {
      var pos = MapSet.getCursorPosition (evt);
      this.div.style.left = (pos.x + this.offsetX) + "px";
      this.div.style.top = (pos.y + this.offsetY) + "px";
   },

   show : function (point) {
      if (!this.div) return;
      var text;
      if (point) {
         text = '<h3>' + point.name + '</h3>';
         text += '<p>' + point.notes.join('<br/>') + '</p>';
      } else {
         text = '';
      }
      this.div.innerHTML = text;
      // Without the next line, Explorer5/Mac has a redraw problem.
      this.div.style.visibility = "visible";
      this.div.style.display = "block";
   },

   hide : function () {
      if (!this.div) return;
      // Without the next line, Explorer5/Mac has a redraw problem.
      this.div.style.visibility = "hidden";
      this.div.style.display = "none";
      this.div.innerHTML = "";
   },

   debug : function (message) {
      if (!this.div) return;
      this.div.innerHTML = message;
   }

};



/**
 * MapText Class
 *
 * This class is used internally by the MapSet class.
 */
var MapText = function (mapTextId) {
   this.table = document.getElementById(mapTextId);
   this.columns = this.table.getElementsByTagName('td');
}


MapText.prototype = {

   setPoints: function (points) {
      this.points = points;
   },

   init: function () {

      var columnCount = this.columns.length;
      var itemCount = this.points.length;
      var itemsPerCol = Math.ceil(itemCount / columnCount);
      
      var currentPointIndex = 0;
   
      for (var i=0; i < columnCount; i++) {
         var columnContent = '';
         for (var j=0; j < itemsPerCol && currentPointIndex < itemCount; j++) {
            columnContent += MapText.createListing(this.points[currentPointIndex]);
            currentPointIndex++;
         }
         this.columns[i].innerHTML = columnContent;
      }
   
   }

};


MapText.createListing = function (point) {
   var className = point.map.mapId+'_textblock';
   if (point.category) {
      className += ' '+point.category;
   }
   var text = '<div class="'+className+'">';
   text += '<h3><strong>' + point.label + ')</strong> ' + point.name + '</h3>';
   text += '<p>' + point.notes.join('<br/>') + '</p></div>';
   return text;
}
