OpenLayers.Feature.Photos = OpenLayers.Class.create();
OpenLayers.Feature.Photos.prototype =
  OpenLayers.Class.inherit( OpenLayers.Feature, {

  photos: null,
  _photos: null,
  activeId: null,
  feature: null,
  display: false,
  threshold: 20,
  spots: null,

  /** 
   * @constructor
   * 
   * @param {OpenLayers.Layer} layer
   * @param {XMLNode} xmlNode
   */
  initialize: function( layer, xmlNode, icon, xmlMetaData )
    {
    var newArguments = arguments;
    var data = this.processXMLNode(xmlNode);
    newArguments = new Array(layer, data.lonlat, data);

    OpenLayers.Feature.prototype.initialize.apply(this, newArguments);

    this.data.icon = icon;
    this.spots = [ data.id ];

    if ( xmlMetaData )
      {
      this.requestSuccess( xmlMetaData );
      }
    else
      {
      OpenLayers.loadURL( this.layer.metadataUrl + "id=" + data.id, null, this, this.requestSuccess, null );
      }
    },

  requestSuccess: function ( request )
    {
    var doc = request.responseXML;
    this._photos = new Array( );

    var i;
    var imgs = OpenLayers.Ajax.getElementsByTagNameNS( doc, "", "", "photo" );

    for ( var j = 0; j < imgs.length; j ++ )
      {
      if ( imgs[j].getAttribute( "spot" ) == this.data.id )
        {
        var metadata = new Array( );
        metadata["links"] = new Array( );
        metadata["id"]    = imgs[j].getAttribute( "id" );
        metadata["spot"]  = this.data.id;
        for ( i = 0; i < imgs[j].childNodes.length; i ++ )
          {
          var node = imgs[j].childNodes[i];
          if ( node.nodeName == "link" )
            {
            var link = new Array( );
  
            for ( var k = 0; k < node.childNodes.length; k ++ )
              {
              if ( node.childNodes[k].nodeName == "left"   |
                   node.childNodes[k].nodeName == "right"  |
                   node.childNodes[k].nodeName == "top"    |
                   node.childNodes[k].nodeName == "bottom" |
                   node.childNodes[k].nodeName == "title"  |
                   node.childNodes[k].nodeName == "description" )

                { link[node.childNodes[k].nodeName] = node.childNodes[k].childNodes.length > 0 ? node.childNodes[k].firstChild.nodeValue : ""; }
              }

            link["target"] = node.getAttribute( "target" );
            metadata["links"].push( link );
            }
          else if ( node.nodeName == "img" )
            { metadata[node.getAttribute( "type" )] = new Array( node.firstChild.nodeValue, node.getAttribute( "width" ), node.getAttribute( "height" ) ); }
          else if ( node.nodeName == "title" | node.nodeName == "description" )
            { if ( node.firstChild ) { metadata[node.nodeName] = node.firstChild.nodeValue; } }
          }

        this._photos.push( metadata );
        }
      }

    this.setPhotos( );
    },

  setPhotos: function( )
    {
    var mypx = this.layer.map.getLayerPxFromLonLat( this.data.lonlat );
    var feature = this;

    for ( var i = 0; i < this.layer.features.length; i ++ )
      {
      var px = this.layer.map.getLayerPxFromLonLat( this.layer.features[i].data.lonlat );

      if ( ( ( px.x - mypx.x ) * ( px.x - mypx.x ) + ( px.y - mypx.y ) * ( px.y - mypx.y ) <= this.threshold * this.threshold ) & ( this.layer.features[i].display ) & ( this.layer.features[i] != this ) )
        { feature = this.layer.features[i]; }
      }

    this.display = ( feature == this );
    this.feature = feature;

    if ( this.display )
      {
      this.photos = ( new Array( ) ).concat( this._photos );

      if ( ! this.marker )
        {
        this.createMarker( );
        this.marker.events.register( 'click', this, this.markerClick );
        this.layer.addMarker( this.marker );
        }
      else { this.marker.display( true ); }
      }
    else
      {
      this.photos = null;
      feature.photos = feature.photos.concat( this._photos );
      feature.spots = feature.spots.concat( this.spots );
      this.spots  = null;
      if ( this.marker ) { this.marker.display( false ); };
      }

/*
for ( var i = 0; i < this.photos.length; i ++ )
  {
  if ( this.photos[i]["id"] == "234" )
    { this.markerClick( null, i ); }
  }
*/
    },

  createPopup: function( )
    {

    if ( this.lonlat != null )
      {
      var id = this.id + "_popup";
      var anchor = (this.marker) ? this.marker.icon : null;

      this.popup = new OpenLayers.Popup.Anchored(id,
                                                 this.lonlat,
                                                 this.data.popupSize,
                                                 this.data.popupContentHTML,
                                                 anchor, true);
      }
    return this.popup;
    },

  setPhotoId: function( element )
    {
    this.markerClick( null, element.getAttribute( "title" ) );
    },

  markerClick: function( e, id )
    {
    if ( ! id ) { id = 0; }

    if ( ( this == this.layer.selectedFeature ) & ( this.activeId == id ) )
      {
      this.destroyPopup( );
      this.layer.selectedFeature = null;
      this.activeId = null;
      }
    else
      {
      var a = this;
      this.layer.selectedFeature = this;
      this.activeId = id;

      this.data.popupSize = new OpenLayers.Size( this.photos[id]["preview"][1], this.photos[id]["preview"][2] );

      var atag = document.createElement( "a" );
      var menu = document.createElement( "ul" );
      menu.className = "olTopMenu";
      atag.appendChild( menu );
      atag.firstChild.appendChild( document.createElement( "li" ) );
      atag.firstChild.firstChild.appendChild( document.createTextNode( "other Views (" + ( this.photos[id]["title"] || "Photo " + this.photos[id]["id"] ) + ")" ) );
      menu = document.createElement( "ul" );

      for ( var i = 0; i < this.photos.length; i ++ )
        {
        if ( i != id )
          {
          var item = document.createElement( "li" );
          item.appendChild( document.createTextNode( this.photos[i]["title"] || "Photo " + this.photos[i]["id"] ) );
          item.title = i;
          OpenLayers.Event.observe( item, "click", function( e ) { a.setPhotoId( this ); } );
          menu.appendChild( item );
          }
        }
      atag.firstChild.firstChild.appendChild( menu );

      var imgdiv = document.createElement( "div" );
      atag.appendChild( imgdiv );
      imgdiv.style.width  = this.photos[id]["preview"][1]+"px";
      imgdiv.style.height = this.photos[id]["preview"][2]+"px";
      imgdiv.style.backgroundImage = "url(" + this.photos[id]["preview"][0] + ")";

      for( i = 0; i < this.photos[id]["links"].length; i ++ )
        {
        var linkdiv = document.createElement( "div" );
        var link = this.photos[id]["links"][i];
        linkdiv.style.position = "absolute";
        linkdiv.style.cursor = "pointer";
        linkdiv.className = "olPhotoLink";
        linkdiv.style.top    = link["top"]+"px";
        linkdiv.style.left   = link["left"]+"px";
        linkdiv.style.width  = ( link["right"] - link["top"] ) + "px";
        linkdiv.style.height = ( link["bottom"] - link["top"] ) + "px";
        linkdiv.title = i;

        OpenLayers.Event.observe( linkdiv, "click", function( e ) { a.linkClicked( this ); } );
        imgdiv.appendChild( linkdiv );
        }

      this.data['popupContentHTML'] = atag;

      var popup = this.createPopup( );
      this.layer.map.addPopup( popup, true );
      }
    },

  linkClicked: function( link )
    {
    var target = this.photos[this.activeId]["links"][link.getAttribute( "title" )]["target"];

    this.layer.openLocation( target );

/*    var foundFeature = null;
    var id = 0;

    for ( var i = 0; i < this.layer.features.length; i ++ )
      {
      var feature = this.layer.features[i];
      if ( feature.photos )
        {
        for ( var j = 0; j < feature.photos.length; j ++ )
          {
          if ( feature.photos[j]["id"] == target )
            { foundFeature = feature; id = j; }
          }
        }
      }

    if ( foundFeature )
      {
      foundFeature.markerClick( null, id );
      if ( ! foundFeature.onScreen( ) ) { this.layer.map.setCenter( foundFeature.lonlat ); }
      }
*/
    },

  destroy: function( )
    {
    if (this.marker != null)
      { this.layer.removeMarker(this.marker); }
    OpenLayers.Feature.prototype.destroy.apply(this, arguments);
    },

    /**
     * @param {XMLNode} xmlNode
     * 
     * @returns Data Object with 'id', 'lonlat', and private properties set
     * @type Object
     */
  processXMLNode: function( xmlNode )
    {
    //this should be overridden by subclasses
    // must return an Object with 'id' and 'lonlat' values set
    var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
    var id = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://mapserver.gis.umn.edu/mapserver", "ms", "id");
    var text  = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
    var floats = text.split(",");
    return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
                                          parseFloat(floats[1])),
            id: OpenLayers.Util.getXmlNodeValue( id[0] ) };
    },

    /** @final @type String */
  CLASS_NAME: "OpenLayers.Feature.Photos"
});
/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
 * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
 * for the full text of the license. */

