﻿ /**
 * This section extends (or create in IE) the NodeList object
 * to allow for enumeration of read-only node lists (element.childNodes, etc)
 * A NodeList differs from an Array in a couple ways.
 * - It is specifically designed to hold Nodes (XML nodes or HTML elements)
 * - It is read only
 * - It's much faster
 */
function IENodeList(node_list){
	var _real_node_list;
	this._real_node_list = node_list;
};
NodeList_Extensions = {
	elements:	function() {
					return this.inject([], function(elementNodes, element) {
					  if(element.nodeType == Node.ELEMENT_NODE)
						elementNodes.push(element);
					  return elementNodes;
					})
				},
	firstElement:	function(){
					return this.elements().first();
				},
	lastElement:	function(){
					return this.elements().last();
				},
	inspect:	Array.prototype.inspect				
};
Object.extend(IENodeList.prototype, Enumerable);
Object.extend(IENodeList.prototype, {
	_each:	function(iterator) {
		for (var i = 0; i < this._real_node_list.length; i++)
			iterator(this._real_node_list[i]);
	},
	first:		function(){ return this._real_node_list[0] },
	last:		function(){ return this._real_node_list[this._real_node_list.length - 1] },
	indexOf:	function(object) {
				for (var i = 0; i < this._real_node_list.length; i++)
					if (this._real_node_list[i] == object) return i;
				return -1;
			}
});
Object.extend(IENodeList.prototype, NodeList_Extensions);

if(typeof NodeList != 'undefined'){
	Object.extend(NodeList.prototype, Enumerable);	
	Object.extend(NodeList.prototype, {
		_each:		Array.prototype._each,
		first:		Array.prototype.first,
		last:		Array.prototype.last,
		indexOf:	Array.prototype.indexOf
	});
	Object.extend(NodeList.prototype, NodeList_Extensions);
}
/**
 * Build a browser independent NodeList object from a node list.
 * This does nothing in FireFox, but supplies a bunch of fixes in IE.
 */
function $NL(node_list){

	if(typeof IENodeList != 'undefined' || typeof node_list != 'NodeList'){
		return new IENodeList(node_list);
	}
	else{
		return node_list;
	}
}
/**
 * @constructor
 * This Class provides static methods to find the relative postion based on the current browser.
 */
var Dom = function() {
};
/**
  * Static Method finds the left most X-axis coordinate of the given object.
  * @param {Object} Object or String id of the DOM desired
  * @param topNode Object or String id to declare the top node to traverse too.
  * measurment will include the topNode
  * @return int - X-axis coordinate
  * @member Dom
  */  
Dom.getLeftX = function(id, topNode) {
	var obj = $(id);
	topNode = (topNode) ? $(topNode) : topNode;
  var curleft = 0;
  
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
			if(topNode && obj == topNode)
			  break;
		}
	} else if (obj.x)
		curleft += obj.x;
	return curleft;
}
Dom.getLeftXAsPx = function(id, topNode) {
	return Dom.getLeftX(id, topNode) + "px";
}
/**
 * Static Method finds the right most X-axis coordinate of the given object.
 * @param {Object} id Object or Id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return int - X-axis coordinate
 * @member Dom
 */
Dom.getRightX = function(id, topNode) {
	var obj = $(id);
  
  return Dom.getLeftX(obj, topNode) + obj.offsetWidth;
}
Dom.getRightXAsPx = function(id,topNode) {
	return Dom.getRightX(id,topNode) + "px";
}
 /**
 * Static Method finds the bottom most Y-axis coordinate of the given object.
 * @param {Object} Object or String id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return int - Y-axis coordinate
 * @member Dom
 */ 
Dom.getBottomY = function(id,topNode) {
	var obj = $(id);
	  
	return Dom.getTopY(obj,topNode) + obj.offsetHeight;
}
Dom.getBottomYAsPx = function(id,topNode) {
	return Dom.getBottomY(id,topNode) + "px";
}
 /**
 * Static Method finds the top most Y-axis coordinate of the given object.
 * @param {Object} Object or String id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return int - Y-axis coordinate
 * @member Dom
 */ 
Dom.getTopY = function(id,topNode) {
	var obj = $(id);
	topNode = $(topNode)
	var curtop = 0;
	var index = 0;
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			index++;
			curtop += obj.offsetTop		  
			obj = obj.offsetParent;
			if(topNode && obj == topNode)
			  break;
		}
	} else if (obj.y)
	curtop += obj.y;
	return curtop;
}
Dom.getTopYAsPx = function(id,topNode) {
	return Dom.getTopY(id,topNode) + "px";
}
/**
 * Static Method finds the Left, Right, Top, Bottom positions of a certain object
 * and returns an object with 4 properties: <br />
 * .leftX <br />
 * .rightX <br />
 * .topY <br />
 * .bottomY <br />
 * @param {Object} id Object or String id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return Object - with 4 properties .leftX, .rightX, .topY, bottomY
 * @member Dom
 */
