/**
 * Madison Radiology Global Javascript File
 *
 * This is the global javascript file for the Madison Radiology
 * website. The only requirements of this script is the full 
 * MooTools <mootools.net> Javascript framework "core" and "more"
 * packages.
 */

/**
 * Google Maps API Wrapper Class
 *
 * Accepts a wrapper element ID and an address as initializer
 * arguments. Draws a map in the passwd wrapper element using
 * geocoder to translate the passwd address into lat/lng coords
 * and creates a marker with a start address form field to allow
 * for Google directions to the address pointed to on the map
 */
var GMaps = new Class({

  /**
   * initializer
   *
   * handles initialization tasks, including making sure the browser
   * is compatable with gmaps, creating the geocoder and gmap objects,
   * and setting gmap options
   */
  initialize: function(map_id, address) {
    
    if(!GBrowserIsCompatible()) return false;

    this.address = address;
		this.maplayer = $(map_id);

    this.geocoder = new GClientGeocoder();
		this.map = new GMap2(this.maplayer);
		this.map.addControl(new GLargeMapControl());
		this.map.addControl(new GMapTypeControl());
		this.map.addMapType(G_PHYSICAL_MAP); 
		
		this.showAddress(address);

  },

  /**
   * showAddress
   * 
   * accepts an address string as its single argument and uses gmaps
   * geocoder to translate the address into lat/long coords that the
   * gmaps api can use to draw a map of the location.
   */
	showAddress: function(address){
	  
		this.geocoder.getLatLng(address, function(point) {

		  if(point){
				this.map.setCenter(point, 14);
        marker = this.createMarker(point);
        this.createDirectionsInfoWindow(marker, address);
			}

		}.bind(this));
		
	},
	
	/**
	 * createMarker
	 *
	 * accepts a point as its only argument and creates a marker overlay
	 * at that location on the map
	 */
	createMarker: function(point) {
	  
	  var marker = new GMarker(point);
		this.map.addOverlay(marker);

		return marker;
		
	},
	
	/**
	 * createDirectionsInfoWindow
	 *
	 * accepts a GMarker instance as its first argument and an address
	 * as its second. creates an info window at the point where the 
	 * marker is located with a text field that accepts a start address.
	 * the second argument (address) is the end address
	 */
	createDirectionsInfoWindow: function(marker, address) {
	  
    var marker_html = '<div class="mapsInfoWindow"><h1>Get Directions</h1><form method="get" action="http://maps.google.com/maps"><label for="saddr">Start Address:</label><input class="mapsInfoWindowSaddr" type="text" name="saddr" value="" /><input type="hidden" name="daddr" value="'+address+'" /><input class="mapsInfoWindowSubmit" type="submit" value="Map" name="submit" /></form></div>';
    marker.openInfoWindowHtml(marker_html);

    this.addMarkerEvent(marker, 'click', marker_html);
    
	},
	
	/**
	 * addMarkerEvent
	 *
	 * adds a GEvent listener to a marker point. the first argument is the
	 * marker, the second argument is the event type (ex. "click"), and the
	 * third argument is the html for the info window to open on event
	 */
	addMarkerEvent: function(marker, event_type, info_window_html) {
	  
	  GEvent.addListener(marker, event_type, function() {
		  marker.openInfoWindowHtml(info_window_html);
		});
		
	}

});

/**
 * Locations Tabbed Content Class
 *
 * handles switching the main content based on the activated location tab,
 * including handling effects associated with this operation and the
 * creation of a gmaps object for each location
 */