/** 
 * @class
 * 
 * @requires OpenLayers/Control.js
 */
OpenLayers.Control.Profiles = OpenLayers.Class.create();
OpenLayers.Control.Profiles.prototype = 
  OpenLayers.Class.inherit( OpenLayers.Control, {

    /** @type String */
    activeColor: "darkblue",
    

  // DOM Elements
  
    /** @type DOMElement */
    layersDiv: null,
    scaleDiv: null,
    lengthDiv: null,
    profileimageDiv: null,
    profileDiv: null,
    barDiv: null,
    menuP: null,
    dayboxes: null,

    profiles: null,
    spots: null,
    activeProfile: null,

    /** @type DOMElement */
    baseLayersDiv: null,

    /** @type Array */
    baseLayerInputs: null,
    
    
    /** @type DOMElement */
    dataLbl: null,
    
    /** @type DOMElement */
    dataLayersDiv: null,

    /** @type Array */
    dataLayerInputs: null,


    /** @type DOMElement */
    minimizeDiv: null,

    /** @type DOMElement */
    maximizeDiv: null,
    
    /** @type Boolean */
    ascending: true,


    loaded: false,
    url: null,
    tour: null,
    day: null,
 
    /**
    * @constructor
    */
    initialize: function( url, tour, day, options )
      {
      OpenLayers.Control.prototype.initialize.apply( this, arguments );

      this.url  = url;
      this.tour = tour;
      this.day  = day;
      },

    /** 
     * @param {OpenLayers.Map} map
     */
    setMap: function(map) {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);

        this.map.events.register("addlayer", this, this.redraw);
        this.map.events.register("changelayer", this, this.redraw);
        this.map.events.register("removelayer", this, this.redraw);
        this.map.events.register("changebaselayer", this, this.redraw);
    },

    /**
    * @returns A reference to the DIV DOMElement containing the switcher tabs
    * @type DOMElement
    */  
    draw: function() {
        OpenLayers.Control.prototype.draw.apply(this);

        if ( ! this.loaded )
          {
          this.loaded = true;
          OpenLayers.loadURL( this.url + "/" + this.tour + ".xml", null, this, this.parseData );
          }

        // create layout divs
        this.loadContents();

        // set mode to minimize
        this.minimizeControl();
        
        // populate div with current info
        this.redraw();    

        return this.div;
    },

    /** Goes through and takes the current state of the Map and rebuilds the
     *   control to display that state. Groups base layers into a radio-button
     *   group and lists each data layer with a checkbox.
     * 
     * @returns A reference to the DIV DOMElement containing the control
     * @type DOMElement
     */  
    redraw: function( )
      {
      this.spots = new Array( );

      for ( i = 0; i < this.map.layers.length; i ++ )
        {
// this.map.layers[i].visibility
        if ( this.map.layers[i].metaData )
          {
//alert( this.map.layers[i].name );
          var doc   = this.map.layers[i].metaData.responseXML
          var spots = OpenLayers.Ajax.getElementsByTagNameNS( doc, "", "", "spot" );

          var j;
          for ( j = 0; j < spots.length; j ++ )
            {
            if ( ! this.spots[spots[j].getAttribute( "day" )] ) { this.spots[spots[j].getAttribute( "day" )] = new Array( ); }
            this.spots[spots[j].getAttribute( "day" )].push( [ parseFloat( spots[j].getAttribute( "distance" ) ), parseFloat( spots[j].getAttribute( "height" ) ), spots[j].getAttribute( "id" ), i ] );
            }
          }
        }

        if ( this.profiles ) { this.display( this.day > -1 ? this.day : ( this.profiles.length - 1 ) ); }

        return this.div;
      },

    parseData: function( request )
      {
      var doc = request.responseXML;
      var p = OpenLayers.Ajax.getElementsByTagNameNS( doc, "", "", "profile" );
      var i;

      this.profiles = new Array( );

      for ( i = 0; i < p.length; i ++ )
        {
        this.profiles.push( [ p[i].getAttribute( "id" ), this.url + "/" + p[i].getAttribute( "src" ), parseFloat( p[i].getAttribute( "length" ) ), parseFloat( p[i].getAttribute( "min" ) ), parseFloat( p[i].getAttribute( "max" ) ), p[i].getAttribute( "scales" ), p[i].getAttribute( "up" ), p[i].getAttribute( "down" ) ] );
        }

    for ( i = 0; i < this.profiles.length; i ++ )
      {
      var a = document.createElement( "a" );
      a.appendChild( document.createTextNode( this.profiles[i][0] ) );
      a.style.cursor = "pointer";
      if ( i > 0 ) { this.menuP.appendChild( document.createTextNode( " | " ) ); }

      OpenLayers.Event.observe( a, "click", this.display.bind( this, i ) );

      this.menuP.appendChild( a );
      }
    this.display( this.day > -1 ? this.day : ( this.profiles.length - 1 ) );

      },

    /** A label has been clicked, check or uncheck its corresponding input
     * 
     * @private
     * 
     * @param {Event} e
     */

    onInputClick: function(e) {
    },
    
    /** Need to update the map accordingly whenever user clicks in either of
     *   the layers.
     * 
     * @private
     * 
     * @param {Event} e
     */
    onLayerClick: function(e) {
    },


    /** Cycles through the loaded data and base layer input arrays and makes
     *   the necessary calls to the Map object such that that the map's 
     *   visual state corresponds to what the user has selected in the control
     * 
     * @private
     */
    updateMap: function() {
    },

    /** Set up the labels and divs for the control
     * 
     * @param {Event} e
     */
    maximizeControl: function(e) {

        //HACK HACK HACK - find a way to auto-size this layerswitcher
        this.div.style.width = "830px";
        this.div.style.height = "";

        this.showControls(false);

        if (e != null) {
            OpenLayers.Event.stop(e);                                            
        }
    },
    
    /** Hide all the contents of the control, shrink the size, 
     *   add the maximize icon
     * 
     * @param {Event} e
     */
    minimizeControl: function(e) {

        this.div.style.width = "0px";
        this.div.style.height = "0px";

        this.showControls(true);

        if (e != null) {
            OpenLayers.Event.stop(e);                                            
        }
    },

    /** Hide/Show all LayerSwitcher controls depending on whether we are
     *   minimized or not
     * 
     * @private
     * 
     * @param {Boolean} minimize
     */
    showControls: function(minimize) {
        this.maximizeDiv.style.display = minimize ? "" : "none";
        this.minimizeDiv.style.display = minimize ? "none" : "";

        this.layersDiv.style.display = minimize ? "none" : "";
    },

  spotClicked: function( layer, id )
    {
    this.map.layers[layer].openLocation( id, "spot" );
    },
    
  display: function( profile )
    {
    var scales = [ 100000, 50000, 25000, 10000, 5000, 2500, 1000, 500, 250, 100 ];
    var width  = 700;
    var height = 300;

    if ( profile ) { this.activeProfile = profile; }
    if ( this.profiles && ( profile || this.activeProfile ) )
      {
    var length = Math.round( this.profiles[profile][2] * 10 ) * 100;
    var mintics = 10;
    var scale;

    if ( profile == null ) { profile = this.activeProfile; }
    else { this.activeProfile = profile; }

    var i = 0;
    while ( ( i < scales.length ) && ( ( length / scales[i] ) < mintics ) )
      { i ++; }
    scale = scales[i];

    while ( this.barDiv.childNodes.length > 0 ) { this.barDiv.removeChild( this.barDiv.firstChild ); }

    for ( i = 0; i < length/scale; i += 2 )
      {
      var bar = document.createElement( "div" );
      bar.style.background = "gray";
      bar.style.width = Math.min( Math.round( scale * width / length ), width - Math.round( scale * width * i / length ) ) + "px";
      bar.style.height = "10px";
      if ( i > 0 ) { bar.style.top = ( -5 * i ) + "px"; }
      bar.style.position = "relative";
      bar.style.left = Math.round( scale * width * i / length ) + "px";
      this.barDiv.appendChild( bar );
      }
    this.barDiv.style.width = width + "px";

    var j = 0;
    var spots = document.createElement( "spots" );
    spots.style.position = "absolute";

    while ( this.dayboxes.firstChild ) { this.dayboxes.removeChild( this.dayboxes.firstChild ); }
    this.dayboxes.style.width = width + "px";
    this.dayboxes.style.height = height + "px";
//    Rico.Corner.changeOpacity( dayboxes, 0.20 );

    var min = profile;
    var max = min;
    if ( this.profiles[profile][0] == "all" ) { min = 0; max = this.profiles.length - 2; }

    var prof;
    var dist = 0;
    for ( prof = min; prof <= max; prof ++ )
      {
      var p = this.profiles[prof][0];

      if ( this.spots[p] )
        {
        for ( j = 0; j < this.spots[p].length; j ++ )
          {
          var spot = document.createElement( "div" );
          spot.className = "olProfileSpot";
          OpenLayers.Event.observe( spot, "click", this.spotClicked.bind( this, this.spots[p][j][3], this.spots[p][j][2] ) );
          spot.style.left = Math.round( -5 + width * ( this.spots[p][j][0] + dist ) / this.profiles[profile][2] ) + "px";
          spot.style.top = ( -5 + Math.round( height * ( 1 - ( this.spots[p][j][1] - this.profiles[profile][3] ) / ( this.profiles[profile][4] - this.profiles[profile][3] ) ) ) ) + "px";
          spots.appendChild( spot );
          }
        }

      if ( ( min != max ) && ( ( prof - min ) % 2 == 0 ) )
        {
        var box = document.createElement( "div" );
        box.style.position = "absolute";
        box.style.left = Math.round( width * dist / this.profiles[profile][2] ) + "px";
        box.style.width = Math.round( width * this.profiles[prof][2] / this.profiles[profile][2] ) + "px";;
        box.style.top = "0px";
        box.style.height = "100%";
        box.style.background = "#20209B";

        this.dayboxes.appendChild( box );
        }

      dist += this.profiles[prof][2];
      }

    this.profileimageDiv = document.createElement( "div" );
    this.profileimageDiv.style.height = height + "px";
    this.profileimageDiv.style.top = "0px";
    this.profileimageDiv.style.left = "0px";
    this.profileimageDiv.style.position = "absolute";
    this.profileimageDiv.style.background = "url( " + this.profiles[profile][1] + " )";
    this.profileimageDiv.style.width = width + "px";
    this.profileDiv.style.height = height + "px";
    this.dayboxes.appendChild( this.profileimageDiv );
    this.profileimageDiv.appendChild( spots );

    var labels = this.profiles[profile][5].split( ',' );
    for ( i = 0; i < labels.length; i ++ )
      {
      var label = document.createElement( "span" );
      label.appendChild( document.createTextNode( labels[i] + " m" ) );
      label.style.top = Math.round( height * ( 1 - ( labels[i] - this.profiles[profile][3] ) / ( this.profiles[profile][4] - this.profiles[profile][3] ) ) ) + "px";
      label.className = "olProfileLabel";
      this.profileimageDiv.appendChild( label );
      }

    var text = document.createTextNode( scale / 1000 > 1 ? ( scale / 1000 ) + " km" : scale + " m" );
    if ( this.scaleSpan.childNodes.length > 0 ) { this.scaleSpan.replaceChild( text, this.scaleSpan.firstChild ); }
    else { this.scaleSpan.appendChild( text ); }

    text = document.createTextNode( length / 1000 > 1 ? ( length / 1000 ) + " km" : length + " m" );
    if ( this.lengthSpan.childNodes.length > 0 ) { this.lengthSpan.replaceChild( text, this.lengthSpan.firstChild ); }
    else { this.lengthSpan.appendChild( text ); }

    text = document.createTextNode( this.profiles[profile][6] + " m / " + this.profiles[profile][7] + " m" );
    if ( this.updownSpan.childNodes.length > 0 ) { this.updownSpan.replaceChild( text, this.updownSpan.firstChild ); }
    else { this.updownSpan.appendChild( text ); }

    var day = -1;
    for ( i = 0; i < this.menuP.childNodes.length; i ++ )
      {
      if ( this.menuP.childNodes[i].nodeName == "A" )
        {
        day ++;
        this.menuP.childNodes[i].className = ( day == profile ? "active" : "" );
        }
      }
    } },

    /** Set up the labels and divs for the control
     * 
     */
    loadContents: function() {
        //configure main div
        this.div.className = "olProfileViewer";
    
        OpenLayers.Event.observe(this.div, "mouseup", this.mouseUp.bindAsEventListener(this));
        OpenLayers.Event.observe(this.div, "click", this.ignoreEvent);
        OpenLayers.Event.observe(this.div, "mousedown", this.mouseDown.bindAsEventListener(this));
        OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);


        // layers list div        
        this.layersDiv = document.createElement("div");
        this.layersDiv.className = "olProfileViewerDiv";
        this.layersDiv.id = "layersDiv";
        this.layersDiv.style.paddingLeft = "80px";

        this.menuP = document.createElement( "p" );
        this.layersDiv.appendChild( this.menuP );

        var profile = document.createElement( "div" );
 
        this.profileDiv = document.createElement( "div" );
        this.dayboxes = document.createElement( "div" );
        this.dayboxes.style.position = "absolute";
        this.profileDiv.appendChild( this.dayboxes );
 
        this.barDiv = document.createElement( "div" );
        this.barDiv.style.height = "10px";
        profile.appendChild( this.profileDiv );
        profile.appendChild( this.barDiv );
        this.layersDiv.appendChild( profile );

        this.layersDiv.appendChild( document.createTextNode( "Scale: " ) );
        this.scaleSpan = document.createElement( "span" );
        this.layersDiv.appendChild( this.scaleSpan );
        this.layersDiv.appendChild( document.createElement( "br" ) );
        this.layersDiv.appendChild( document.createTextNode( "Length: " ) );
        this.lengthSpan = document.createElement( "span" );
        this.layersDiv.appendChild( this.lengthSpan );
        this.layersDiv.appendChild( document.createElement( "br" ) );
        this.layersDiv.appendChild( document.createTextNode( "Up / Down: " ) );
        this.updownSpan = document.createElement( "span" );
        this.layersDiv.appendChild( this.updownSpan );

        this.div.appendChild( this.layersDiv );

        Rico.Corner.round(this.div, {corners: "tl bl",
                                      bgColor: "transparent",
                                      color: this.activeColor,
                                      blend: false});

        Rico.Corner.changeOpacity(this.layersDiv, 0.90 );

        var imgLocation = OpenLayers.Util.getImagesLocation();
        var sz = new OpenLayers.Size(18,18);        

        // maximize button div
        var img = imgLocation + 'layer-switcher-maximize.png';
        this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
                                    "OpenLayers_Control_MaximizeDiv", 
                                    null, 
                                    sz, 
                                    img, 
                                    "absolute");
        this.maximizeDiv.style.top = "5px";
        this.maximizeDiv.style.right = "0px";
        this.maximizeDiv.style.left = "";
        this.maximizeDiv.style.display = "none";
        OpenLayers.Event.observe(this.maximizeDiv, 
                      "click", 
                      this.maximizeControl.bindAsEventListener(this));
        
        this.div.appendChild(this.maximizeDiv);

        // minimize button div
        var img = imgLocation + 'layer-switcher-minimize.png';
        var sz = new OpenLayers.Size(18,18);        
        this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
                                    "OpenLayers_Control_MinimizeDiv", 
                                    null, 
                                    sz, 
                                    img, 
                                    "absolute");
        this.minimizeDiv.style.top = "5px";
        this.minimizeDiv.style.right = "0px";
        this.minimizeDiv.style.left = "";
        this.minimizeDiv.style.display = "none";
        OpenLayers.Event.observe(this.minimizeDiv, 
                      "click", 
                      this.minimizeControl.bindAsEventListener(this));

        this.div.appendChild(this.minimizeDiv);
    },
    
    /** 
     * @private
     *
     * @param {Event} evt
     */
    ignoreEvent: function(evt) {
        OpenLayers.Event.stop(evt);
    },

    /** Register a local 'mouseDown' flag so that we'll know whether or not
     *   to ignore a mouseUp event
     * 
     * @private
     *
     * @param {Event} evt
     */
    mouseDown: function(evt) {
        this.mouseDown = true;
        this.ignoreEvent(evt);
    },

    /** If the 'mouseDown' flag has been set, that means that the drag was 
     *   started from within the LayerSwitcher control, and thus we can 
     *   ignore the mouseup. Otherwise, let the Event continue.
     *  
     * @private
     *
     * @param {Event} evt
     */
    mouseUp: function(evt) {
        if (this.mouseDown) {
            this.mouseDown = false;
            this.ignoreEvent(evt);
        }
    },

    /** @final @type String */
    CLASS_NAME: "OpenLayers.Control.Profiles"
});
/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
 * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
 * for the full text of the license. */