Dom.getBoundries = function(id,topNode) {

	 var boundries = {
		leftX:   Dom.getLeftX(id,topNode),
		rightX:  Dom.getRightX(id,topNode),
		topY:    Dom.getTopY(id,topNode),
		bottomY: Dom.getBottomY(id,topNode),
		width: Dom.getRightX(id,topNode) - Dom.getLeftX(id,topNode),
		height: Dom.getBottomY(id,topNode) - Dom.getTopY(id,topNode)		
	}
	return boundries;	
}
Dom.isInBounds = function(id, x, y) {
	var rectangle = Dom.getBoundries(id);
	
	if(x<=rectangle.leftX || x>=rectangle.rightX)
	  return false;
	if(y<=rectangle.topY || y>=rectangle.bottomY)
	  return false;
	
	return true;
}
//TODO This code has been moved into prototype and should be deleted once we are
// sure there are no other users out there on the site.
function Node() {}
Node.ELEMENT_NODE = 1; 
Node.ATTRIBUTE_NODE = 2; 
Node.TEXT_NODE = 3; 
Node.CDATA_SECTION_NODE = 4; 
Node.ENTITY_REFERENCE_NODE = 5; 
Node.ENTITY_NODE = 6; 
Node.PROCESSING_INSTRUCTION_NODE = 7; 
Node.COMMENT_NODE = 8; 
Node.DOCUMENT_NODE = 9; 
Node.DOCUMENT_TYPE_NODE = 10; 
Node.DOCUMENT_FRAGMENT_NODE = 11; 
Node.NOTATION_NODE = 12;

Node.getFirstElementNode = function(elements) {
	// Hack for Safari's issue with converting to prototype's $A() for childNodes
	var array = new Array();
	for(var i=0; i<elements.length; i++) {
		array.push(elements[i]);
	}
	elements = $A(array);
	return elements.find(function(element) {	
      if(element.nodeType == Node.ELEMENT_NODE)
  		  return element;	
    });
}
Node.getAllElementNode = function(elements) {
   	// Hack for Safari's issue with converting to prototype's $A() for childNodes
    var array = new Array();
	for(var i=0; i<elements.length; i++) {
		array.push(elements[i]);
	}
	return $A(array).inject($(), function(elementNodes, element) {	
      if(element.nodeType == Node.ELEMENT_NODE)
  	    elementNodes.push(element);
      return elementNodes;
    })
}
function isMacBrowser() {
	if(navigator.userAgent.toLowerCase().indexOf('mac') > -1) {
		return true;	
	}
	return false;
}

var isSelectHidden = false;
var selectsFound;
function disableAllSelectElementsInIE() {
	if (!isSelectHidden && hideSelectElements()){
		
		if(!selectsFound) {
			var selectObjs = document.getElementsByTagName('SELECT');
			selectsFound = new Array();
			
			for(var i=0; i<selectObjs.length; i++) {
				selectsFound.push(selectObjs[i]);
			}
		}
		for(var i=0; i<selectsFound.length; i++) {
			selectsFound[i].style.visibility = 'hidden';
		}
		isSelectHidden = true;		
	}
}
function enableAllSelectElementsInIE() {
	
	if (isSelectHidden && selectsFound){
		for(var i=0; i<selectsFound.length; i++) {
				selectsFound[i].style.visibility = 'visible';	
		}
		isSelectHidden = false;
	}
		
}
function hideSelectElements() {
	if(document.all) {
		return true;	
	}
	return false;
}
function hideElementsForOverlays() {
	disableAllSelectElementsInIE();
}
function showElementsForOverlays() {
	enableAllSelectElementsInIE();
}
function Address() {}
Address.getParameters = function() {
	var params = new Object;
	var search = window.location.search.replace('?','');
	if(search != '') {
		var items = search.split('&');
		
		for(var i=0; i<items.length; i++) {
			var keyValue = items[i].split('=');
			
			params[keyValue[0]] = keyValue[1];
		}
	}
	return params;
}
function hasObject(parent, objectStr) {
	
	if(parent[objectStr])
		return true;
	else
		return false;
}
	/*
 * Site Constants  
 */
 menuTimeout = 250;
 IMG_ROOT = '/images/';
 IMG_SHARE = '/images/'
 var PAGE_ID = '';
 var MODEL_ID = '';
 var PAGE_LOAD = false;
/**
 * Function when called will find all Image tags and cache all images and any Over states that follows a given naming convention.
 * Such as xxxxxx.gif and xxxxxxOver.gif 
 * @type void
 */