var LocationsTabs = new Class({
  
  /* implements options class */
  Implements: [Options],

  /* default object options values */
  options: {
    'defaultOpen': 0,
    'lowOpacity': .5,
    'highOpacity': 1
  },
  
  /* tabs array */
  tabs: [],
  
  /* containers array */
  containers: [],
  
  /* currently open container index */
  open_index: 0,
  
  /**
   * initializer
   *
   * accepts a tabs and containers selector as the first and second arguments
   * and optionally, an options array as the third. handles setting up fx, 
   * events, and initial state
   */
  initialize: function(tabs, containers, options) {
    
    this.setOptions(options);
    
    this.tabs = $$(tabs);
    this.containers = $$(containers);
    
    this.setupTabFxs();
    this.setupContainerFxs();
    this.setupInitialState();
    
  },
  
  /**
   * setupTabFxs
   *
   * handles setting up tab effects and event(s)
   */
  setupTabFxs: function() {
    
    this.tabs.each(function(li, i) {
      
      var fx = new Fx.Morph(li, {
        duration: 250,
        link: 'cancel',
        transition: Fx.Transitions.Quad.easeOut
      });
      
      li.store('fx', fx);
      li.addEvent('mouseenter', this.handleTabHover.bind(this));

      if(li.hasClass('defaultLocationOpen')) { this.options.defaultOpen = i; }
      
      this.contractTab(i);
        
    }.bind(this));

  },
  
  /**
   * setupContainerFxs
   *
   * handles hiding the container elements
   */
  setupContainerFxs: function() {
    
    this.containers.each(function(li, i){
      li.hide();
    });
    
  },
  
  /**
   * setupInitialState
   *
   * handles expanding the tab and showing the content for the index
   * specified in options as defaultOpen
   */
  setupInitialState: function() {
    
    i = this.options.defaultOpen;
    this.expandTab(i);
    this.handleContainerActive(i, i);
    this.open_index = i;
    
  },
  
  /**
   * handleTabHover
   *
   * function tied to the event for entering the tab space (mouseenter)
   * handles contracting and expanding tabs and showing the correct content
   * takes the event object as the only peramiter
   */
  handleTabHover: function(e) {
    
    index = this.tabs.indexOf(e.target);
    if(index == -1) { return; }
    var open_index = this.open_index;

    if(open_index != index) {
      this.contractTab(open_index);
    }
    
    this.expandTab(index);    
    this.handleContainerActive(index, open_index);
    
    this.open_index = index;

  },

  /**
   * handleContainerActive
   *
   * accepts the index of the most-recently activated tab and the index of the 
   * last open tab. Using this information it hide/shows the correct containers
   */
  handleContainerActive: function(index, open_index) {
    
    if(open_index != index) {
      this.hideContainer(open_index);
    }
    this.showContainer(index);
    
    this.drawGMap(index);
    
  },

  /**
   * hideContainer
   * 
   * accepts the container index as the only argument. uses the native
   * Element object method "hide" to hide the container
   */
  hideContainer: function(index) {
    
    this.containers[index].hide();
    
  },

  /**
   * showContainer
   *
   * accepts the index of the container to show. uses the native
   * Element object method "show" to show the container
   */
  showContainer: function(index) {

    this.containers[index].show();
        
  },
  
  /**
   * drawMap
   *
   * accepts the current index as the only argument. handles drawing 
   * the gmaps using the GMaps wrapper class
   */
  drawGMap: function(index) {

    var drawMap = function() {
      map_container = this.containers[index].getLast();
      
      map_inner_container = map_container.getChildren();
      if(map_inner_container.length != 0) { return; }
      
      map_inner_container = new Element('div').setProperty('class', 'locationDetailsInnerMap').inject(map_container);
      address = this.containers[index].getElement('.locationDetailsAddress address').get('text');
      
      new GMaps(map_inner_container, address);
    }.bind(this);

    drawMap.delay(300);

  },
  
  /**
   * expandTab
   *
   * highlight (expand) active tab
   */
  expandTab: function(index) {
    
    this.tabs[index].fade(this.options.highOpacity);
    this.tabs[index].retrieve('fx').start('.locationsListItemSelected'); 
    
  },
  
  /**
   * contractTab
   *
   * de-highlight (contract) unactive tab
   */
  contractTab: function(index) {
    
    this.tabs[index].fade(this.options.lowOpacity);
    this.tabs[index].retrieve('fx').start('.locationsListItem');
    
  }

});

/**
 * HeaderNavFxs
 *
 * handles the hover effects for the main navigation bar
 */
var HeaderNavFxs = new Class({
  
  /* this class implements the Options class */
  Implements: [Options],
  
  /* options array */
  options: {
    'mouseenterClass': 'mootools_headerNavMouseenter',
    'mouseleaveClass': 'mootools_headerNavMouseleave'
  },
  
  /* the nav anchors */
  elements: [],
  
  /* the nav list items */
  elements_li: [],
  
  /* current page index */
  current_page_index: 0,

  /**
   * initializer
   *
   * accepts the nav anchors as the first argument
   */
  initialize: function(elements, options) {
    
    this.setOptions(options);
    
    this.elements = $$(elements);
    this.elements_li = this.elements.getParent();
    this.elements_topparent = this.elements_li.getParent();
    this.elements_topparent = this.elements_topparent[0];
    
    this.setupElementFxsAndEvents();
    this.setupInitialState();
    
  },
  
  /**
   * setupElementFxsAndEvents
   *
   * handles setting up fx and events for the nav anchor elements
   */
  setupElementFxsAndEvents: function() {
    
    this.elements.each(function(element, index) {
      
      fx = new Fx.Morph(element, {
        'duration': 200,
        'link': 'cancel'
      });
      
      element.store('fx', fx);
      
      element.addEvent('mouseenter', this.handleMouseenter.bind(this));
      element.addEvent('mouseleave', this.handleMouseleave.bind(this));
      
    }.bind(this)); 
    
  },
  
  setupInitialState: function() {
    
    this.elements_li.each(function(element_li, index) {
      
      if(element_li.hasClass('currentPage')) {
        this.current_page_index = index;
        this.mouseenterFx(index);
      }
      
    }.bind(this));
    
  },
  
  /**
   * handleMouseenter
   *
   * handles starting mouseenter fxs
   */
  handleMouseenter: function(event) {
    target = event.target;
    index = this.elements.indexOf(target);
    this.mouseenterFx(index);
  },

  /**
   * handleMouseleave
   *
   * handles reverting the element state back to normal when the
   * mouseleave event is triggered
   */
  handleMouseleave: function(event) {
    target = event.target;
    index = this.elements.indexOf(target);
    if(this.current_page_index == index) { return; }
    this.mouseleaveFx(index);    
  },
  
  /**
   * mouseenterFx
   *
   * starts the fx for a mouseenter event
   */
  mouseenterFx: function(index) {
    
    fx = this.elements[index].retrieve('fx');
    fx.start('.'+this.options.mouseenterClass);
    
  },
  
  /**
   * mouseleaveFx
   *
   * starts the fx for a mouseleave event
   */
  mouseleaveFx: function(index) {

    fx = this.elements[index].retrieve('fx');
    fx.start('.'+this.options.mouseleaveClass);
    
  }

});