/**
 * @class
 * 
 * @requires OpenLayers/Control.js
 */
OpenLayers.Control.MyArgParser = OpenLayers.Class.create();
OpenLayers.Control.MyArgParser.prototype = 
  OpenLayers.Class.inherit( OpenLayers.Control, {

    /** @type OpenLayers.LonLat */
    center: null,
    
    /** @type int */
    zoom: null,

    /** @type Array */
    layers: null,

    getLayer: null,

    /**
     * @constructor
     * 
     * @param {DOMElement} element
     * @param {String} base
     */
    initialize: function(element, base, getLayer) {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
        this.getLayer = getLayer;
    },

    /** Set the map property for the control. 
     * 
     * @param {OpenLayers.Map} map
     */
    setMap: function(map) {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);

        //make sure we dont already have an arg parser attached
        for(var i=0; i< this.map.controls.length; i++) {
            var control = this.map.controls[i];
            if ( (control != this) &&
                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
                break;
            }
        }
        if (i == this.map.controls.length) {

            var args = OpenLayers.Util.getArgs();
            if (args.lat && args.lon) {
                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
                                                    parseFloat(args.lat));
                if (args.zoom) {
                    this.zoom = parseInt(args.zoom);
                }
    
                // when we add a new baselayer to see when we can set the center
                this.map.events.register('changebaselayer', this, 
                                         this.setCenter);
                this.setCenter();
            }
    
            if ( args.overlays )
              {
              this.overlays = args.overlays;

              if ( this.getLayer )
                {
                for( var i = 0; i < this.overlays.length; i++ )
                  { this.getLayer( this.map, this.overlays.charAt( i ) ); }
                }
              }

            if (args.layers) {
                this.layers = args.layers;
    
                // when we add a new layer, set its visibility 
                this.map.events.register('addlayer', this, 
                                         this.configureLayers);
                this.configureLayers();
            }
        }
    },
   
    /** As soon as a baseLayer has been loaded, we center and zoom
     *   ...and remove the handler.
     */
    setCenter: function() {
        
        if (this.map.baseLayer) {
            //dont need to listen for this one anymore
            this.map.events.unregister('changebaselayer', this, 
                                       this.setCenter);
                                       
            this.map.setCenter(this.center, this.zoom);
        }
    },

    /** As soon as all the layers are loaded, cycle through them and 
     *   hide or show them. 
     */
    configureLayers: function() {

        if (this.layers.length == this.map.layers.length) { 
            this.map.events.unregister('addlayer', this, this.configureLayers);

            for(var i=0; i < this.layers.length; i++) {
                
                var layer = this.map.layers[i];
                var c = this.layers.charAt(i);
                
                if (c == "B") {
                    this.map.setBaseLayer(layer);
                } else if ( (c == "T") || (c == "F") ) {
                    layer.setVisibility(c == "T");
                }
            }
        }
    },     
    
    /** @final @type String */
    CLASS_NAME: "OpenLayers.Control.MyArgParser"
});
/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
 * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
 * for the full text of the license. */


