<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
    <title>KML parser</title>
    <style>
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
      #map {
        height: 100%;
      }
    </style>
    <script src="KmlMapParser.js"></script>
    <script src="map.js"></script>
  </head>
  <body>
    <div id="map"></div>
    <script async defer
        src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script>
  </body>
</html>
/* Styles go here */

/*****************************************************************************
*  KmlMapParser.js
*  KmlMapParser  - A Google Maps API Extension
*  Project: http://code.google.com/p/kmlmapparser/
*  Author:  R. Raponi 
*  Version: 1.0
*
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
* -----------------------------------------------------------------------------------
*
* @param {Object} config Configuration options
*
* var grid = new KmlMapParser({ map: map,
*                               showSidebar: true,
*                               sidebarId: 'sideBar'
*                              }
*
*  @param {Object} config Configuration options
*  --------------------------
*    allFoldersOpen: true
*    zoomLevel: 15
*    zoomOnClick: true
*    imageDirectory: 'http://localhost:8090/envistaExamples/resources/images'
*    imageSize: {width: 32, height:32}
*    imageHotspot: {x: 0, y:32}
*    showImageShadow: true
*    showSidebar: true
*    sidebarId: 'sideBar'
*    showSidebarDescriptions: true
*    showOverlaysInSidebar: true
*    showRootName: true
*    showBubble: false
*    showSidebarBubble: true
*    highlightColor: 'ff0000' 
*    showFolders: false
*    showMultiPointsAsMarkers: true
*    useMapCenter: true
*    afterParseFn: myCallbackFunction
*    kml: ['../kmlFiles/sidebarMap.kml', '../kmlFiles/state_capitals.kml']
*    kmlFile: '../kmlFiles/sidebarMap.kml'  < deprecated >
*    showDragZoomButton: false  [REQUIRES keydragzoom.js or keydragzoom_packed.js see <a href="http://google-maps-utility-library-v3.googlecode.com">http://google-maps-utility-library-v3.googlecode.com</a>]
*    dragZoomButtonImage: 'http://localhost:8090/envistaExamples/resources/images/dragzoom_btn.png';
*
*
* sidebar CSS can be found in KmlMapParser.css
******************************************************************************/

// Extend the global String object with a method to remove leading and trailing whitespace
if (!String.prototype.trim) {
  String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, '');
  };
}


KmlMapParser  = function(config){

  //check and set config variables
  if (config === undefined){
    throw 'KmlMapParser internal error: needs Configuration options especially the map!';
  }

  this.origConfig = config;
  this.config = config;

  this.kmlFile = config.kml ? config.kml : config.kmlFile;

  //TODO make map if not defined
  this.map = config.map;

  if (config.imageDirectory !== undefined){
    this.imageDirectory = config.imageDirectory;
  }

  this.sidebarId = config.sidebarId ? this.config.sidebarId : 'the_side_bar';
  this.showMarkers = (config.showMarkers!==undefined) ? this.config.showMarkers : true;
  this.showSidebar = (config.showSidebar!==undefined) ? this.config.showSidebar : true;
  this.showSidebarDescriptions = (config.showSidebarDescriptions!==undefined) ? this.config.showSidebarDescriptions : true;
  
  this.showBubble = (config.showBubble!==undefined) ? this.config.showBubble : true;
  if (this.showBubble){
	  this.showSidebarBubble = (config.showSidebarBubble!==undefined) ? this.config.showSidebarBubble : false;
  }else{
	  this.showSidebarBubble = false;
  }
  
  this.showFolders = (config.showFolders!==undefined) ? this.config.showFolders : true;
  if (this.showFolders){
	  this.allFoldersOpen = (config.allFoldersOpen!==undefined) ? this.config.allFoldersOpen : undefined;
  }
  this.defaultImageSize = (config.imageSize!== undefined) ? this.config.imageSize : {width: 32, height:32};
  this.defaultHotspot = (config.imageHotspot!== undefined) ? this.config.imageHotspot : {x: 16, y:32};
  this.showImageShadow = (config.showImageShadow !== undefined) ? this.config.showImageShadow : true;
  
  this.showMultiPointsAsMarkers = (config.showMultiPointsAsMarkers!==undefined) ? this.config.showMultiPointsAsMarkers : false;
  this.showRootName = (config.showRootName!==undefined) ? this.config.showRootName : true;
  
  this.showDragZoomButton = (config.showDragZoomButton!==undefined) ? this.config.showDragZoomButton : false;
  if (this.showDragZoomButton){
	  this.dragZoomButtonImage = (config.dragZoomButtonImage!==undefined) ? this.config.dragZoomButtonImage : 'http://maps.gstatic.com/mapfiles/ftr/controls/dragzoom_btn.png';
  }
  
  this.useMapCenter = (config.useMapCenter!==undefined) ? this.config.useMapCenter : false;
  if (this.useMapCenter){
      this.mapCenter = this.map.getCenter();
  }
  
  this.defaultZoomLevel = (config.zoomLevel!==undefined) ? this.config.zoomLevel : 15;
  this.shouldZoom = (config.zoomOnClick!==undefined) ? this.config.zoomOnClick : true;

  if (config.highlightColor === undefined){
    this.highlightColor = '#aaffff';
  } else {
    this.highlightColor = config.highlightColor;
  }
    
  this.showOverlaysInSidebar = (config.showOverlaysInSidebar!==undefined) ? this.config.showOverlaysInSidebar : true;
  
  if (config.afterParseFn === undefined){
    this.hasAfterParse = false;
  } else {
    this.hasAfterParse = true;
    this.afterParseFn = config.afterParseFn;
  }

  //create the info window
  this.infoWindow = new google.maps.InfoWindow();
  this.mapshapes  = [];
  this.folderIndex  = 0;
  this.placemarkIndex  = 0;
  this.overlayIndex = 0;
  this.zindex=100;
  
  if (this.showDragZoomButton){
	  this.enableDragZoom();
  }
  if (this.kmlFile !== undefined){
    this.parse(this.kmlFile);
  }
};