/**
 * HomePageLocationHover
 *
 * handles the hover effects on the locations on the home page
 * to direct users to the locations page
 */
var HomePageLocationHover = new Class({

  /**
   * initializer
   *
   * accepts the list items as the first argument
   */
  initialize: function(elements) {
    
    this.elements = $$(elements);
    this.setupElements();
    
  },
  
  /**
   * setupElementFxsAndEvents
   *
   * handles setting up events for the elements and inserts the 
   * rollover text in paragraph elements
   */
  setupElements: function() {
    
    this.elements.each(function(element, index) {
      
      tipsTitle = function(element) { return 'Click For More Information' }
      tipsText = function(element) { return '<em>Click</em> to view additional details including the <em>address</em>, <em>directions</em>, <em>phone</em>, and <em>pictures</em> of this location.' }
    
      new Tips(element, {
        'showDelay': 0,
        'hideDelay': 0,
        'title': tipsTitle,
        'text': tipsText,
        'className': 'tip-container',
        'offset': {'x': 10, 'y': 20}
      });

      element.addEvent('click', this.handleClick.bind(this));
      
    }.bind(this)); 
    
  },
  
  /**
   * handleMouseenter
   *
   * handles click event
   */
  handleClick: function(event) {

    target = event.target;
    index = this.elements.indexOf(target);
    if(index == -1) {
      target = target.getParent('li');
      index = this.elements.indexOf(target);
    }
    
    window.location = "/locations.php?default_index="+index;

  }

});

/**
 * AlignFooterBottom
 *
 * aligns the footer to the bottom of the page unless the content continues
 * below beyond the bottom of the page, at which point the footer is set to 
 * a display of relative
 */
var AlignFooterBottom = new Class({

  /**
   * initialize
   *
   * sets up window resize event and handles initial footer formatting
   */
  initialize: function(footer_el) {
   
    this.setupWindowEvent();
    this.handleWindowResize();
    
  },

  /**
   * setupWindowEvent
   *
   * sets up the window resize event
   */
  setupWindowEvent: function() {
    
    window.addEvent('resize', this.handleWindowResize.bind(this));
    
  },

  /**
   * handleWindowResize
   *
   * handles the window resize functions that must be called when the window
   * resize event is called 
   */
  handleWindowResize: function() {
    
    this.getRelivantHeights();
    this.handleFooterPlacement();
    
  },
  
  /**
   * getRelivantHeights
   *
   * gets the relivant heights for making the calculations to place the footer appropriatly
   */
  getRelivantHeights: function() {
    
    this.content_height = (($('ContentMain').getSize().y > $('Content').getSize().y) 
      ? $('ContentMain').getSize().y : $('Content').getSize().y);
    this.header_height = $('Header').getSize().y;
    this.footer_height = $('Footer').getSize().y;
    this.window_height = window.getSize().y;
    this.top_height = this.header_height + this.content_height;
    
  },

  /**
   * handleFooterPlacement
   *
   * handle deciding how the footer should be places and set the styles to do so
   */
  handleFooterPlacement: function() {
    
    if((this.top_height + this.footer_height) > this.window_height) {
      $('Footer').setStyles({
        'position': 'relative'
      });
    } else {
      $('Footer').setStyles({
        'position': 'absolute',
        'bottom': '0px',
        'width': '944px'
      });
    }

  }
  
});

/**
 * add "domready" event to window to execute javascript only when the dom
 * structure has been completly loaded 
 */
window.addEvent('domready', function () {

  /* header hover effects */
  if($('HeaderNav')) new HeaderNavFxs('#HeaderNav ul li a');
  
  /* locations page tab/maps functionality */
  if($('LocationsList')) new LocationsTabs('#LocationsList li', '#LocationDetails li');
  
  /* home page location hover fx (tooltip) */
  if($('HomeLocationListing')) new HomePageLocationHover('#HomeLocationListing li');
  
  /* accordion effects for some sections of the site */
  if($('Accordion')) {
    new Fx.Accordion('#Accordion h3', '#Accordion div', {
      display: -1,
      onActive: function(toggler, element) { toggler.removeClass('background'); toggler.addClass('active'); },
      onBackground: function(toggler, element) { toggler.addClass('background'); toggler.removeClass('active'); }
  })};
  
  /* push footer to bottom of page if content is not large enough */
  //if($('Footer')) new AlignFooterBottom('#Footer');
  
/*
User-Agent: *
Allow: / 
*/

});