/**
 * @class
 * 
 * @requires OpenLayers/Layer/EventPane.js
 * @requires OpenLayers/Layer/FixedZoomLevels.js
 */
OpenLayers.Layer.GoogleKMZ = OpenLayers.Class.create();
OpenLayers.Layer.GoogleKMZ.prototype =
  OpenLayers.Class.inherit( OpenLayers.Layer.EventPane, 
                            OpenLayers.Layer.FixedZoomLevels, {
    
    /** @final @type int */
    MIN_ZOOM_LEVEL: 0,
    
    /** @final @type int */
    MAX_ZOOM_LEVEL: 17,

    /** Hardcode these resolutions so that they are more closely
     *   tied with the standard wms projection
     * 
     * @final @type Array(float) */
    RESOLUTIONS: [1.40625,0.703125,0.3515625,0.17578125,0.087890625,0.0439453125,0.02197265625,0.010986328125,0.0054931640625,0.00274658203125,0.001373291015625,0.0006866455078125,0.00034332275390625,0.000171661376953125,0.0000858306884765625,0.00004291534423828125],

    /** @type GMapType */
    type: null,

    kmz: null,

    /** 
     * @constructor
     * 
     * @param {String} name
     */
    initialize: function(name, options) {
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
                                                                    arguments);
        this.addContainerPxFunction();
    },



setOverlay: function( kmz )
  {
/*alert( "b" );
            for ( var i = 0; i < this.kmz.length; i ++ )
              {
              var kmz = new GGeoXml( this.kmz[i] );
              map.addOverlay( kmz );
              }
*/

/*            detailMapLayers = ( this.type || G_NORMAL_MAP ).getTileLayers( );
                var copyCollection = new GCopyrightCollection( kmz );
                var copyright = new GCopyright( 1, new GLatLngBounds( new GLatLng( -90, -180 ), new GLatLng( 90, 180 ) ), 0, "");
                copyCollection.addCopyright( copyright );

                function CustomGetTileUrl( a, b )
                  { return "http://maps.google.de/mapsdt?id=" + kmz + "&x=" + a.x + "&y=" + a.y + "&zoom=" + ( 17 - b ); }

                var tilelayer = new GTileLayer( copyCollection, 3, 11 );
                tilelayer.getTileUrl = CustomGetTileUrl;

                detailMapLayers[detailMapLayers.length > 0 ? detailMapLayers.length - 1 : 0] = tilelayer;
*/
  },

    
    /** Load the GMap and register appropriate event listeners. If we can't 
     *   load GMap2, then display a warning message.
     * 
     * @private
     */
    loadMapObject:function() {
        
        //has gmaps library has been loaded?
        try {
            // create GMap, hide nav controls
            this.mapObject = new GMap2( this.div );

            for ( var i = 0; i < this.kmz.length; i ++ )
              {
              var kmz = new GGeoXml( this.kmz[i] );
              this.mapObject.addOverlay( kmz );
              }

/*            detailMapLayers = ( this.type || G_NORMAL_MAP ).getTileLayers( );
            if ( this.kmz )
              {
              for ( var i = 0; i < this.kmz.length; i ++ )
                {
                var kmz = this.kmz[i];
                var copyCollection = new GCopyrightCollection( kmz );
                var copyright = new GCopyright( 1, new GLatLngBounds( new GLatLng( -90, -180 ), new GLatLng( 90, 180 ) ), 0, "");
                copyCollection.addCopyright( copyright );

                function CustomGetTileUrl( a, b )
                  { return "http://maps.google.de/mapsdt?id=" + kmz + "&x=" + a.x + "&y=" + a.y + "&zoom=" + ( 17 - b ); }

                var tilelayer = new GTileLayer( copyCollection, 3, 11 );
                tilelayer.getTileUrl = CustomGetTileUrl;

                detailMapLayers.push( tilelayer );
                }
              }

            // move the ToS and branding stuff up to the pane
            // thanks a *mil* Erik for thinking of this
            var poweredBy = this.div.lastChild;
            this.div.removeChild(poweredBy);
            this.pane.appendChild(poweredBy);
            poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
            poweredBy.style.left = "";
            poweredBy.style.bottom = "";

            var termsOfUse = this.div.lastChild;
            this.div.removeChild(termsOfUse);
            this.pane.appendChild(termsOfUse);
            termsOfUse.className = "olLayerGoogleCopyright";
            termsOfUse.style.right = "";
            termsOfUse.style.bottom = "";*/

        } catch (e) {
            // do not crash
        }
               
    },

    /** Overridden from EventPane because if a map type has been specified, 
     *   we need to attach a listener for the first moveend -- this is how 
     *   we will know that the map has been centered. Only once the map has 
     *   been centered is it safe to change the gmap object's map type. 
     * 
     * @param {OpenLayers.Map} map
     */
    setMap: function(map) {
        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);

        if (this.type != null) {
            this.map.events.register("moveend", this, this.setMapType);
        }
    },
    
    /** The map has been centered, and a map type was specified, so we 
     *   set the map type on the gmap object, then unregister the listener
     *   so that we dont keep doing this every time the map moves.
     * 
     * @private
     */
    setMapType: function() {
        if (this.mapObject.getCenter() != null) {
            this.mapObject.setMapType(this.type);
            this.map.events.unregister("moveend", this, this.setMapType);
        }
    },

    /**
     * @param {Event} evt
     */
    onMapResize: function() {
        this.mapObject.checkResize();  
    },


    /**
     * @param {OpenLayers.Bounds} bounds
     *
     * @returns Corresponding zoom level for a specified Bounds. 
     *          If mapObject is not loaded or not centered, returns null
     * @type int
     *
    getZoomForExtent: function (bounds) {
        var zoom = null;
        if (this.mapObject != null) {
            var moBounds = this.getMapObjectBoundsFromOLBounds(bounds);
            var moZoom = this.getMapObjectZoomFromMapObjectBounds(moBounds);

            //make sure zoom is within bounds    
            var moZoom = Math.min(Math.max(moZoom, this.minZoomLevel), 
                                 this.maxZoomLevel);

            zoom = this.getOLZoomFromMapObjectZoom(moZoom);
        }
        return zoom;
    },
    
    */
    
  //
  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
  //

    /**
     * @param {Object} moBounds
     * 
     * @returns An OpenLayers.Bounds, translated from the passed-in
     *          MapObject Bounds
     *          Returns null if null value is passed in
     * @type OpenLayers.Bounds
     */
    getOLBoundsFromMapObjectBounds: function(moBounds) {
        var olBounds = null;
        if (moBounds != null) {
            var sw = moBounds.getSouthWest();
            var ne = moBounds.getNorthEast();
            olBounds = new OpenLayers.Bounds(sw.lng(), 
                                             sw.lat(), 
                                             ne.lng(), 
                                             ne.lat() );
        }
        return olBounds;
    },

    /**
     * @param {OpenLayers.Bounds} olBounds
     * 
     * @returns A MapObject Bounds, translated from olBounds
     *          Returns null if null value is passed in
     * @type Object
     */
    getMapObjectBoundsFromOLBounds: function(olBounds) {
        var moBounds = null;
        if (olBounds != null) {
            var sw = new GLatLng(olBounds.bottom, olBounds.left);
            var ne = new GLatLng(olBounds.top, olBounds.right);
            moBounds = new GLatLngBounds(sw, ne);
        }
        return moBounds;
    },
    




    /** Hack-on function because GMAPS does not give it to us
     * 
     * @param {GLatLng} gLatLng 
     *
     * @returns A GPoint specifying gLatLng translated into "Container" coords
     * @type GPoint
     */
    addContainerPxFunction: function() {
        if (typeof GMap2 != "undefined" && !GMap2.fromLatLngToContainerPixel) {
          
            GMap2.prototype.fromLatLngToContainerPixel = function(gLatLng) {
          
                // first we translate into "DivPixel"
                    var gPoint = this.fromLatLngToDivPixel(gLatLng);
      
                    // locate the sliding "Div" div
                //  it seems like "b" is the main div
                    var div = this.getContainer().firstChild.firstChild;
      
                    // adjust by the offset of "Div" and voila!
                gPoint.x += div.offsetLeft;
                gPoint.y += div.offsetTop;
    
                return gPoint;
            };
        }
    },

    /** 
     * @return String with information on why layer is broken, how to get
     *          it working.
     * @type String
     */
    getWarningHTML:function() {

        var html = "";
        html += "The Google Layer was unable to load correctly.<br>";
        html += "<br>";
        html += "To get rid of this message, select a new BaseLayer "
        html += "in the layer switcher in the upper-right corner.<br>";
        html += "<br>";
        html += "Most likely, this is because the Google Maps library";
        html += " script was either not included, or does not contain the";
        html += " correct API key for your site.<br>";
        html += "<br>";
        html += "Developers: For help getting this working correctly, ";
        html += "<a href='http://trac.openlayers.org/wiki/Google' "
        html +=  "target='_blank'>";
        html +=     "click here";
        html += "</a>";
        
        return html;
    },


    /************************************
     *                                  *
     *   MapObject Interface Controls   *
     *                                  *
     ************************************/


  // Get&Set Center, Zoom

    /** Set the mapObject to the specified center and zoom
     * 
     * @param {Object} center MapObject LonLat format
     * @param {int} zoom MapObject zoom format
     */
    setMapObjectCenter: function(center, zoom) {
        this.mapObject.setCenter(center, zoom); 
    },
   
    /**
     * @returns the mapObject's current center in Map Object format
     * @type Object
     */
    getMapObjectCenter: function() {
        return this.mapObject.getCenter();
    },

    /** 
     * @returns the mapObject's current zoom, in Map Object format
     * @type int
     */
    getMapObjectZoom: function() {
        return this.mapObject.getZoom();
    },


  // LonLat - Pixel Translation
  
    /** 
     * @param {Object} moPixel MapObject Pixel format
     * 
     * @returns MapObject LonLat translated from MapObject Pixel
     * @type Object
     */
    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
        return this.mapObject.fromContainerPixelToLatLng(moPixel);
    },

    /** 
     * @param {Object} moPixel MapObject Pixel format
     * 
     * @returns MapObject Pixel translated from MapObject LonLat
     * @type Object
     */
    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
        return this.mapObject.fromLatLngToContainerPixel(moLonLat);
    },

  
  // Bounds
  
    /** 
     * @param {Object} moBounds MapObject Bounds format
     * 
     * @returns MapObject Zoom for specified MapObject Bounds
     * @type Object
     */
    getMapObjectZoomFromMapObjectBounds: function(moBounds) {
        return this.mapObject.getBoundsZoomLevel(moBounds);
    },

    /************************************
     *                                  *
     *       MapObject Primitives       *
     *                                  *
     ************************************/


  // LonLat
    
    /**
     * @param {Object} moLonLat MapObject LonLat format
     * 
     * @returns Longitude of the given MapObject LonLat
     * @type float
     */
    getLongitudeFromMapObjectLonLat: function(moLonLat) {
        return moLonLat.lng();  
    },

    /**
     * @param {Object} moLonLat MapObject LonLat format
     * 
     * @returns Latitude of the given MapObject LonLat
     * @type float
     */
    getLatitudeFromMapObjectLonLat: function(moLonLat) {
        return moLonLat.lat();  
    },
    
    /**
     * @param {int} lon float
     * @param {int} lat float
     * 
     * @returns MapObject LonLat built from lon and lat params
     * @type Object
     */
    getMapObjectLonLatFromLonLat: function(lon, lat) {
        return new GLatLng(lat, lon);
    },

  // Pixel
    
    /** 
     * @param {Object} moPixel MapObject Pixel format
     * 
     * @returns X value of the MapObject Pixel
     * @type int
     */
    getXFromMapObjectPixel: function(moPixel) {
        return moPixel.x;
    },

    /** 
     * @param {Object} moPixel MapObject Pixel format
     * 
     * @returns Y value of the MapObject Pixel
     * @type int
     */
    getYFromMapObjectPixel: function(moPixel) {
        return moPixel.y;
    },

    /** 
     * @param {int} x
     * @param {int} y
     * 
     * @returns MapObject Pixel from x and y parameters
     * @type Object
     */
    getMapObjectPixelFromXY: function(x, y) {
        return new GPoint(x, y);
    },

    /** @final @type String */
    CLASS_NAME: "OpenLayers.Layer.GoogleKMZ"
});
/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
 * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
 * for the full text of the license. */