DebugOnloadEventTiming = Class.create();
Object.extend(DebugOnloadEventTiming.prototype, {
	initialize: function() {
		this.startTime;
		this.endTime;
	},
	startTime: function() {
		this.startTime = new Date();
	},
	endTime: function() {
		this.endTime = new Date();
	},
	getElapseTime: function() {
		return this.endTime.getTime() - this.startTime.getTime();
	},
	printResults: function() {
		Debug.debug('onLoad Events Start Time:  ' + this.startTime.getDay() + '/' + this.startTime.getMonth() + '/' + this.startTime.getYear()
									   + ' ' + this.startTime.getHours() + ':' + this.startTime.getMinutes() + ':' + this.startTime.getSeconds()+ ':' + this.startTime.getMilliseconds() );
									   
		Debug.debug('onLoad Events End Time:  ' + this.endTime.getDay() + '/' + this.endTime.getMonth() + '/' + this.endTime.getYear()
									   + ' ' + this.endTime.getHours() + ':' + this.endTime.getMinutes() + ':' + this.endTime.getSeconds()+ ':' + this.endTime.getMilliseconds() );								   

		Debug.debug('onLoad Events Total Elapse Time:  ' + this.getElapseTime() + '(' + (this.getElapseTime()/1000) + ' seconds)');		
	}
});
var debugOnloadEventTiming = new DebugOnloadEventTiming();
OnPageLoadEvents = Class.create();
Object.extend(OnPageLoadEvents.prototype, {
	initialize: function(){
		this.actions = new Array();
		Event.observe(window, 'load', function(){this._executeActions()}.bind(this), false);
	},
	addAction: function(func) {
		
		this.actions.push(func);
	}, 
	_executeActions: function() {
		for(var i=0; i<this.actions.length; i++) {
			var action = this.actions[i];
			action.apply(window,[]);
		}
	}	
});
var onPageLoadEvents = new OnPageLoadEvents();

onPageLoadEvents.addAction(function() {debugOnloadEventTiming.startTime()});
onPageLoadEvents.addAction(function() {PAGE_LOAD = true;});

function debugOnloadStartTime() {
	var startTime
	
}
/**
*	Replaces ~search_string~ with replace_string in input_string.
*	Returns result.
*/

Object.extend(String.prototype, {
	templateReplace: function(search_string, replace_string){
		//var regEx = new RegExp('~' + search_string + '~', 'g')
		var output = this.replace('~' + search_string + '~', replace_string);

		return output;
	}
});
function ProductCategory(id, name) {
	this.id = id;
	this.name = name;
	this.models = $A();
	this.domHotSpot = null;
	this.domNavigation = null;
	this.domOverlay = null;

  /**
   * Method adds a ProductModel Object to an array.   
   * @param model ProductModel Model Object representing a Model
   * @type void
   * @member ProductCategory
   */
  this.addModel = function(model) {
  	  model.categoryId = this.id;
    	this.models.push(model);
  }
   /**
   * Method returns a ProductModel Object from the current array. 
   * @param model ProductModel Model Object representing a Model
   * @type ProductModel
   * @member ProductCategory
   */
  this.getModel = function(key) {
  	return this.models.find(function(model){
  		if(model.id.indexOf(key) >= 0)
  		  return model;
  	})
  }
}
ProductCategory.prototype.toString = function() {
	return "Category (Object)";
}
/** Type Declarations Class/Instance **/
ProductCategory.type = 'Category';
ProductCategory.prototype.type = 'Category';
/*******************************************************************************/
/**
 * Class holds the needed information for a paticular Product/Model
 * @constructor
 */
function ProductModel(id, name, startingPrice, endingPrice,code) {
	this.id = id.toLowerCase();
	this.name = name;
	this.startingPrice = startingPrice;
	this.endingPrice = endingPrice;
	this.code = code;
	this.categoryId = 0;
	this.features = $A();
	this.domHotSpot = null;
	this.domNavigation = null;
	this.domOverlay = null;
	
	/**
   * Method adds a VehicleFeature Object to an array.
   * @param feature VehicleFeature VehicleFeature Object representing a Model Feature
   * @type void
   * @member ProductModel
   */
	this.addFeature = function(feature) {
		this.features.push(feature);
	}
	/**
	 * Method returns a string representing the price of the  Model
	 * @type String
	 * @member ProductModel
	 */
	this.getPriceAsString = function() {
		return "" // "Starting at " + this.startingPrice + 
		//       ((this.endingPrice) ? " - " + this.endingPrice : "" ) + ' ';
	}
}
ProductModel.prototype.toString = function() {
	return "Model (Object)";
}
/** Type Declarations Class/Instance **/
ProductModel.type = 'Model';
ProductModel.prototype.type = 'Model';

/*******************************************************************************/

/**
 * Class holds current State of the navigation<br/>
 * @constructor
 */