KmlMapParser.prototype = {

   /* 
   * Process one or more KML documents
   */
   parse :  function (urls) {
      if (typeof urls === 'string') {
        urls = [urls];
      }
      this.docSet = { docs: [], remaining: urls.length};

      for (var i = 0; i < urls.length; i++) {
    	  this.parseUrl(urls[i], i);
      }
  },


 /*
  *  parse and process one KML document
  */
  parseUrl : function (url, num){
      var baseUrl = url.split('?')[0],
      	  doc = { baseUrl: baseUrl, url: url, id: num };

      this.docSet.docs.push(doc);

      var that = this,
      	  render = this.render,
      	  callback = function (responseXML) {render(responseXML, doc, that);};

      this.fetchXMLdoc(url, callback);
  },

  
  /*
   * Get the document associated with the id
   * @param {Number or String} docId the document id is the index number of the KML url
   * @return {Object} document
   */
  getDocumentById : function (docId) { 
	  var iDocId = (typeof docId === 'string') ? parseInt(docId, 10) : docId;
	  return this.docSet.docs[docId];
  },

  
  
  /*
   *  Show the map objects associated with a document
   *  by the documents id 
   * @param {String} docId
   */
   hideDocumentById : function (docId) { 
       var doc = this.getDocumentById(docId);   
       if (doc && doc.isVisible){ 
           this.setVisibilityByDoc(doc, false);  
       }   
   },
   
   
   /*
   *  Show the map objects associated with a document
   *  by the documents id 
   * @param {String} docId
   */
   showDocumentById : function (docId){
 	  var doc = this.getDocumentById(docId);
       if (doc && !doc.isVisible ){ 
           this.setVisibilityByDoc(doc, true);  
       }   
   },
   
   
   /*
   *  Sets visibility on all map objects and the sidebar
   * @param {Boolean} isVisible 
   */
   setVisibility : function (isVisible){               
       this.setVisibilityOnMarkers(isVisible);      
       this.setOverlayVisibility(isVisible);       
       
       if (this.showSidebar && !!this.sidebarId){
           var side = document.getElementById(this.sidebarId);
           if (side !== null){
               this.setVisibilitybyNode(side, isVisible);
           }
       }                       
   },
   
   
   /*
   *  Sets visibility on all map markers and shapes 
   * @param {Boolean} isVisible
   * @param {Number}  docId  (optional)
   */
   setVisibilityOnMarkers : function (isVisible, docId){
       var ms = this.mapshapes,
           mss, shapeId; 

       for ( shapeId in ms){
          if (ms.hasOwnProperty(shapeId)) {
             mss = ms[shapeId];
             if ((docId !== undefined && mss.docId === docId ) || (docId === undefined)){
                 this.setMapObjectVisibility(mss, isVisible);
             }
          }
       }
   },
   
   
   /*
   *  sets the visiblility on map objects 
   *  by the documents id
   * @param {Number} docId
   * @param {boolean} isVisible 
   */
   setVisibilityByDoc : function (doc, isVisible) {
       var docRoot = document.getElementById('doc-'+ doc.id),
           i, overlayId, overlay;
       
       if (doc !== undefined && doc.isVisible !== isVisible){ 
          //markers and shapes     
          this.setVisibilityOnMarkers(isVisible, doc.id);
         
          //overlays
          if (doc.hasOverlays){  
             for (i = 0; i < doc.overlays.length; i++){
                this.setOverlayVisibility(doc.overlays[i].overlay, isVisible);
             }
          }
           
          //folders    
          if (this.showSidebar && docRoot !== null){
             this.setVisibilitybyNode(docRoot, isVisible);
          }          
          doc.isVisible = isVisible;
       }
   },
  
  
  /*
  * set Overlay visibility on a marker or shape
  * actually removes it form the map
  * @param {Object} Overly
  * @param {Boolean} isVisible
  */
  setMapObjectVisibility: function (shape, isVisible) {
      if ( typeof shape === 'object'){    
          //shapes
          if (isVisible){
            shape.setMap(this.map);
          }else{
            shape.setMap(null);
          }
      }     
  },
  
  
  /*
   *  sets the overlay visibility by the documents id
   * @param {Number} docId
   * @param {Boolean} visibility 
   */
   setOverlayVisibilityByDocId : function (docId, isVisible){      
       var doc = this.getDocumentById[docId];
       
       if (doc !== undefined && doc.hasOverlays){    
           for (i = 0; i < doc.overlays.length; i++){
              this.setVisibilitybyOverlay(doc.overlays[i].overlay, isVisible);
           }
       }
   },
  
   
   /*
    *  sets the overlay visibility by the overlays id
    * @param {Number} overlayId
    * @param {Boolean} visibility 
    */
    setOverlayVisibilityById:  function (id, isVisible){	  
	  var docs = this.docSet.docs,
	      doc, overlay, i, j;
	  
	  if (docs.length > 0){
		  for (i = 0; i < docs.length; i++){
			  doc = docs[i]; 
			  if (doc !== undefined && doc.hasOverlays){    
				  for (j = 0; j < doc.overlays.length; j++){
					  overlay = doc.overlays[j].overlay;
					  if (id && id === overlay.id_){
						  this.setVisibilitybyOverlay(overlay, isVisible);
					  }
				  }
			  }
		  }
	  }
  },
  
  
  /*
  * set Overlay visibility
  * @param {Object} Overly
  * @param {Boolean} isVisible
  */
  setVisibilitybyOverlay: function (overlay, isVisible) {
      if (overlay !== undefined && overlay !== null){
    	  overlay.setVisibility(isVisible);
      } 
  },
  
  
  /*
  *  sets all the visibility on all overlays
  * @param {Boolean} isVisible
  */
  setOverlayVisibility : function (isVisible) {
	  var docs = this.docSet.docs,
          doc, overlay, i, j;
  
	  if (docs.length > 0){
		  for (i = 0; i < docs.length; i++){
			  doc = docs[i]; 
			  if (doc !== undefined && doc.hasOverlays){    
				  for (j = 0; j < doc.overlays.length; j++){
					  overlay = doc.overlays[j].overlay;
					  this.setVisibilitybyOverlay(overlay, isVisible);
				  }
			  }
		  }
	  }
  },
  
  
  /*
  *  set Overlay opacity
  * @param {Number} opacity 0.0 - 1.0.
  */
  setOverlayOpacity: function (opacity) {
      var docs = this.docSet.docs,
       	  doc, overlay, i, j;

      if (docs.length > 0){
    	  for (i = 0; i < docs.length; i++){
    		  doc = docs[i]; 
    		  if (doc !== undefined && doc.hasOverlays){    
    			  for (j = 0; j < doc.overlays.length; j++){
    				  overlay = doc.overlays[j].overlay;
    				  this.setOpacityByOverlay(overlay, opacity);
    			  }
    		  }
    	  }
      }
  }, 
  
  
  /*
  *  sets the overlay opacity by the documents id
  * @param {Number} docId
  * @param {Number} opacity 0.0 - 1.0
  */
  setOverlayOpacityByDocId : function (docId, opacity) {
	  var doc = this.getDocumentById[docId];
      
      if (doc !== undefined && doc.hasOverlays){    
          for (i = 0; i < doc.overlays.length; i++){
             this.setOverlayOpacity(doc.overlays[i], opacity);
          }
      }
  },
  

  /*
  *  set Overlay opacity
  * @param {Object} Overly.
  * @param {Number} opacity 0.0 - 1.0
  */
  setOpacityByOverlay: function (overlay, opacity) {
      this.setVisibilitybyOverlay(overlay, true);
      overlay.setOpacity(opacity);
  },
  
  
  /*
  *  clears Map of all object displayed on it and
  *  removes contents the side bar and destroys all references to the KML
  *  there is no recovery from this action except to re-parse the KML
  */
  clearMap : function (){
	  if (this.docSet && this.docSet.docs){
	      var docs = this.docSet.docs,
	          ms = this.mapshapes,
	          doc, i, j, mss, shapeId, overlay, side;

	      if (docs.length > 0){
	    	  for (i = 0; i < docs.length; i++){
	    		  doc = docs[i];
	    		  if (doc !== undefined && doc.hasOverlays){    
	    			  for (j = 0; j < doc.overlays.length; j++){
	    				  overlay = doc.overlays[j].overlay;
	    				  overlay.setMap(null);
	    			  }
	    		  }
	    	  }
	      }
	      
	      this.setVisibilityOnMarkers(false);
	      
	      if (this.showSidebar && !!this.sidebarId){
	        side = document.getElementById(this.sidebarId);
	        if (side !== null){
	            side.innerHTML='';
	        }
	      }
	      //reset 
	      this.mapshapes  = [];
	      this.folderIndex  = 0;
	      this.placemarkIndex  = 0;
	      this.zindex=100;
	      this.kmlFile=undefined;
	      this.docSet=undefined;
	  }
  },
  
  
  /*
  *  Filter Sidebar 
  * @param {String} text string.
  */
  filterSidebar: function (text) {
    if (text === undefined || text === null ){
      text='';
    }
    var t = text.toLowerCase().trim(),
        isVisible, side;
          
      if (this.showSidebar && !!this.sidebarId){
          side = document.getElementById(this.sidebarId);
          if (side !== null){
            var nodes=side.getElementsByTagName("li"),
                i, node, li;
                
            if (nodes !== null && nodes.length > 0){    
                for (i = 0; i < nodes.length; i++){
                   node = nodes[i];
                   if ( -1 !== node.className.search('placemark')) {
                 	   val = this.getNodeValue(node).toLowerCase();
                       if ( t !== undefined){
                           isVisible = (val.search(t) !== -1) ? true : false;
                       }
                       this.setVisibilitybyNode(node, isVisible);
                   }
                }  
             }
          }
      }   
  },
  
  
  
  /*
   * filter Markers on all map markers and shapes 
   * @param {Boolean} isVisible
   * @param {String}  text  
   */
   filterMarkers : function (text){
	   if (text === undefined || text === null ){
	       text='';
	     }
	   var t = text.toLowerCase().trim(),
	           isVisible,
	           ms = this.mapshapes,
	           mss, shapeId, it;
	   
       for ( shapeId in ms){
          if (ms.hasOwnProperty(shapeId)) {
             mss = ms[shapeId];
             if (t === undefined &&  t === ''){
                 this.setMapObjectVisibility(mss, true);
             }else{
            	 descrip = mss.description;
                 if ( descrip !== undefined){
                    it = descrip.toLowerCase();
                    isVisible = (it.search(t) !== -1) ? true : false;
                    this.setMapObjectVisibility(mss, isVisible);
                 }
             }
          }
       }
   },
   
  
  /*
   *  Filter Sidebar 
   * @param {String} text string.
   */
   filterMap: function (text) {
     if (text === undefined || text === null ){
       text='';
     }
     var t = text.toLowerCase().trim();    
     this.filterSidebar(t); 
     this.filterMarkers(t);
   },
   
  
  /**
   * Parses anXML string and returns the parsed document in a DOM data structure.
   * Returns an empty DOM node if XML parsing is not supported in this browser.
   * @param {string} str XML string.
   * @return {Element|Document} DOM.
   */
   xmlParse : function (str){
        if (typeof ActiveXObject !== 'undefined' && typeof GetObject !== 'undefined') {
            var doc = new ActiveXObject('Microsoft.XMLDOM');
            doc.loadXML(str);
            return doc;
        }
        if (typeof DOMParser !== 'undefined') {
        	return (new DOMParser()).parseFromString(str, 'text/xml');
        }
        return createElement('div', null);
  },
  
    
  fetchXMLdoc : function (url, callback){
      var callBack = callback, 
      	  xmlParse = this.xmlParse,
      	  req = false; 
            
      if (window.XMLHttpRequest && !(window.ActiveXObject)) {
    	  req = new window.XMLHttpRequest(); 
      } else if (window.ActiveXObject) {
    	  req = new window.ActiveXObject('Microsoft.XMLHTTP'); 
      }     
      
      if (req){
       	  req.open('GET', url, true);
    	  req.onreadystatechange = function (){
	          if (req.readyState === 4) {
		            if (!!this.xhrtimeout){
		               clearTimeout(this.xhrtimeout);
		            }
		            if (req.status === 200) {
		            	callBack(xmlParse(req.responseText));
			        } else {
			        	 //this.log('There was a problem retrieving the KML document for:  ' + url + ' ('+ req.statusText + ' : ' req.statusText+ '). ');
			        	callBack();
			        }
	          }
	       };
	       this.xhrtimeout = setTimeout( function(){callBack();}, 120000);
	       req.send('');
      } else {
    	   callBack(null);
      }
  },
  
    
  //private
  setVisibilitybyNode : function(node, isVisible) {
      if (isVisible){
          node.style.display='block';
      }else{
          node.style.display='none';
      }
  },


  getNodeValue : function(node) {     
      if (node === undefined || node === null) {
    	  return '';
      } else {
    	  return (node.innerText || node.text || node.textContent).trim();
      }
  },


  getBooleanValue : function(node) {
      var val = this.getNodeValue(node);

      switch(val.toLowerCase()){
      	case "true": case "yes": case "1": return true;
      	case "false": case "no": case "0": case null: return false;
      	default: return true;
      }
  },

  
  //private
  findStyle: function(node, styles, styleId){
	  var style;
	  
      if (styles[styleId] === undefined){
    	  styles[styleId] = { width: 4, fill: true, outline: true };
      }

      var styleNodes = node.getElementsByTagName('IconStyle');
      if (styleNodes && styleNodes.length > 0){  
    	  style = styleNodes[0];
    	  
    	  var hotspot = style.getElementsByTagName('hotSpot')[0],
    	  	  scale = this.getNodeValue(style.getElementsByTagName('scale')[0]),
    	  	  w =  parseInt(this.defaultImageSize.width, 10),
    	  	  h =  parseInt(this.defaultImageSize.height, 10),
    	  	  x =  parseInt(this.defaultHotspot.x, 10),
        	  y =  parseInt(this.defaultHotspot.y, 10); 

    	  // the default google maps icon is 32 x 32; default bottom center is 16,32;
    	  // hotspots are assumed to be pixels
    	  if (hotspot !== null && hotspot !== undefined){
    		  x =  parseInt(hotspot.getAttribute('x'), 10);
    		  y =  parseInt(hotspot.getAttribute('y'), 10);
    	  }
    	  if (!isNaN(scale)){	
    		  scale = 1.0;
    	  }
    	  
    	  styles[styleId] = {
    		 href: this.getNodeValue(style.getElementsByTagName('href')[0]),
    		 size: {width: w, height: h},
          	 scale: scale,
          	 anchor: {x: x, y: y}
          };
      }
      
      styleNodes = node.getElementsByTagName('LineStyle');
      if (styleNodes && styleNodes.length > 0){
    	  style = styleNodes[0];
    	  styles[styleId].color = this.getNodeValue(style.getElementsByTagName('color')[0]);
    	  styles[styleId].width = this.getNodeValue(style.getElementsByTagName('width')[0]);
      }
      
      styleNodes = node.getElementsByTagName('PolyStyle');
      if (styleNodes && styleNodes.length > 0){
    	  style = styleNodes[0];
    	  styles[styleId].color = this.getNodeValue(style.getElementsByTagName('color')[0]);
    	  styles[styleId].fill      = this.getBooleanValue(style.getElementsByTagName('fill')[0]);
    	  styles[styleId].outline   = this.getBooleanValue(style.getElementsByTagName('outline')[0]);
      }
    
      return styles[styleId];
  },


  convertStyles : function(styles){
      var styleId;
      for ( styleId in styles){
    	  if (styles.hasOwnProperty(styleId)){
    		  this.convertKmlStyle(styles[styleId]);
    	  }
      }
      return styles;
  },
  
  
  /*
   * Converts the KML style into a google maps marker image with a 32 x 32 default style
   * 
   * see http://www.mymapsplus.com/Markers for a visual marker reference
   */
  convertKmlStyle : function (style) {
      var zeroPoint = new google.maps.Point(0,0),
      	  size	= style.size,
       	  scale	= style.scale,
       	  anchor = style.anchor;
      
      if (style && style.href !== undefined && style.href !== '') {
        var anchorPoint = new google.maps.Point(anchor.x * scale, anchor.y * scale),
        	imageSize = new google.maps.Size(size.width * scale, size.height * scale),
        	href = style.href;
        
        if (this.imageDirectory !== undefined){
           var hrefs = href.split('/'),
           	   image;
           
           if ( hrefs.length > 0){
              image = hrefs[hrefs.length-1];
              style.href = this.imageDirectory + '/' + image;
              href = style.href;
           }
        }
    
        //now create marker image
        style.icon =  new google.maps.MarkerImage(href, imageSize, zeroPoint, anchorPoint, imageSize);
        
        if (this.showImageShadow){
	        // Look for a  shadow on a standard marker (same as the dot marker) or push-pin; 
	        var regEx = /\/(blue|red|green|ltblue|lightblue|yellow|purple|pink)\.png/,
        		shadowSize = new google.maps.Size(size.width * scale * 2, size.height * scale);
	        
	        if (regEx.test(href)) {
	            style.shadow = new google.maps.MarkerImage('http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png',
	        		  									  shadowSize,
	                                                      zeroPoint,
	                                                      anchorPoint);
	        } else if (href.indexOf('-pushpin.png') > -1) {      
	            style.shadow = new google.maps.MarkerImage('http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png',
	        		  									  shadowSize,
	                                                      zeroPoint,
	                                                      anchorPoint);
	        }
         }
      }
  },


  render : function (responseXML, doc, scope) {
      if (!responseXML) {
          // Error retrieving the data
      } else if (!doc) {
    	  throw 'KmlMapParser error: render called with null document';
      } else { //no errors
        var i, styles = {};
            doc.isRoot        = true;
            doc.folders       = [];
            doc.placemarks    = [];
            doc.overlays    = [];
            doc.isVisible = true;
        
        // get KML styles if any
        var styleId, pnNode, styleNode, styleNodeId,
            nodes = responseXML.getElementsByTagName('Style'),
            nodeCount = nodes.length;

        for (i = 0; i < nodeCount; i++) {
        	styleNode = nodes[i];
        	styleNodeId = styleNode.getAttribute('id');
          
        	if (styleNodeId !== null) {
        		styleId = '#' + styleNodeId;
        		scope.findStyle(styleNode, styles, styleId);
        	}
        }

        doc.styles = scope.convertStyles(styles);
        
        //get the file name
        var s = doc.baseUrl.lastIndexOf('/'),
            docFileName = doc.baseUrl;
        
        if (s > -1){
            docFileName = doc.baseUrl.slice(s+1); 
        }
        doc.fileName = docFileName;
        
        // get the doc name if it does not have one use the file name
        var docName = responseXML.getElementsByTagName('name')[0],
            docTag = docName.parentNode.tagName;
            
        if (docTag === 'Document' || docTag === 'document'){
            docName = scope.getNodeValue(docName);
        }else{
            var p = docFileName.lastIndexOf('.');
            docName = (p > -1) ? docFileName.substring(0,p) : docFileName;
        }
        doc.name = docName;

        // get folder names to show in the sidebar
        var folderNodes = responseXML.getElementsByTagName('Folder'), 
            placemarkNodes,
            parentFolder = doc;

        //no folders found only placemarks
        if (folderNodes === undefined || (folderNodes && folderNodes.length ===0)){
            doc.hasFolders = false;
            placemarkNodes = responseXML.getElementsByTagName('Placemark');

            if (placemarkNodes && placemarkNodes.length > 0){
                doc.hasPlacemarks = true;
                scope.findPlacemarks(doc, placemarkNodes, doc);
            }else{
            	doc.hasPlacemarks = false;
            }
            
            var groundNodes = responseXML.getElementsByTagName('GroundOverlay');
            if (groundNodes && groundNodes.length > 0){
                doc.hasOverlays = true;
                scope.findOverlays(doc, groundNodes, doc);
            } else {
                doc.hasOverlays = false;
            }
            
        } else {
            doc.hasFolders = true;
            doc.hasPlacemarks = false;
            doc.hasOverlays = false;
            scope.processChildren(doc, responseXML.documentElement);
        }
        scope.createMapObjects(doc);
        
        if (scope.showSidebar){
            scope.createSidebar(doc);
        }
        
        scope.docSet.remaining -= 1;
        if (scope.docSet.remaining === 0) {
            if (scope.useMapCenter){
              scope.map.setCenter(scope.mapCenter);
            } else {
              scope.map.fitBounds(scope.bounds);
            } 
            if (scope.hasAfterParse){
                scope.afterParseFn(doc);
            }         
        }
      }
   },
   
  
  /*
  * parses kml lookinging for folders and placemarks
  */ 
  processChildren : function(doc, node, parentFolder){
    var i, child, pf;
    for (i = 0; i < node.childNodes.length; i++){
        child = node.childNodes[i];
        if ( child.nodeType === 1){
            switch (child.tagName) {
              case 'Folder' :
                  pf = parentFolder!==undefined ? parentFolder : doc;
                  pf.hasFolders=true;
                  this.findFolders(doc, child, pf);
                  break;
              case 'Placemark' :
                  pf = parentFolder!==undefined ? parentFolder : doc;
                  pf.hasPlacemarks=true;
                  this.createPlacemark(doc, child, pf);
                  break;
              case 'GroundOverlay' :
                  pf = parentFolder!==undefined ? parentFolder : doc;
                  pf.hasOverlays=true;
                  this.findOverlays(doc, child, pf);
                  break;
              case 'Document' :
                  this.processChildren(doc, child);
                  break;
              case 'Kml' :
                  this.processChildren(doc, child);
                  break;
              default:
                  break;
            }
        }
     }
     return;
  },
  
  
 /*
  * finds and creates a folder
  */
  findFolders : function(doc, folderNode, parentFolder){
      var placemarkNodes, groundNodes,
      	  parentName = parentFolder.name,
          pnNode = folderNode.parentNode,
          pnNodeName = this.getNodeValue(pnNode.getElementsByTagName('name')[0]),
          folderName = this.getNodeValue(folderNode.getElementsByTagName('name')[0]),
          open = (this.allFoldersOpen !== undefined) ?  this.allFoldersOpen : this.getBooleanValue(folderNode.getElementsByTagName('open')[0]),
          folderId = doc.id + '-' + this.folderIndex++,
      	  folder = {  id: folderId,
                  name: folderName,
                  isOpen: open,
                  folders: [],
                  hasFolders : false,
                  placemarks: [],
                  hasPlacemarks: false,
                  hasOverlays: false
                },      
          subNodes = folderNode.getElementsByTagName('Folder'); 
      
      if (subNodes && subNodes.length > 0){
          folder.hasFolders = true;
          parentFolder.folders.push(folder);
          this.processChildren(doc, folderNode, folder);  
      } else {    
          //this is a leaf now look for placemarks
          placemarkNodes = folderNode.getElementsByTagName('Placemark');  
          if (placemarkNodes && placemarkNodes.length > 0){
              folder.hasPlacemarks = true;
              this.findPlacemarks(doc, placemarkNodes, folder, parentFolder);
          } else {
              folder.hasPlacemarks = false;
          }
          //look for overlays
          groundNodes = folderNode.getElementsByTagName('GroundOverlay');
          if (groundNodes && groundNodes.length > 0){
        	  folder.hasOverlays = true;
        	  this.findOverlays(doc, groundNodes, folder, parentFolder);
          } else {
        	  folder.hasOverlays = false;
          }
          parentFolder.folders.push(folder); 
      }
  },


 /*
  * parses leaf or root placemarks
  */
  findPlacemarks : function(doc, placemarkNodes, folder, parentFolder){
      var pm, node, nodeName;
      for (pm = 0; pm < placemarkNodes.length; pm++){
          node = placemarkNodes[pm];
          nodeName =  this.getNodeValue(node.getElementsByTagName('name')[0]);
          folder.hasPlacemarks = true;
          doc.hasPlacemarks = true;
          if (nodeName !== folder.name){
            this.createPlacemark(doc, node, folder);
          } else {
            folder.hasFolders = false;
            this.createPlacemark(doc, node, folder);
          }
       }
  },
  
  
  /*
  * create a Placemark
  */
  createPlacemark : function(doc, placemarkNode, folder){
      var style,
          docStyles = doc.styles,
          placemark = {	folderId: folder.id,
    		  		folderName: folder.name,
    		  		name:  this.getNodeValue(placemarkNode.getElementsByTagName('name')[0]),
    		  		description: this.getNodeValue(placemarkNode.getElementsByTagName('description')[0]),
    		  		styleUrl: this.getNodeValue(placemarkNode.getElementsByTagName('styleUrl')[0]),
    		  		shape: []
      				},
      	  inlineStyles = placemarkNode.getElementsByTagName('Style'),
     	  regex = /^https?:\/\//,
      	  multi = placemarkNode.getElementsByTagName('MultiGeometry');

      //add style to placemark
      placemark.style = doc.styles[placemark.styleUrl] || { width: 4, fill: true, outline: true };
      
      // inline style overrides shared style
      if (inlineStyles && (inlineStyles.length > 0)){
          style = this.findStyle(node,docStyles,'inline');
          this.convertKmlStyle(style);
          if (style){ placemark.style = style;}
      }
      
      //look for link in placemark
      if (regex.test(placemark.description)){
        placemark.description = ['<a href="', placemark.description, '">', placemark.description, '</a>'].join('');
      }
      
      // check if it is a MultiGeometry
      placemark.multi = (multi.length > 0) ? true : false;
            
      // process coordinates
      this.processPlacemarkCoords(placemark, placemarkNode);
      placemark.id=folder.id+'-'+this.placemarkIndex++;
      
      if (folder !== doc){
         doc.placemarks.push(placemark);
      }
      folder.placemarks.push(placemark);
  },



 /*
  * create Google Maps objects and shapes.
  *
  */
  createMapObjects : function(doc){
      if (!!google.maps) {
        var pm, i, j, placemark, shapes, shape, latlng, coords, coord, path, bounds, poly,
        	found = false,
            placemarks = doc.placemarks;

        //create bounds for the map
        this.bounds = this.bounds || new google.maps.LatLngBounds();
        
        //loop through the placemarks
        for (pm = 0; pm <placemarks.length; pm++){
            placemark = placemarks[pm];
            shapes = placemark.shape;
            //loop through the shapes lat/lng
            for (i = 0; i < shapes.length; i++){
                shape = shapes[i];
                if (shape.point){
                    coord = shape.point.coordinates[0];
                    latlng = new google.maps.LatLng(coord.lat, coord.lng);
                    this.bounds.extend(latlng);
                    placemark.points = placemark.points || [];
                    marker = this.createMarker(doc, placemark, latlng, i);
                    marker.active = true;
                } else if (shape.circle){
                    coord = shape.circle.coordinates[0];
                    latlng = new google.maps.LatLng(coord.lat, coord.lng);
                    this.bounds.extend(latlng);
                    placemark.circles = placemark.circles || [];
                    circle = this.createCircle(doc, placemark, latlng, i);
                    circle.active = true;
                }else if (shape.linestring || shape.polygon){
                	 coords = shape.linestring ? shape.linestring[0].coordinates : shape.polygon[0].coordinates;
                	 path = [];
                     bounds = new google.maps.LatLngBounds();
                     for (j = 0; j < coords.length; j++){
                         coord = coords[j];
                         latlng = new google.maps.LatLng(coord.lat, coord.lng);
                         path.push(latlng);
                         bounds.extend(latlng);
                     }
                     if (shape.linestring){
	                    placemark.lines = placemark.lines || [];
	                    poly = this.createPolyline(doc, placemark, path, bounds, i);
                     } else {  //shape.polygon
                    	 placemark.areas = placemark.areas || [];
                    	 poly = this.createPolygon(doc, placemark, path, bounds, i);
                     }
                     poly.active = true;
	                 this.bounds.union(bounds);
                }
            }
         }
      }
  },


  /*
  *  create a Marker on the map
  */
  createMarker : function(doc, placemark, latlng, shapeNumber){
      var content= '<div><h3>' + placemark.name + '</h3><div>' + placemark.description + '</div></div>',
      	  marker,
          markerOptions = {folderId:   placemark.folderId,
        		  map:      this.map,
        		  position: latlng,
        		  title:    placemark.name,
        		  zIndex:   this.zindex++,
        		  description: content,
        		  clickable: true
          		};
  
  	  if (placemark.style.icon !== undefined){
  		  markerOptions.icon = placemark.style.icon;
  	  } 
      if (placemark.style.shadow !== undefined){
    	  markerOptions.shadow = placemark.style.shadow;
      }
     
      marker = new google.maps.Marker(markerOptions);

      marker.id = placemark.id+'-'+shapeNumber;
      marker.docId = doc.id;
      
      if (this.showBubble){
    	  var that = this;
    	  google.maps.event.addListener(marker, 'click', function(){
    		  that.openBubble(marker);
    	  });
      }
    
      this.mapshapes[marker.id]=marker;
      placemark.marker=marker;
      placemark.points.push(marker);
      marker.setMap(this.map);

      return marker;
      
  },


  /*
  *  create a circle on the map
  */
  createCircle : function(doc, placemark, latlng, shapeNumber) {
      var content= '<div><h3>' + placemark.name + '</h3><div>' + placemark.description + '</div></div>',
         rgb = this.getRGBColorTransparency(placemark.style.color),
         circleOptions = { map:  this.map,
    	  			center: latlng,
    	  			radius: 20,
    	  			position: latlng,
    	  			strokeColor: rgb.color,
                    strokeWeight: placemark.style.width,
                    strokeOpacity: rgb.opacity,
                    fillColor: rgb.color,
                    fillOpacity: rgb.opacity,               
    	  			title:    placemark.name,
    	  			description: content,
    	  			folderId:   placemark.folderId,
    	  			zIndex:   this.zindex++
      				},
      	  circle = new google.maps.Circle(circleOptions);
      
      circle.id = placemark.id+'-'+shapeNumber;
      circle.docId = doc.id;
      
      if (this.showBubble){
    	  var that = this;
    	  google.maps.event.addListener(circle, 'click', function() {
    		  that.openBubble(circle);
    	  });
      }
      this.mapshapes[circle.id]=circle;
      placemark.circles.push(circle);
      placemark.mapObject = circle;
      circle.setMap(this.map);
      
      return circle;
  },


  /*
  *  create a polyline on the map
  */
  createPolyline : function(doc, placemark, path, bounds, shapeNumber) {
      var point = path[Math.floor(path.length/2)],
      	  content= '<div><h3>' + placemark.name + '</h3><div>' + placemark.description + '</div></div>',
      	  rgb = this.getRGBColorTransparency(placemark.style.color),      
          polyOptions = { map:  this.map,
    	  		path: path,
    	  		position: point,
    	  		strokeColor: rgb.color,
    	  		strokeWeight: placemark.style.width,
    	  		strokeOpacity: rgb.opacity,
    	  		title:    placemark.name,
    	  		description: content,
    	  		folderId:   placemark.folderId,
    	  		zIndex:   this.zindex++},
      	poly = new google.maps.Polyline(polyOptions);
      
      poly.bounds = bounds;
      poly.id = placemark.id+'-'+shapeNumber;
      poly.docId = doc.id;
      
      if (this.showBubble){
    	  var that = this;
    	  google.maps.event.addListener(poly, 'click', function() {
    		  that.openBubble(poly);
    	  });
      }
      
      this.mapshapes[poly.id] = poly;
      placemark.lines.push(poly);
      placemark.mapObject = poly;
      poly.setMap(this.map);
      
      return poly;
  },



 /**
  * create a polygon on the map
  *
  */
  createPolygon : function(doc, placemark, paths, bounds,shapeNumber) {
      var point = bounds.getCenter(),
          content= '<div><h3>' + placemark.name + '</h3><div>' + placemark.description + '</div></div>',
          rgb = this.getRGBColorTransparency(placemark.style.color);
 
      var polyOptions = { map:  this.map,
                          paths: paths,
                          title: placemark.name,
                          position: point,
                          strokeColor: rgb.color,
                          strokeWeight: placemark.style.width,
                          strokeOpacity: rgb.opacity,
                          fillColor: rgb.color,
                          fillOpacity: rgb.opacity,
                          description: content,
                          folderId:   placemark.folderId,
                          zIndex:   this.zindex++
                        };

        var poly = new google.maps.Polygon(polyOptions);
        
        poly.bounds = bounds;
        poly.id = placemark.id+'-'+shapeNumber;
        poly.docId = doc.id;
        
        if (this.showBubble){
      	  var that = this;
      	  google.maps.event.addListener(poly, 'click', function() {
      		  that.openBubble(poly);
      	  });
        }
        
        this.mapshapes[poly.id]= poly;
        placemark.areas.push(poly);
        placemark.mapObject = poly;
        poly.setMap(this.map);

        return poly;
  },

 /**
  * process the shape coordinates
  *  linearring is really a polygon
  */
  processShape : function(geometry, placemark, node){
      switch(geometry) {
          case "MultiGeometry":
            this.processPlacemarkCoords(placemark, node);
            break;
          case "LinearRing":
            placemark.shape.push({ polygon: this.getCoords(node,"LinearRing")});
            break;
          case "Polygon":
            placemark.shape.push({ polygon: this.getCoords(node,"LinearRing")});
            break;
          case "Point":
            placemark.shape.push({ point: this.getCoords(node, "Point")[0]});
            break;
          case "Circle":
            placemark.shape.push({ circle: this.getCoords(node, "Point")[0]});
            break;
          case "LineString":
            placemark.shape.push({ linestring: this.getCoords(node,"LineString")});
            break;
          default:
            break;
      }
      return placemark;
  },


  /*
  *
  * process Placemark Coordinates
  * multi geometries can be made up of multiple points, lines and polygons
  *
  */
  processPlacemarkCoords :  function (placemark, node){
       var shapeNodes = node.getElementsByTagName('Point'),
       	   i;

       if ( shapeNodes.length > 0){
          for (i=0; i< shapeNodes.length; i++ ){
               if (placemark.multi && !this.showMultiPointsAsMarkers){
            	   this.processShape('Circle', placemark, shapeNodes[i]);
                }else{
                   this.processShape('Point', placemark, shapeNodes[i]);
                }
           }
       }
       
       this.processShapeNodes(placemark, node, 'LineString');
       this.processShapeNodes(placemark, node, 'LinearRing');
       this.processShapeNodes(placemark, node, 'Polygon');

       return placemark;
  },
  
  
  /*
   * process the shape node for a given KML shape
   * except for points
   */
   processShapeNodes : function (placemark, node, shape) {
	  var shapeNodes = node.getElementsByTagName(shape);
      if ( shapeNodes.length > 0){
          for (i=0; i< shapeNodes.length; i++ ){
             this.processShape(shape, placemark, shapeNodes[i]);
          }
      }
  },
  

  /*
  * get coordinates for lat/longs and return them in an array (currently, altitude is not being used).
  */
  getCoords : function (node, tag) {
      var coordListA = [], i, j, coords, path, pathLength, coordList, k,
          coordNodes = node.getElementsByTagName('coordinates');

      if (!coordNodes){
          return [{coordinates: []}];
      }

      for (j=0; j<coordNodes.length;j++){
        coords = this.getNodeValue(coordNodes[j]).trim();
        coords = coords.replace(/,\s+/g, ',');
        path = coords.split(/\s+/g);
        pathLength = path.length;
        coordList = [];
        for (k = 0; k < pathLength; k++){
          coords = path[k].split(',');
          coordList.push({ lat: parseFloat(coords[1]), lng: parseFloat(coords[0]), alt: parseFloat(coords[2])});
        }
        coordListA.push({coordinates: coordList});
      }

      return coordListA;
  },
  
  
  /*
  * find map overlays
  */
  findOverlays: function  (doc, groundNodes, folder, parentFolder) {
      var node, groundOverlay, color, overlay, id;
  
      doc.overlays = doc.overlays || [];
      for (i = 0; i < groundNodes.length; i++) {
        node = groundNodes[i];
        
        // Init the ground overlay object
        groundOverlay = { name: this.getNodeValue(node.getElementsByTagName('name')[0]),
        				  description: this.getNodeValue(node.getElementsByTagName('description')[0]),
        				  icon: {href: this.getNodeValue(node.getElementsByTagName('href')[0])},
        				  latLonBox: { north: parseFloat(this.getNodeValue(node.getElementsByTagName('north')[0])),
        					  			east:  parseFloat(this.getNodeValue(node.getElementsByTagName('east')[0])),
        					  			south: parseFloat(this.getNodeValue(node.getElementsByTagName('south')[0])),
        					  			west:  parseFloat(this.getNodeValue(node.getElementsByTagName('west')[0]))}
        				};
        
        //look to see if the overlay has an id
        id = node.getAttribute('id');
        groundOverlay.id = (id !== null) ? id : 'overlayview-'+ this.overlayIndex++;
        
        //get bounds
        var box = groundOverlay.latLonBox,
        	bounds = new google.maps.LatLngBounds(new google.maps.LatLng(box.south, box.west),
        							              new google.maps.LatLng(box.north, box.east));
        groundOverlay.bounds = bounds;
        
        if (this.bounds) {
            this.bounds.union(bounds);
        }

        // Opacity is encoded in the kml color node
        color = this.getNodeValue(node.getElementsByTagName('color')[0]);        
        if ((color !== '') && (color.length === 8)) {
           var rgb = this.getRGBColorTransparency(color);
           this.overlayOpacity = rgb.opacity;
        } 
        
        if (groundOverlay.opacity === undefined) {
            groundOverlay.opacity = 0.7;
            this.overlayOpacity = 0.7;
        }   
        
        this.createOverlay(doc, groundOverlay, folder);
      }
  },
  
  
  /*
  * Add a ProjectedOverlay to the map from a groundOverlay KML object
  */
  createOverlay : function  (doc, groundOverlay, folder){  
	  if (!window.KmlOverlayView){
		  throw 'KmlMapParser error: KmlOverlayView not found while rendering GroundOverlay from KML';
	  }
      var overlay = new KmlOverlayView({ map: this.map, href: groundOverlay.icon.href, percentOpacity: groundOverlay.opacity, bounds: groundOverlay.bounds, id: groundOverlay.id});
    
	  groundOverlay.overlay = overlay;
	  groundOverlay.id = overlay.id_;
          
	  if (folder !== undefined) {
		  folder.overlays = folder.overlays || [];
		  groundOverlay.folderId = folder.id;
		  groundOverlay.folderName = folder.name;
		  folder.overlays.push(groundOverlay);
	  }
    
	  if (folder !== doc){
		  doc.overlays.push(groundOverlay);
		  doc.hasOverlays=true;
	  }
 
	  return overlay;
  },


  createSidebar : function(doc){
      if (!!this.sidebarId){
         var childFolders = doc.folders, i, placemarks, pm, folder, folderName,
             side = document.getElementById(this.sidebarId), id,
             contents = '',
             innerContents ='';
             folderName = doc.name;
             
         if (side !== null){
              if (this.showFolders && doc.hasFolders){
                  innerContents += this.addSidebarFolder(folderName, doc.folders);
              } else {
                  placemarks = doc.placemarks;
                  innerContents += this.addSidebarList(placemarks);
                 
                  //overlays
                  overlays = doc.overlays;
                  if (this.showOverlaysInSidebar && overlays !== undefined){
                	  innerContents += this.addSidebarOverlayList(overlays);
                  }
              }
              if (this.showRootName && innerContents !==''){
                  contents += '<li class="docroot" id="doc-'+doc.id+'" style="display:block"><span class="open"></span>'+ folderName +'<ul>'+ innerContents + '</ul></li>';
              } else if (innerContents !== ''){
                  contents += '<li class="docroot" id="doc-'+doc.id+'" style="display:block"><ul>'+ innerContents + '</ul></li>';
              }

             var root = document.getElementById('root');
             if (root !== null){
                 var c = document.createElement('div');
                 c.innerHTML=contents;
                 root.appendChild(c);
             } else {
                 side.innerHTML = '<ul id="root" >'+ contents + '</ul>';
                  
                 //add click listener
                 var scope=this,
                     clickSidebar = function(e){
	                    var t, child, className;
	                    if (!e){
	                        t = window.event.srcElement;
	                        className = window.event.srcElement.className;
	                    }else{
	                       t = e.target;
	                       if (!t){
	                          t = window.event.srcElement;
	                          className = window.event.srcElement.className;
	                       }else{
	                           className = t.className;
	                       }
	                    }
	                    
                    switch(className) {
                        case "placemark":
                        	if (scope.showBubble){
	                        	if (scope.showSidebarBubble){
	                        		scope.openBubbleById(t.id);
	                        	}else {
	                        		scope.hilightShapesById(t.id);
	                        	}
                        	}
                        	break;
                        case "open":
                        	t.className='closed';
                        	scope.setVisibilitybyNode(t.parentNode.getElementsByTagName('ul')[0], false);
                        	break;
                        case "closed":
                        	t.className='open';
                        	scope.setVisibilitybyNode(t.parentNode.getElementsByTagName('ul')[0], true);
                        	break;
                        case "checked":
                            t.className='unchecked';
                            scope.setOverlayVisibilityById(t.id.replace('-check', ''),false);
                            break;
                        case "unchecked":
                            t.className='checked';
                            scope.setOverlayVisibilityById(t.id.replace('-check', ''),true);
                            break; 
                    }
                };
       
               if (side.addEventListener){
                  side.addEventListener('click',clickSidebar,false);
               } else if (side.attachEvent){
                  side.attachEvent('onclick',clickSidebar);
               }
              }//end root null
         }
      }
  },


 /**
  * adds an entry to the sidebar
  */
  addSidebarFolder : function(parentName, childFolders) {
      var folder, i, folderName, cls,
      	  contents='', folderContents='', placemarkContents='', 
      	  overlayContents='', placemarks, sameName = false;

      if (!!parentName && childFolders.length > 0){
          for (i = 0; i < childFolders.length; i++){
              folder = childFolders[i];
              folderName = folder.name;
              cls = folder.isOpen ? 'open' : 'closed';
              if (folder.hasPlacemarks ){
                  placemarks = folder.placemarks;
                  if (folderName === placemarks[0].name){
                      sameName = true;
                  } else{
                      sameName = false;
                  } 
                  placemarkContents = this.addSidebarList(placemarks);
              }else{
                  placemarkContents='';
              }
              
              if (this.showOverlaysInSidebar && folder.hasOverlays){
            	  overlayContents = this.addSidebarOverlayList(folder.overlays);
              } else {
            	  overlayContents='';
              }
              
              if (folder.hasFolders){
                  folderContents = this.addSidebarFolder(folderName, folder.folders);
              } else {
                  folderContents='';
              }
    
              if (sameName){
                  contents += placemarkContents+overlayContents;
              }else{
            	  var d = folder.isOpen ? 'block' : 'none';
           		  contents += '<li><span class="'+cls+'"></span>'+ folderName +'<ul style="display:'+d+';">'+folderContents+placemarkContents+overlayContents+'</ul></li>';
              }         
          }
      }  
      return contents;
  },
 
  
 /**
  * createMarkerIconString
  */
  createMarkerIconString: function(markerIcon) {
      if (markerIcon !== undefined ){
          return '<img src="'+markerIcon+'" />';
      }else{
          return '';
      }
  },


  /**
  * adds an entry to the sidebar
  */
  addSidebarList : function(placemarks) {
      var pm, placemark, marker, icon, contents='', innerContents='', descrip='',
      	  markerId, markerIcon, id,
      	  itemList = [], itemListContents=[],
      	  folderHtml = '<span class="closed"></span>';

      //look for the placemarks
      for (pm = 0; pm < placemarks.length; pm++){
            placemark = placemarks[pm];
            id = placemark.id;
            if ( placemark.marker){
                  marker = placemark.marker;
                  id = marker.id;
                  markerIcon = placemark.style.href;
                  icon=this.createMarkerIconString(markerIcon);
            }
                
            if (this.showSidebarDescriptions){
                 innerContents = '<li class="placemark" id="'+ id + '">'+ folderHtml + icon + placemark.name;
                 descrip = this.createDescriptionString(placemark.description);
                 if (descrip !== undefined && itemListContents[placemark.name] === undefined){
                     itemListContents[placemark.name]=descrip;
                 }
            } else {
            	  if (placemark.marker){
            		  innerContents = '<li class="placemark" id="'+ id + '">'+ icon + placemark.name;
            	  } else {
            		  innerContents = '<li class="placemark" id="'+ id + '">'+ placemark.name;
            	  }
            }
            

             itemList[placemark.name]=innerContents;
          
      }

      var placemarkId, lc;
      for ( placemarkId in itemList){
         if (itemList.hasOwnProperty(placemarkId)) {
            lc = itemListContents[placemarkId];
            if (this.showSidebarDescriptions){
                if (lc === undefined ){
                    contents += itemList[placemarkId] +'</li>';
                }else{
                    contents += itemList[placemarkId] + '<ul style="display:none;">'+lc+'</ul></li>';
                }
            } else {
                contents += itemList[placemarkId] + '</li>';
            }
         }
      }

      return contents;
  },
  
  
  /**
   * adds an overlay to the sidebar
   */
   addSidebarOverlayList : function(overlays) {
       var  i, groundOverlay, overlay, contents='', innerContents='', descrip='',
       		itemList = [], itemListContents=[],
       		folderHtml = '<span class="closed"></span>';
       
       //look for the overlays
       for (i = 0; i < overlays.length; i++){
    	   groundOverlay = overlays[i];
           if (groundOverlay.overlay){
           	   overlay = groundOverlay.overlay;
           	   overlayId = groundOverlay.id;
                   
               if (this.showSidebarDescriptions){
               	   descrip = this.createDescriptionString(groundOverlay.description);
                   innerContents = '<li class="overlay">'+ folderHtml + '<span id="'+overlayId + '-check" class="checked"></span>' + groundOverlay.name+ '<ul style="display:none;">'+ descrip +'</ul></li>';
               } else {
                   innerContents = '<li class="overlay">' + '<span id="'+overlayId + '-check" class="checked"></span>' + groundOverlay.name;
               }
           }
           contents += innerContents;
       }
       return contents;
   },
  
  
  /**
  * create a description for a placemark
  */
  createDescriptionString : function(description){
      if (description !== undefined && description !== ''){
        var descrip = description.replace('<a name="JR_PAGE_ANCHOR_0_1"></a>', '');
        return '<li class="description" style="color: black;">'+ descrip +'</li>';
      }
      return undefined;
  },
  
 
  /**
  * gets the placemark from the id
  */
  getPlacemarkById : function(id){
      var aId = id.split('-');
      if (aId.length>0){
          var docId = aId[0],
              j = id.lastIndexOf('-'),
              newId = id.slice(0,j);
          if (docId){
              var placemark, placemarkId, pm,
              placemarks = this.docSet.docs[docId].placemarks;
              for (pm = 0; pm < placemarks.length; pm++){
                  placemark = placemarks[pm];
                  placemarkId = placemark.id;
                  if (placemark && placemark.id === newId){
                      return placemark;
                  }
              }
          }
      }
      return undefined;
  },
  
  
  /**
  * gets the doc id from a placemark or folder id.
  */
  getDocFolderIds: function(id){
      if (!!id && typeof id !== 'number'){
          var aId = id.split('-');
          if (aId.length>0){
              return aId;
          }
      }
      return undefined;
  },
  
    
  /**
  * returns the placemarks for a folder and placemark name
  */
  getPlacemarksByName : function(folderId, placemarkName) {
        if (folderId){
          var ids = this.getDocFolderIds(folderId), 
              placemarks, pm, placemark, aPlacemarks=[], doc;
              
          if (ids !== undefined){
              doc = this.getDocumentById(ids[0]);
          }
          if (doc !== undefined && doc.placemarks){
            placemarks = doc.placemarks;
            
            //loop through the placemarks
            for (pm = 0; pm <placemarks.length; pm++){
                placemark = placemarks[pm];
                if (placemark && placemark.folderId === folderId  && placemark.name === placemarkName){
                   aPlacemarks.push(placemark);
                }
            }
            return aPlacemarks;
          }
        } 
        return undefined;
  },


  /**
  * Opens the shared google info window, anchors it to the specified marker or map object, and
  * displays the marker content.
  */
  hilightShapesById : function(id) {
      var 	placemark, center,
      		object = this.mapshapes[id];
      
      if (object === undefined){
        object = this.mapshapes[id+'-0'];
      }
      if (object && object.folderId){
          var placemarks = this.getPlacemarksByName(object.folderId, object.title);
          if (placemarks){
              center = this.hilightShapes(placemarks);
          }
          this.zoomMap();
      }
      return false;
  },


  /**
  * hilights and unhilight shapes
  */
  hilightShapes : function(placemarks) {
      if (this.lastSelectedPlacemark === placemarks){
         return;
      }
      //unhighlight the last shapes
      if (this.lastSelectedPlacemark !== undefined){
          this.changeShapeColors(this.lastSelectedPlacemark, false);
      }
      var markerLatLng = this.changeShapeColors(placemarks, true);
      if (markerLatLng !== undefined){
        this.map.setCenter(markerLatLng);
      }
      this.lastSelectedPlacemark=placemarks;
  },


   /**
  * hilights and unhilight shapes  
  */
  changeShapeColors : function(placemarks, hilight) {
      var pm, placemark, points, point, lines, line, areas, area, circles, circle, markerLatLng, i,
      	  hilite = (hilight!==undefined) ? hilight : true,
      	  hiliteColor;
      
      if (hilite){
          this.lastSelectedPlacemarks=placemarks; 
          hiliteColor = this.highlightColor;
      }   
      //look for the placemarks
      for (pm = 0; pm < placemarks.length; pm++){
          placemark = placemarks[pm];
          points = placemark.points;
          circles = placemark.circles;
          lines = placemark.lines;
          areas = placemark.areas;
          marker = placemark.marker;
          if (!!marker){
              markerLatLng = marker.getPosition();
          }
          if (points!== undefined){
              for (i = 0; i < points.length; i++){
                  point = points[i];
                  if (hilite){              
                     point.setOptions({zIndex:this.zindex++});
                  } 
              }             
          }else if (circles !== undefined){
                  for (i = 0; i < circles.length; i++){
                      circle = circles[i];
                      if (hilite){
                        circle.origStrokeColor = circle.strokeColor;
                        circle.origFillColor = circle.strokeColor;
                        circle.setOptions({strokeColor:hiliteColor, fillColor: hiliteColor, zIndex:this.zindex++});
                      } else {
                        circle.setOptions({strokeColor:circle.origStrokeColor, fillColor: circle.origFillColor});
                      } 
                  }
           }else if (lines!== undefined){
              for (i = 0; i < lines.length; i++){
                  line = lines[i];
                  if (hilite){
                      line.origStrokeColor = line.strokeColor;                    
                      line.setOptions({strokeColor:hiliteColor, zIndex:this.zindex++});
                  } else {
                      line.setOptions({strokeColor:line.origStrokeColor});
                  }   
              }
          }else if (areas!== undefined){
              for (i = 0; i < areas.length; i++){
                  area = areas[i];
                  if (hilite){
                      area.origStrokeColor = area.strokeColor;
                      area.origFillColor = area.strokeColor;
                      area.setOptions({strokeColor:hiliteColor, fillColor: hiliteColor, zIndex:this.zindex++});
                  } else {
                      area.setOptions({strokeColor:area.origStrokeColor, fillColor: area.origFillColor});
                  }     
              }
          }
      }
      return markerLatLng;
  },


  /**
  * Opens the shared google info window, anchors it to the specified marker or map object, and
  * displays the marker content.
  */
  openBubble : function(object){
      if (object !== undefined){
    	  this.zoomMap();
    	  if (this.infoWindow){
	          if (object.description !== undefined){
	              this.infoWindow.setContent(object.description);
	          }
	          if (object.position !== undefined){
	              this.infoWindow.setPosition(object.position);
	          }
	          this.infoWindow.open(this.map, object);
    	  }
          var placemarks = this.getPlacemarksByName(object.folderId, object.title);
          if (placemarks){
              this.hilightShapes(placemarks);
          }
      }       
  },
 

  /**
  * zooms map
  */
  zoomMap : function(zoom){
      if (this.shouldZoom){
          if (zoom === undefined){
            zoom = this.defaultZoomLevel;
          }
          if (this.map.getZoom() < zoom+1){
             this.map.setZoom(zoom);
          }
      }
  },


  /**
  * Opens the shared google info window, anchors it to the specified marker or map object, and
  * displays the marker content.
  */
  openBubbleById : function(id) {
      var m = this.mapshapes[id];
      if (m === undefined){
        m = this.mapshapes[id+'-0'];
      }
      this.openBubble(m);
  },


  /**
  * Called when clicking anywhere on the map and closes the info window.
  */
  closeBubble : function() {
	  if (this.infoWindow){
		  this.infoWindow.close();
	  }
  },

  
  /*
   *  KML color and opacity (alpha) values are expressed in hexadecimal notation. The range of values for any one color is 0 to 255 (00 to ff).
   *  For alpha, 00 is fully transparent and ff is fully opaque.
   *  The order of expression is aabbggrr, where aa=alpha (00 to ff); bb=blue (00 to ff); gg=green (00 to ff); rr=red (00 to ff).
   *  
   *  For example, if you want to apply a blue color with 50 percent opacity to an overlay, 
   *  you would specify the following: <color>7fff0000</color>, where alpha=0x7f, blue=0xff, green=0x00, and red=0x00. 
   */
   getRGBColorTransparency: function (kmlColor) {
      var color = {};
      if (kmlColor) {
        aa = kmlColor.substr(0,2);
        bb = kmlColor.substr(2,2);
        gg = kmlColor.substr(4,2);
        rr = kmlColor.substr(6,2);
        color.color = "#" + rr + gg + bb;
        color.opacity = parseFloat(parseInt(aa,16)/256).toFixed(1);
      } 
      if (isNaN(color.opacity)){
    	  color.color = this.getRandomColor();
    	  color.opacity = 1.0;
      }
      return color;
  },


 /*
  * getRandomColor
  */
  getRandomColor : function(){     
      return  '#' + Math.round(0xffffff * Math.random()).toString(16);
  },
 
  
  /*
  *  enable DragZoom icon on the map
  */
  enableDragZoom: function () {
	  if (!this.map.enableKeyDragZoom) {
		  throw 'KmlMapParser error: keydragzoom.js OR keydragzoom_packed.js not found while enabling dragZoomButton.';
	  }

      this.map.enableKeyDragZoom({
          visualEnabled: true,
          visualPosition: google.maps.ControlPosition.LEFT,
          visualPositionOffset: new google.maps.Size(35, 0),
          visualPositionIndex: null,
          visualSprite: this.dragZoomButtonImage,
          visualSize: new google.maps.Size(20, 20),
          visualTips: {off: "Turn on drag zoom", on: "Turn off drag zoom"}
       });
  },


  /*
  * logs a message to the window console if one exists
  * else it will display an alert
  */
  log : function(msg) {
      if (window.console !== undefined){
        console.log(msg);
      } else {
        alert("log:" + msg);
      }
  }

};
function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 11,
    center: {lat: 41.876, lng: -87.624}
  });

  
 
  var xml = new KmlMapParser({ map: map,
                           kml: 'westcampus.kml',
                });
  
}
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>KmlFile</name>
    <Style id="west_campus_style">
      <IconStyle>
        <Icon>
          <href>https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png
          </href>
        </Icon>
      </IconStyle>
      <BalloonStyle>
        <text>$[video]</text>
      </BalloonStyle>
    </Style>
    <Placemark>
      <name>Google West Campus 1</name>
      <styleUrl>#west_campus_style</styleUrl>
      <ExtendedData>
        <Data name="video">
          <value><![CDATA[<iframe width="480" height="360"
            src="https://www.youtube.com/embed/ZE8ODPL2VPI" frameborder="0"
            allowfullscreen></iframe><br><br>]]></value>
        </Data>
      </ExtendedData>
      <Point>
        <coordinates>-122.0914977709329,37.42390182131783,0</coordinates>
      </Point>
    </Placemark>
    <Placemark>
      <name>Google West Campus 2</name>
      <styleUrl>#west_campus_style</styleUrl>
      <ExtendedData>
        <Data name="video">
          <value><![CDATA[<iframe width="480" height="360"
            src="https://www.youtube.com/embed/nb4gvrNrDWw" frameborder="0"
            allowfullscreen></iframe><br><br>]]></value>
        </Data>
      </ExtendedData>
      <Point>
        <coordinates>-122.0926995893311,37.42419403634421,0</coordinates>
      </Point>
    </Placemark>
    <Placemark>
      <name>Google West Campus 3</name>
      <styleUrl>#west_campus_style</styleUrl>
      <ExtendedData>
        <Data name="video">
          <value><![CDATA[<iframe width="480" height="360"
            src="https://www.youtube.com/embed/0hhiEjf7_NA" frameborder="0"
            allowfullscreen></iframe><br><br>]]></value>
        </Data>
      </ExtendedData>
      <Point>
        <coordinates>-122.0922532985281,37.42301710721216,0</coordinates>
      </Point>
    </Placemark>
  </Document>
</kml>