/**
 * @class
 * 
 * @requires OpenLayers/Control.js
 */
OpenLayers.Control.MyKeyboard = OpenLayers.Class.create();
OpenLayers.Control.MyKeyboard.prototype = 
  OpenLayers.Class.inherit( OpenLayers.Control, {

    /** @type int */
    slideFactor: 50,

    /**
     * @constructor
     */
    initialize: function() {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
    },
    
    /**
     * 
     */
    draw: function() {
        OpenLayers.Event.observe(document, 
                      'keypress', 
                      this.defaultKeyDown.bind(this));
    },
    
    /**
    * @param {Event} evt
    */
    defaultKeyDown: function (evt) {
//alert( evt.keyCode );
        switch(evt.keyCode) {
            case OpenLayers.Event.KEY_LEFT:
                this.map.pan(-50, 0);
                break;
            case OpenLayers.Event.KEY_RIGHT: 
                this.map.pan(50, 0);
                break;
            case OpenLayers.Event.KEY_UP:
                this.map.pan(0, -50);
                break;
            case OpenLayers.Event.KEY_DOWN:
                this.map.pan(0, 50);
                break;
        }
    },
    
    /** @final @type String */
    CLASS_NAME: "OpenLayers.Control.MyKeyboard"
});
/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
 * See http://svn.openlayers.org/trunk/openlayers/release-license.txt 
 * for the full text of the license. */