function ProductNavigationState () {
	this._navShowing = false;
	this._categoryShowing = false;
	this._modelShowing = false;
	this._currentActive = null;

	/**
	 * @member ProductNavigationState
	 * Method sets the current state of the navigation.
	 * If false, method will also update categoryShowing and modelShowing attributes
	 * @param boolean - state True/False 
	 */
	this.setNavShowing = function (state) {
		this._navShowing = state;
		
		// update dependencies if false
		if(!state) {
			this.setCategoryShowing(false);
			this.setModelShowing(false);
			this.setCurrentActive(null);
		}
	}
	/**
	 * 
	 * Method sets categoryShowing attribute, to indicate if a Category Navigation is current visible.
	 * If True, will also set _navShowing to true and will set _modelShowing to false, since only a
	 * model or category can be shown at any point.
	 * @param boolean - state True/False 
	 * @member ProductNavigationState
	 */
	this.setCategoryShowing = function (state) {
		this._categoryShowing = state;
		
		// update dependencies if true
		if(state) {
			this.setNavShowing(true);
			this.setModelShowing(false);
		}
	}
	/**
	 * Method sets modelShowing attribute, to indicate if a Model Navigation is current visible.
	 * If True, will also set _navShowing to true and will set _categoryShowing to false, since only a
	 * model or category can be shown at any point.
	 * @param boolean - state True/False 
	 * @member ProductNavigationState
	 */
	this.setModelShowing = function (state) {
		this._modelShowing = state;
		
		// update dependencies if true
		if(state) {
			this.setNavShowing(true);
			this.setCategoryShowing(false);
		}
	}
  /**
	 * Method sets the current object that is active on the vehicle navigation
	 * @param object - Category or Model Object
	 * @member ProductNavigationState
	 */
	this.setCurrentActive = function(object) {
		this._currentActive = object;
		
		if(!object)
		  return;
	  if(object.type == ProductCategory.type) {
	  	this.setCategoryShowing(true);	  	
	  }
	  else {
	  	this.setModelShowing(true);	  	
	  }
	}
  /**
	 * Method gets the current object that is active on the vehicle navigation
	 * @type object - Category or Model Object
	 * @member ProductNavigationState
	 */
	this.getCurrentActive = function() {
		return this._currentActive;		
	}
	/**
	 * Method returns true/false depending on if a vehicle navigation is current showing
	 * @type boolean
	 * @member ProductNavigationState
	 */
	this.isNavShowing = function() {
		return this._navShowing;
	}
	/**
	 * Method returns true/false depending on if a vehicle Category navigation is current showing
	 * @type boolean
	 * @member ProductNavigationState
	 */
	this.isCategoryShowing = function() {
		return this._categoryShowing;
	}
	/**
	 * Method returns true/false depending on if a vehicle model navigation is current showing
	 * @type boolean
	 * @member ProductNavigationState
	 */
	this.isModelShowing = function() {
		return this._modelShowing;
	}
}
/*******************************************************************************/
/**
 * Class acts as the controller to handle all interaction of the navigation bar<br/>
 * @constructor
 */
function ProductNavigationManager() {
	this.categories = $A();
	this.navState = new ProductNavigationState();
    this.navView = new VehicleNavigationView();
	this.timeOutId;
	this.timeOutSet = false;
	this.ajaxQueue;
	this.imageCache = new Array();
	
	this.backdrop;	

	/**
	 * Method will be called on instantiation of this class.
	 * Exact features are TBD
	 * @member ProductNavigationManager
	 */
	this.init = function() {
	this._setOnState();	
      	 // Add onload event
	 onPageLoadEvents.addAction(this.init_onload.bindAsEventListener(this));
	 Event.observe(window, 'unload', Event.unloadCache);
		}
	this.init_onload = function() {
	  this._setupNavigation();
	  this._cacheMenuImages();
      this._preLoadMenus();  
      	}
	/**
   * Method adds a Category Object to an array.   
   * @param model - Category Category Object representing a Category
   * @type void
   * @member ProductNavigationManager
   */
  this.addCategory = function(category) {
    this.categories.push(category);
      }
  /**
   * Method returns a Category Object from the current array.
   * @param model - Category Category Object representing a  Category
   * @type void
   * @member ProductNavigationManager
   */
   
  this.getCategory = function(key) {
    
  	return this.categories.find(function(category){
  		//if(category.id.indexOf(key.toUpperCase()) >= 0) {
  		 return category;
  		//}
  	})
  }
  /**
   * Function will control the action of showing a Model or Category Vehicle Navigation Item
   * @param domObject Dom Element
   * @param categoryId String 
   * @param modelId String
   * @type void
   * @member ProductNavigationManager
   */
  this.showNavigation = function (domObject, categoryId, modelId) {
  	var dataObject = this._getDataObject(categoryId, modelId);
  	if(dataObject == null)
  	  return;
  	if(this.timeoutSet) {
  		clearTimeout(this.timeoutId);
  		this.timeoutSet = false;
  	}
	//this.closeDisclaimer();
	
  	// attach Dom Hot Spot if not already done.
  	if(!dataObject.domHotSpot)
  	  dataObject.domHotSpot = domObject;
  	if(this.navState.isNavShowing()) {
  	  if(this.navState.getCurrentActive() == dataObject)
  	    return;
  	  else
  		this.hideNavigation();
  	}
  	// Hide all flash objects in safari
  	hideElementsForOverlays();
  	if(dataObject.type == ProductCategory.type) {
  		this._showCategoryNavigation(dataObject);
  	}
  	else {
  		/*
  		 * IE needs to have an expliced hide on the body to make sure
  		 * the navigation actually hides when you roll off the menu
  		 */
        if(document.all)
  		  Event.observe(document.body, 'mouseout', ieBodyhideNavigation, true);
  		  
  		this._showModelNavigation(dataObject);
  	}
  }
  /**
   * Method will control the action to hide a Model or Category Vehicle Navigation Item.
   * @param eventObject Event
   * @type void
   * @member ProductNavigationManager
   */
  this.hideNavigation = function (eventObject) {
  	var currentActive = this.navState.getCurrentActive();
  	// Only Hide if its ok
  	if(!this._shouldWeHideNavigation(eventObject))
  	  return;
    
    var wrapper = $('wrapper');
    // Hide Navigation
    currentActive.domNavigation.style.display = 'none';
    currentActive.domOverlay.style.display = 'none';
    
    /* 
     * Removed from the actual DOM because IE has issues displaying if we dont remove,
     * since these elements could be cloned.
     */
    wrapper.removeChild(currentActive.domNavigation);
    wrapper.removeChild(currentActive.domOverlay);    
    /*
     * Removed IE specific body event
  	 * 
  	 */
    if(document.all)
      Event.stopObserving(document.body, 'mouseout', ieBodyhideNavigation, true);
      
    this.navState.setNavShowing(false);
    
    // Show all flash objects in safari
  	//showElementsForOverlays();
  }
  /**
   * Method will activate a timer and execute hideNavigation().
   * @member ProductNavigationManager
   */
  this.hideNavigationWithTimeOut = function(eventObject) {
    if(this.timeoutSet)
      return;
    // Only Hide if its ok
  	if(!this._shouldWeHideNavigation(eventObject))
  	  return;

  	this.timeoutId = setTimeout("Navigator.hideNavigation()",menuTimeout);
  	this.timeoutSet = true;
  }
   /**
   * Method will figure out whether or not to hide the current openned navigation
   * @member ProductNavigationManager
   */
  this._shouldWeHideNavigation = function (eventObject) {
  	var currentActive = this.navState.getCurrentActive();
  	
  	// Nothing to do if we dont have a nav showing
  	if(!currentActive || !currentActive.domNavigation || !currentActive.domOverlay)
  	  return false; 	
  	// If in bounds, do nothing
  	if(this.isEventInBounds(currentActive.domOverlay, eventObject) || this.isEventInBounds(currentActive.domNavigation, eventObject))
  	  return false;
  	return true;
  }
  /**
   * Method clears the current hideNavigation timeout setting if there is one.
   * @member ProductNavigationManager
   */
  this.clearTimeOut = function() {
  	if(this.timeoutSet) {
  	  clearTimeout(this.timeoutId);
  	  this.timeoutSet = false;
  	}
  }
  /**
   * Method will handle the specific task to show a Category Navigation Item
   * @param category ProductCategory
   * @type void 
   * @member ProductNavigationManager
   */
  this._showCategoryNavigation = function(category) {
  	var status = this.navView.showCategoryNavigation(category);
  	this.navState.setCurrentActive(category);
  }
  /**
   * Method will handle the specific task to show a Model Navigation Item
   * @param category ProductCategory
   * @type void 
   * @member ProductNavigationManager
   */
  this._showModelNavigation = function(model) {
  	var status = this.navView.showModelNavigation(model);
  	this.navState.setCurrentActive(model);
  }
  /**
   * Method figures out if the cursor of the given Event is within the given DOM Element
   * @param domObject DOMElement 
   * @param eventObject Event
   * @type void 
   * @member ProductNavigationManager
   */
  this.isEventInBounds = function(domObject, eventObject) {	
  	if(!eventObject)
  	  return false;
	return Dom.isInBounds(domObject,Event.pointerX(eventObject), Event.pointerY(eventObject));
  }
  /**
   * Method pre caches all menu images
   * @type void 
   * @member ProductNavigationManager
   */  
  this._cacheMenuImages = function() {

  }
  /**
   * Method returns either a Category or a Model object based on if a model is passed in.
   * @param category String
   * @param model String (optional)
   * @type ProductCategory/ProductModel
   * @member ProductNavigationManager
   */
  this._getDataObject = function(category, model) {
  	var dataObject;
  	if(!model)
  	  dataObject = this._getCategoryObject(category);
  	else
  	  dataObject = this._getModelObject(category, model);
  	return dataObject;
  }
  /**
   * Method returns either a Category object based on the id passed in
   * @param categoryId String
   * @type ProductCategory
   * @member ProductNavigationManager
   */
  this._getCategoryObject = function(categoryId) { 
  	return this.getCategory(categoryId);
  }
  /**
   * Method returns either a Model object based on the id passed in
   * @param modelId String
   * @type Model
   * @member ProductNavigationManager
   */
  this._getModelObject = function(categoryId, modelId) {
  	var category = this.getCategory(categoryId);
  	return category.getModel(modelId);
  }
  this._setOnState = function() {
  }
  /**
   * Method executes the preliminary setup of the navigation bar. 
   * Including: caching of all images and rollovers, and all needed
   * events.
   * @member ProductNavigationManager
   */
  this._setupNavigation = function() {  
  	 var mvCategoryContainers = document.getElementsByClassName('mvCategoryContainer');
  	 var mvMiscLinkContainers = document.getElementsByClassName('mvMiscLinkContainer');
  	 
  	 // Category and Model: Add Events and cache all images
  	 for(var i=0; i<mvCategoryContainers.length; i++) {
  	 	var mvCategoryContainer = mvCategoryContainers[i];
  	 	var mvCategory = $A(document.getElementsByClassName('mvCategory',mvCategoryContainer));
  	 	var mvModels = $A(document.getElementsByClassName('mvModels',mvCategoryContainer));
  	 	
  	 	if(mvCategory != '') {
  	 	  this._setupCategory(mvCategory[0]);
  	 	  this._setupModels(mvModels[0]);
  	 	  }
  	 }
  	 
  	 // Misc Links: Add Events and cache all images
	for(var i=0; i<mvMiscLinkContainers.length; i++) {
  	 	var miscDivs = ($NL(mvMiscLinkContainers[i].childNodes)).elements();
		
		for(var x=0; x<miscDivs.length; x++) {
			var miscDiv = miscDivs[x];
			var href = ($NL(miscDiv.childNodes)).firstElement();
  	 		var img = ($NL(href.childNodes)).firstElement();
  	 		
  	 		this._cacheOnOverImages(img.src);
  	 		// Events
  	 		if(img.src.indexOf('On.gif') == -1) {
  	 			Event.observe(img, 'mouseout', function() {swapOver(this);}.bind(img), false);
  	 			Event.observe(img, 'mouseover', function() {swapOver(this);}.bind(img), false);
  	 		}
		}
	}
  }
  /**
   * Method prebuild category overlays.
   * @member ProductNavigationManager
   */
  this._preLoadMenus = function() {  	
  	this.categories.each(function(category){ 
  	   this.navView.buildCategoryOverlay(category); 	  
  	}.bind(this));
  }
  /**
   * Method managers the caching of all navigation category images and
   * adds swapOver and showNavigation events to the category position of the 
   * Navigtaion bar.
   * @member ProductNavigationManager
   */
  this._setupCategory = function(mvCategory) { 
    // Find Category Image and Cache
    var categoryImg = ($NL(mvCategory.childNodes)).firstElement();
    
    //this._cacheOverImage(categoryImg.src);
    
    // Add Events to Category
    Event.observe(categoryImg, 'mouseover', function() {this.hideNavigation()}.bindAsEventListener(this), false);
    Event.observe(categoryImg, 'mouseout', function() {}, false);
    Event.observe(categoryImg, 'click', function() {swapOut(categoryImg);this.showNavigation(categoryImg, categoryImg.id)}.bindAsEventListener(this), false);   
  }
  this._cacheOverImage = function(src) {
  }
  this._cacheOnOverImages = function(src) {
  }
  /**
   * Method caches image in browser.
   * @member ProductNavigationManager
   */
  this._cacheImage = function(src) {
   }
  /**
   * Method handels the setup of all Navigation model images and
   * addes showNavigation to a mouseover event on each model.
   * @member ProductNavigationManager
   */
  this._setupModels = function(mvModel) {
  	 var modelLinks = ($NL(mvModel.childNodes)).elements();
  	 // Loop through each Model, cache and apply needed events
  	 modelLinks.each(function(modelLink) {
  	 	var modelImg = modelLink.firstChild;
  	 	var ids = modelImg.id.split('*_*');
  	 	var catId = ids[0];
  	 	var modId = ids[1];
  	 	// Cache Model Image and Overlay
  	 	this._cacheOverImage(modelImg.src);
  	 	// Add Events to Model
  	 	Event.observe(modelImg, 'mouseover', function() {this.showNavigation(modelImg, catId, modId)}.bindAsEventListener(this), false);
  	 }.bind(this)); 	 
  }
}
/**
 * Class provides static methods to build and display the navigation onto the browser
 * @constructor
 */