/**
 * @class
 * 
 * @requires OpenLayers/Layer/Markers.js
 */
OpenLayers.Layer.WFSMetadata = OpenLayers.Class.create();
OpenLayers.Layer.WFSMetadata.prototype = 
  OpenLayers.Class.inherit( OpenLayers.Layer.Markers, OpenLayers.Layer.HTTPRequest, {


    isBaseLayer: false,

    /** @type Array(OpenLayers.Feature) */
    features: null,

    /** @type OpenLayers.Feature */
    selectedFeature: null,


    DEFAULT_PARAMS: { service: "WFS",
                      version: "1.0.0",
                      request: "GetFeature",
                      typename: "docpoint",
bbox: "-90,-180,90,180"
/*                      bbox: "900000,2000000,1000000,2200000"*/
                    },

    loaded: false,
    metaData: null,


    /**
    * @constructor
    *
    * @param {String} name
    * @param {String} location
    */
    initialize: function(name, url, params, options) {
        var newArguments = new Array();
        //uppercase params
        params = OpenLayers.Util.upperCaseObject(params);
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, newArguments);

        var newArguments = new Array();
        //uppercase params
        newArguments.push(name, options);
        OpenLayers.Layer.Markers.prototype.initialize.apply(this, newArguments);

        OpenLayers.Util.applyDefaults(
                       this.params,
                       OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
                       );

        this.features = new Array();
    },

    display: function( display )
      {
      OpenLayers.Layer.prototype.display.apply( this, [ display ] );

      if ( display && ! this.loaded )
        {
        this.loaded = true;
        OpenLayers.loadURL( this.getFullRequestString( ), null, this, this.parseData );
        }
      },

    /**
     * 
     */
    destroy: function( )
      {
      this.clearFeatures( );
      this.features = null;
      OpenLayers.Layer.Markers.prototype.destroy.apply( this, arguments );
      },
       
    /** 
     * @param {OpenLayers.Bounds} bounds
     * @param {Boolean} zoomChanged
     * @param {Boolean} dragging
     */
    moveTo: function( bounds, zoomChanged, dragging )
      {
      OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply( this, arguments );
      OpenLayers.Layer.Markers.prototype.moveTo.apply( this, arguments );

      if ( zoomChanged )
        {
        for ( var i = 0; i < this.features.length; i ++ )
          { this.features[i].display = false; }
        for ( var i = 0; i < this.features.length; i ++ )
          {
//          if ( this.features[i].setPhotos )
            { this.features[i].setPhotos( ); }
          }
        }
      },

    /** Return from AJAX request
    *       
    * @param {} request
    */
    parseData: function( request )
      {
      var doc = request.responseXML;
        
      if ( !doc || request.fileType != "XML" )
        { doc = OpenLayers.parseXMLString( request.responseText ); }

      var resultFeatures = OpenLayers.Ajax.getElementsByTagNameNS( doc, "http://www.opengis.net/gml", "gml", "featureMember" );

      if ( this.loadAll )
        {
        this.resultFeatures = resultFeatures;
        OpenLayers.loadURL( this.metadataUrl, null, this, this.parseMetaData );
        }
      else
        { this.addResults( resultFeatures ); }
      },

    parseMetaData: function( request )
      {
      this.metaData = request;
      this.addResults( this.resultFeatures );
      },

    /**
     * @param {Object} results
     */
    addResults: function( results )
      {
      for ( var i = 0; i < results.length; i++ )
        {
        var feature = new this.featureClass( this, results[i], this.icon ? this.icon.clone( ) : null, this.metaData || null );
        this.features.push( feature );
        }

      this.map.events.triggerEvent( "changelayer" );

if ( this.activePhoto )
  { this.openLocation( this.activePhoto, "id" ); }
      },

    /**
     * 
     */
    clearFeatures: function() {
        if (this.features != null) {
            while(this.features.length > 0) {
                var feature = this.features[0];
                OpenLayers.Util.removeItem(this.features, feature);
                feature.destroy();
            }
        }        
    },

    openLocation: function( target, index )
      {
      var foundFeature = null;
      var id = 0;
      if ( index == null ) { index = "id"; }

      for ( var i = 0; i < this.features.length; i ++ )
        {
        var feature = this.features[i];
        if ( feature.photos )
          {
          for ( var j = 0; j < feature.photos.length; j ++ )
            {
            if ( feature.photos[j][index] == target )
              { foundFeature = feature; id = j; }
            }
          }
        }

      if ( foundFeature )
        {
        foundFeature.markerClick( null, id );
        if ( ! foundFeature.onScreen( ) ) { this.map.setCenter( foundFeature.lonlat ); }
        }

      },


    /** @final @type String */
    CLASS_NAME: "OpenLayers.Layer.WFSMetadata"
});
     
    