function VehicleNavigationView() {
	/**
	 * Method will control the operatations to build and show a Cateogry Navigation.
	 * Will return true if the Model navigation is displayed
	 * @param category ProductCategory
	 * @type boolean
	 * @member VehicleNavigationView
	 */
	this.showCategoryNavigation = function(category) {
	    var wrapper = $('wrapper');
		this.buildCategoryOverlay(category);
		this.buildCategoryNavigation(category);
		// If we could not build, return, nothing else to do
		if(!category.domOverlay)
		  return;
		// Make sure we have the Model class NOT category
	    wrapper.appendChild(category.domOverlay);
	    category.domOverlay.style.top = Dom.getTopYAsPx(category.domHotSpot,wrapper);
	    category.domOverlay.style.left = Dom.getLeftXAsPx(category.domHotSpot,wrapper);
	    category.domOverlay.style.display = 'block';
		if(!category.domNavigation)
		  return
		category.domNavigation.style.top = Dom.getBottomYAsPx('mvContainer',wrapper);
		/*
		 * Need to calculate the position of category div
		 * This will take into consideration the total size of the
		 * category menu and compare with the total size of the navigation bar.
		 * This is to make sure we do NOT position past the right most point of
		 * the navigation bar.
		 */
		var catTotalSize = 1; // we start with one because the category div has to make up one extra right px
		var navTotalSizeRight = 0;
		var catTotalSizeRight = 0;
	    /* 
	     * Calculate the total width based on how many models we are showing
	     * also adds the left position of the dom hot spot (category image)
	     */
		for(var i=0; i<category.models.length; i++) {
			catTotalSize += 234;
			if(i>0) {
				catTotalSize += 1;
			}
		}
		catTotalRight = catTotalSize + Dom.getLeftX(category.domHotSpot,wrapper) - 5;
	    // calculate the total size and left position of Navigation bar.
	    var mvBoundry = Dom.getBoundries('mvContainer',wrapper);
	    navTotalSize = mvBoundry.width + mvBoundry.leftX
	    /* 
	     * If are category div is larger than the navigation bar then we will 
	     * line it up on the right corner
	     */
		if(catTotalRight  >  navTotalSize) {
		  category.domNavigation.style.left = (mvBoundry.rightX - catTotalSize) + 'px';
		}
		/*
		 * Else position the menu at the left edge of the triggering category image
		 */
		else {
		  category.domNavigation.style.left = (Dom.getLeftX(category.domHotSpot,wrapper) - 5) + 'px';
		}
		wrapper.appendChild(category.domNavigation);
		category.domNavigation.style.display = 'block';
	}
	/**
	 * Method will control the operatations to build and show a Model Navigation.
	 * Will return true if the Model navigation is displayed
	 * @param model ProductModel
	 * @type boolean
	 * @member VehicleNavigationView
	 */
	this.showModelNavigation = function(model) {
		var wrapper = $('wrapper');
		this.buildModelNavigation(model);
		// if we could not build, return, nothing else to do
		if(!model.domNavigation)
		  return
        // show and position navigation menu	
		wrapper.appendChild(model.domNavigation);	
		model.domNavigation.style.top = Dom.getBottomYAsPx('mvContainer',wrapper);
		model.domNavigation.style.left = (Dom.getLeftX(model.domHotSpot,wrapper) - 8) + 'px';
	    model.domNavigation.style.display = 'block';
		this.buildModelOverlay(model);
		// if we could not build, return, nothing else to do
		if(!model.domOverlay)
		  return
		wrapper.appendChild(model.domOverlay);
		model.domOverlay.style.top = Dom.getTopYAsPx(model.domHotSpot,wrapper);
		// We move the image 4 px left because the image has 3px on each side more of space
		model.domOverlay.style.left = (Dom.getLeftX(model.domHotSpot,wrapper) - 4) + 'px';
		model.domOverlay.style.display = 'block';
		return true;
	}
	/**
	 * Method will build the DIV Object Navigation representing a specific Model.
	 * @param model ProductModel
	 * @type void
	 * @member VehicleNavigationView
	 */
	this.buildModelNavigation = function(model) {
	    if(model.domNavigation)
		  return
		var nav = $('mvNavigationModel_Template').cloneNode(true);
	    var links = nav.getElementsByTagName('a');
	  //  var imgs = nav.getElementsByTagName('IMG');
	//Set all Links
//	   for(var i=0; i<links.length; i++) {
//	   	 var link = links[i];
//	   	 var linkImg = link.firstChild;
//	   	 //link.href = this._replaceModelLink(link.href, model.name, model.code);	   	
//	    }
			nav.id = 'mvNav_' + model.categoryId + "_" + model.name;
	//Build Features
	  var containerDivs = nav.getElementsByTagName('DIV');
		var topChildrenDivs = containerDivs[0].getElementsByTagName('DIV');
		var featuresDiv = topChildrenDivs[topChildrenDivs.length - 1];
		var startingPrice = topChildrenDivs[topChildrenDivs.length - 2];
		for(var i=0; i<model.features.length; i++) {
			var featureDiv = document.createElement('DIV');
			featureDiv.className = 'mvNavigationFeature';
			featureDiv.innerHTML = model.features[i];
			featuresDiv.appendChild(featureDiv);		
		}
	//Build Staring Price
		startingPrice.innerHTML = model.getPriceAsString();
        //Add Events
		if(document.all)
		  Event.observe(nav, 'mouseout', function() {event.cancelBubble = true;Navigator.hideNavigationWithTimeOut(event)})
		else
		Event.observe(nav, 'mouseout', function(event) {event.cancelBubble = true;Navigator.hideNavigationWithTimeOut(event)})
		Event.observe(nav, 'mouseover', function() {Navigator.clearTimeOut()})
        model.domNavigation = nav;
		$('mvContainer').appendChild(model.domNavigation);
	}
	/**
	 * Method will build the DIV Object for the Model Overlay representing a specific Model.
	 * @param model ProductModel
	 * @type void
	 * @member VehicleNavigationView
	 */
	this.buildModelOverlay = function(model) {
		if(model.domOverlay)
		  return
		var overlayDiv = document.createElement('DIV');
		var overlayHref = document.createElement('A');
		//var overlayImg = document.createElement('SPAN');
			overlayDiv.className = 'mvOverLay';
			// browser conflict
		if(document.all)	
		  overlayDiv.onmouseout = function() { event.cancelBubble = true;Navigator.hideNavigationWithTimeOut(event)};
		else
		 overlayDiv.onmouseout = function(event) {event.cancelBubble = true;Navigator.hideNavigationWithTimeOut(event)};
		overlayDiv.onmouseover = function() {Navigator.clearTimeOut()};
		overlayHref.href = model.domHotSpot.parentNode.href;
	    overlayHref.target = model.domHotSpot.parentNode.getAttribute('TARGET');
	    overlayDiv.appendChild(overlayHref);  	
		model.domOverlay = overlayDiv;
		$('mvContainer').appendChild(model.domOverlay);  
	}
	/**
	 * Method will build the DIV Object Navigation representing a specific Model.
	 * @param model ProductModel
	 * @type void
	 * @member VehicleNavigationView
	 */
	this.buildCategoryNavigation = function(category) {
		if(category.domNavigation)
		  return
	   
		var categoryDiv = document.createElement('DIV');
		categoryDiv.id = 'mvCat_' + category.name;
		categoryDiv.className = 'mvNavigationCategory';
	    
	    for(var i=0; i<category.models.length; i++) {    	
	  	// Build Model Navigation if needed
		  	if(!category.models[i].domNavigation) {
		  	  this.buildModelNavigation(category.models[i]);  
		  	}
		  	var cloneDiv = category.models[i].domNavigation.cloneNode(true);
		  	cloneDiv.id = category.name + '_' + cloneDiv.id;
		  	cloneDiv.className = 'mvNavigationModelForCategory';
		  	cloneDiv.style.display = 'block';
			this._addLegalDisclaimerEvents(category.models[i].name,cloneDiv);
		  	if(i>0) {
		        var catDivider = document.createElement('DIV');
		        var catDividerImg = document.createElement('IMG');
		      catDivider.className = 'mvNavigationCategoryDivider';	        
		      catDividerImg.src = IMG_ROOT + 'spacer.gif';
		  	  catDivider.appendChild(catDividerImg);
		  	  categoryDiv.appendChild(catDivider)
		  	}
		  	categoryDiv.appendChild(cloneDiv);
	  }
	  category.domNavigation = categoryDiv;
	  $('mvContainer').appendChild(categoryDiv);
	}
	/**
	 * Method will build the DIV Object for the Model Overlay representing a specific Model.
	 * @param model ProductModel
	 * @type void
	 * @member VehicleNavigationView
	 */
	this.buildCategoryOverlay = function(category) {
	  if(category.domOverlay)
	    return
	}
	// Method replaces all ~model~ wildcards with actual model name
	this._replaceModel = function(str, name) {
	  
	  return str.templateReplace(this.MODEL_REPLACE, name);	
	}
	this._replaceModelCode = function(str, name) {
		return str.templateReplace(this.MODEL_CODE_REPLACE, name);	
	}
	// Method replaces all ~category~ wildcards with actual category name
	 this._replaceCategory = function(str, cat) {
     return str.templateReplace(this.CATEGORY_REPLACE, cat);	
	}
}
function ieBodyhideNavigation() {
	Navigator.hideNavigationWithTimeOut();
}
/***************************************************************************************
Code to read products information from the xml file and update the model object.
Model object will be read based on the product id, whenever the user hover mouse on a 
model.
****************************************************************************************/
var Navigator = new ProductNavigationManager();
loadXML('./PublicFeed/ModelContentFeed.aspx', ReadXmlData)
function loadXML (url, processXML) {
 var httpRequest;
    if (typeof XMLHttpRequest != 'undefined') {
    httpRequest = new XMLHttpRequest();
    }
    else if (typeof ActiveXObject != 'undefined') {
    httpRequest = new ActiveXObject('Microsoft.XMLHTTP');
    }
    if (httpRequest) {
    httpRequest.open('GET', url, true);
    httpRequest.onreadystatechange = function () {
    if (httpRequest.readyState == 4 && httpRequest.responseXML) {
    processXML(httpRequest.responseXML);
    }
    };
    httpRequest.send(null);
    }
}
//Function to read xml data and load the model object
function ReadXmlData(xmlDoc)
{
        var Products=xmlDoc.getElementsByTagName("Products");
      //For Loop to create Category object and load the product category id.  
      for (var i=0;i<Products.length;i++)
      {
         var ProdInfo=Products[i].getElementsByTagName("ModelInfo");
          var category = new ProductCategory("");
          //For Loop to create model object and load the product info based on the product category(Category object)
          for(var j=0; j<ProdInfo.length; j++) 
          {
          var ModelId = ProdInfo[j].getElementsByTagName("Id")[0].childNodes[0].nodeValue;
          var ModelName = ProdInfo[j].getElementsByTagName("Name")[0].childNodes[0].nodeValue;
          var ModelImage = ProdInfo[j].getElementsByTagName("ModelImage")[0].childNodes[0].nodeValue;
          var ModelDesc = ProdInfo[j].getElementsByTagName("Description")[0].childNodes[0].nodeValue;
               var model = new ProductModel(ModelId, ModelId);
                 var modelinfo = '<table border=0 width="100%"><tr><td class="ModelText"><a href=./Model.html?ModelId='+ ModelId +'>'+ ModelName +'</a></td></tr><tr><td align="center">'+
               '<a href=./Model.html?ModelId='+ ModelId +'><img style="border:0" align="center" src=' + ModelImage + '></a></td></tr>'+
               '</table><hr>'+
               '<table border=0 width="100%"><tr><td>'+ ModelDesc +'</td></tr></table><hr>'
               model.addFeature(modelinfo);
               model.addFeature('<a href=./Model.html?ModelId='+ ModelId +'>Learn More</a>')
               category.addModel(model);
            }
            Navigator.addCategory(category);
    }
 	
}
