/**
 * Cars.com Javascript Library
 * @version 0.2
 * @fileoverview
 */
 
addNamespace = function(name){
  var ns = name.split('.');
  var c = window;
  for (var i in ns) {
    if (!c[ns[i]]) { c[ns[i]] = {}; }
    c = c[ns[i]];
  }
};

// build namespaces
addNamespace('CARS.utility');
addNamespace('CARS.utility.lazyLoad');
addNamespace('CARS.search');
addNamespace('CARS.data.preferred');
addNamespace('CARS.data.cache');
addNamespace('CARS.bidiq');
addNamespace('CARS.adProducts');
addNamespace('CARS.sort');
addNamespace('CARS.legacy'); // ideally this will be empty


/*
* CARS.utility.safeLoader
* Method for sequentially loading resources
* resource: the resource that we want loaded
* dependency: what the external script depends on
*

CARS.utility.safeLoader = function(resource, dependency) {
	if (typeof(dependency) === ('function' || 'object')) {
		return true;	
	} else {
		var internalTimer = [];
		internalTimer = window.setTimeout(function() {
			CARS.utility.addLib('jQuery',true);
		}, 300);
	}

};*/

/**
 * Methods for working with cookies
 */
CARS.utility.cookie = {
  read: function(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for (var i=0;i < ca.length;i++) {
      var c = ca[i];
      while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
      if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
    }
    return null;
  },
  create: function(name,value,days,domain) {
    var expires = "";
    if (days) {
      var date = new Date();
      date.setTime(date.getTime()+(days*24*60*60*1000));
      expires = "; expires="+date.toGMTString();
    }
    document.cookie = name+"="+value+expires+"; path=/";
  },
  del: function(name) {
    this.create(name,'',-1);
  }
};
/**
 * Method for dynamically adding js/css
 */
CARS.utility.addToHead = function(f,t) {
  var h = document.getElementsByTagName("head")[0];
  var nType = t || f.match(/css/gi) ? 'css' : 'js';
  var n;
  if (nType == 'css') {
    n = document.createElement('link');
    n.type = 'text/css';
    n.rel = 'stylesheet';
    n.href = f;
  } else {
    n = document.createElement('script');
    n.type = 'text/javascript';
    n.src = f;
  }
  h.appendChild(n);
};
/**
 *	Extending the Array prototype
 */
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};
/**
 * Method for dynamically grabbing param, parses cookie in absence
 */
CARS.utility.getParam = function(name) {
  var p = null;
  // check query string
  var q = document.location.search.toString();
  var reg = new RegExp(name+"=([^&]+)","i");
  p = q.match(reg);
  if (p !== null) { return unescape(p[1]); }
  // not found in query, check cookie
  p = CARS.utility.cookie.read(name);
  if (p !== null) { return unescape(p); }  
  // not found
  return false;
};
/**
 * Formatter class for managed formatting methods
 */
CARS.utility.formatter = {
  // method to convert value to integer
  toInt: function(val) {
    return parseInt(val, 10);
  },
  // method to convert value to float
  toFloat: function(val,dec) {
    dec = dec || 2;
    return(val.toFixed(dec));
  },
  // method to strip HTML from {val}
  stripHTML: function(val) {
    var t = typeof val;
    switch (t) {
      case 'object':
        val = val.innerHTML; // assume dom object
      case 'string':
        // TODO: test this regex chain across browsers... \s doesn't always work for spaces
        val = val.replace(/<[^>]+>/mig,"").replace(/\n/mig," ").replace(/ {2,}/mig," ");
        break;
      return this.trim(val);
    }
  },
  // method to trim whitespace at either end of {val}
  trim: function(val) {
    return val.replace(/^\s+|\s+$/g,"");
  },
  // method to parse a date and format
  date: function(date,fmt) {
    // Date.parse(dateString)
  },
  // method to format number (commas) 
  addCommas: function(p) { 
    p += ''; // if number, convert to string
    var objRegExp  = new RegExp('(-?[0-9]+)([0-9]{3})');
    while(objRegExp.test(p)) {
       p = p.replace(objRegExp, '$1,$2'); //replace original string with first group match, a comma, then second group match
    }
    return p;
  },
  // method to format price
  toPrice: function(p) {
    return '$'+this.addCommas(p);
  },
  // method to format phone number
  toPhone: function(p) {
    p += ''; // if number, convert to string
    return p.replace(/([0-9]{3})([0-9]{3})([0-9]{4})/, '$1-$2-$3');
  },
  titleCase: function(s) {
	s = s.split(' ');
	var o = '';
	for (var i=0; i<s.length; i++) {
		s[i] = s[i].substr(0,1).toUpperCase() + s[i].substr(1); 
	}
	return s.join(' ');
  }

};

// helper function to get web property name
CARS.utility.getPropertyName = function() {
  return window.location.host.match(/(www\.)?(.*)/i)[2];
};
// helper function to get top level domain value
CARS.utility.getTLD = function() {
  return window.location.host.match(/[^\.]+\.\w{2,3}$/);
};

/* Method to safely log to firebug
* 'msg': the message to be logged
*/
CARS.utility.logit = function(msg) {
	if (typeof(console) !== 'undefined') {
		console.log(msg);
	}
};

// legacy functions rolled out of utility.js
// these need to be normalized
CARS.legacy.openPopup = function (url, name, properties) {
    if (null != url)
    {
        var popupWin = window.open(url, name, properties);
        if (null != popupWin) popupWin.focus();
    }
};
CARS.legacy.emailToFriend = function() {
	CARS.legacy.openPopup('/go/email/mailerPopUp.jsp', "mailpopup", "width=425,height=600,top=50,left=50");
};
CARS.legacy.checkEmail = function (emailAddressElement, emailAddressErrorMessage) {
	var AtSym=emailAddressElement.value.indexOf('@');
	var Period=emailAddressElement.value.lastIndexOf('.');
	var Space=emailAddressElement.value.indexOf(' ');
	var wwwdot=emailAddressElement.value.indexOf('www.');
	var Length=emailAddressElement.value.length - 1;  
	var emailFieldName = "";
	if (emailAddressElement.name == 'sender') { 
		emailFieldName = 'From';
	} else if (emailAddressElement.name == 'recip') {
		emailFieldName = 'To';
	} else if (emailAddressElement.name == 'cc') {
		emailFieldName = 'CC';
	}
	var errorMessage = 'Make sure the email address in the \"' + emailFieldName + '\" field is entered in the proper format, \nsuch as \"user@domainname.com\". There is a limit of one email address per field.'
	if (emailAddressElement.value==""){
	  alert(emailAddressErrorMessage);
	  emailAddressElement.focus();
	  return false;
	} 
	else if ((AtSym < 1) || (Period <= AtSym+1) || (Period == Length ) || (Space  != -1) || (wwwdot != -1)) {  
	  if (emailAddressElement.name == "sender" || emailAddressElement.name == "recip") {
		alert(emailAddressErrorMessage);
	  }
	  else {
		alert(errorMessage);
	  }
	  emailAddressElement.focus();
	  return false;
	}
	return true;
};



CARS.utility.addLib = function(s,d) {
	if (!s) {s = 'jQuery'};
	switch (s) {
		case 'jQuery':
			if (typeof(jQuery) === 'function') {
				// once we have jQuery, add in plugins
				CARS.utility.addLib('jQuery.cookie', false);
			} else {
				var jquerySource = "http://www.cars.com/js/lib/jquery/1_3_2/jquery.min.js";
				// check if we wrote the script to the head already and this is a wait loop
				if (!d) { CARS.utility.addToHead(jquerySource); };
				CARS.utility.jqueryLoadingInterval = window.setTimeout(function(arg) {
					CARS.utility.addLib('jQuery',true);
				}, 300);
			}
			break; // end of case 'jQuery'
		case 'jQuery.cookie':
			if (typeof(jQuery.cookie) !== "function") {
				var jqueryCookieSource = "http://www.cars.com/js/lib/jquery/plugins/jquery.cookie.js";
				// check if we wrote the script to the head already and this is a wait loop
				if (!d) { CARS.utility.addToHead(jqueryCookieSource); };
				CARS.utility.jqueryLoadingInterval = window.setTimeout(function(arg) {
					CARS.utility.addLib('jQuery.cookie',true);
				}, 300);
			} else {
				return false;
			}
			break; // end of case 'jQuery.cookie'
		case 'zipCapture':
			if (typeof(window.carscom.zipCapture) !== 'object' && typeof(jQuery) === 'function') {
				$.get('/go/includes/_zipCapture.jsp', function(data) {
					$('body').after(data);
				});
			}
	} // end of switch (s)

};

CARS.utility.lazyLoad = {
    containers: [], counter: 0,
    // init hook
    init: function() {
      CARS.utility.addLib('jQuery');
      this.register();
    },
    // register lazy load modules
    register: function() {
      // identify lazy load eligible content
      this.containers = $('.cars-lazy').get();
      // assign ids
      for (var i=0; i<this.containers.length; i++) { 
        if (!this.containers[i].id) {
          this.containers[i].id = 'cll-' + this.counter++;
        }
      }
      // register onscroll
      if (this.containers.length > 0) {
        $(window).bind('scroll resize', function() { CARS.utility.lazyLoad.watcher();});
      }
      // check for current scroll
      this.watcher();
    },
    // load content and remove from watchlist
    load: function(w) {
      // identify what to load
      $(w).load(w.title, function() {
		// remove the title that contains the path, as it appears as a tooltip on mouseover
		w.title = "";
		// call the adloader again to see if we need to move any ads
		if (typeof(CARS.adProducts) === "object") { CARS.adProducts.adLoader.init() };
		
		// try hitting watcher again to see if our offset and top measurements have changed
		CARS.utility.lazyLoad.watcher();
        //CARS.utility.lazyLoad.unwatch(w.id);
        // attach omniture HBX tracking
        if (window.s_clven && window.s_clven.setOIDs) { s_clven.setOIDs(); }
      });
		// on success, remove -- moved outside because it's removing after load; onscroll will call more than once during this time -cj
		this.unwatch(w.id);

    },
    unwatch: function(id) {
      // find what we just loaded
      for (var i=0; i<this.containers.length; i++) {  
        if (this.containers[i].id == id) {
          // remove what we just loaded
          this.containers.remove(i);
        }
      }
    },
    // identify if/when we should load content   
    watcher: function() {
      // don't use 'this' -- called via callback 
      if (this.containers && this.containers.length > 0) {
        // our value to test to load
      var triggerValue = $(window).height() + $(window).scrollTop() + 150;
		for (var i=0; i<this.containers.length; i++) {
			if ($(this.containers[i]).offset().top < triggerValue) {
				this.load(this.containers[i]);
			}
		}
      }
    }
};


// sort namespace
// uses built-in Javascript .sort method
// assumptions:
//   jquery exists
//   .sortable (or override) contains a set of sortable rows and controls
CARS.sort = { sortObjects: {},
	selfRegister: function(sn) {
		sn = sn || 'sortable';
		$('div.'+sn).each(function(index) {
			this.id = this.id || 'sortContainer' + String((new Date()).getTime()).replace(/\D/gi,'');
			CARS.sort.init(this);
		});
	},
	init: function(container) {
		var that = container;
		
		// all of these assume that the actual link is within a styled parent element, so hide that
		$('.sort-mmy.descending',container).click(function() {
			$(this).parent().hide();
			$('.sort-mmy.ascending',container).parent().show();
			CARS.sort.mmy(that,'descending');
			CARS.utility.cookie.create('modelListSort','mmy-descending');
			return false;
		});
		$('.sort-mmy.ascending',container).click(function() {
			$(this).parent().hide();
			$('.sort-mmy.descending',container).parent().show();
			CARS.sort.mmy(that,'ascending');
			CARS.utility.cookie.create('modelListSort','mmy-ascending');
			return false;
		});
		$('.sort-price.ascending',container).click(function() {
			$(this).parent().hide();
			$('.sort-price.descending',container).parent().show();
			CARS.sort.price(that,'ascending');
			CARS.utility.cookie.create('modelListSort','price-ascending');
			return false;
		});
		$('.sort-price.descending',container).click(function() {
			$(this).parent().hide();
			$('.sort-price.ascending',container).parent().show();
			CARS.sort.price(that,'descending');
			CARS.utility.cookie.create('modelListSort','mmy-descending');
			return false;
		});
		
		if (CARS.utility.cookie.read('modelListSort')) {
			CARS.utility.logit('cookie produced correctly and its value is: ' + CARS.utility.cookie.read('modelListSort'));
			
			switch (CARS.utility.cookie.read('modelListSort')) {
				case 'mmy-ascending':
					$('.sort-mmy.ascending',container).click();
				break;
				case 'mmy-descending':
					$('.sort-mmy.descending',container).click();
				break;
				case 'price-ascending':
					$('.sort-price.ascending',container).click();
					CARS.utility.logit('hc1');
				break;
				case 'price-descending':
					$('.sort-price.descending',container).click();
				break;
			}
			
		}
	},
	inject: function(container,rows) {
		// original
		/*$('.sortrows',container).empty;
		$.each(rows, function(i,l) {
			$('.sortrows',container).append(l);
		});*/
		
		// new
		var objHolder = {};
		$('.sortrows',container).empty;
		$.each(rows, function(i,l) {
			objHolder[i] = l;
		});
		$('.sortrows',container).append(objHolder);
	},
	price: function(container,sortDirection) {
		CARS.sort.sortObjects = $('.modelListRow',container);
		$.each(CARS.sort.sortObjects, function(i,l) {
			// load the internal properties into the row object for manipulation
			this.price = $('.price',l).text();
			var range = this.price.split('-');
			if (range.length > 1) {
				// use low price
				if (sortDirection === 'ascending') {
					this.price = parseInt(range[0].replace(/[^0-9\.]+/g,""))
				// use high price
				} else if (sortDirection === 'descending') {
					this.price = parseInt(range[1].replace(/[^0-9\.]+/g,""));
				}
			// controls if 'Price Coming Soon' gets sorted to physical top or bottom of the list for UX reasons
			} else if (this.price.indexOf('$') === -1) {
				if (sortDirection === 'ascending') {
					this.price = 99999999;
				} else if (sortDirection === 'descending') {
					this.price = 0;
				}
			} else {
				this.price = parseInt(range[0].replace(/[^0-9\.]+/g,""));
			}
			
		});
		
		switch (sortDirection) {
			case 'ascending':
				CARS.sort.sortObjects.sort(function(a,b) {
					if (a.price > b.price) {
						return 1;
					} else if (a.price < b.price) {
						return -1;
					} else {
						return 0;
					}
				});
				break;
			case 'descending':
				CARS.sort.sortObjects.sort(function(a,b) {
					if (a.price < b.price) {
						return 1;
					} else if (a.price > b.price) {
						return -1;
					} else {
						return 0;
					}
				});
				break;
			default:
				CARS.utility.logit('the price sort has failed!');
				break;
		}

		CARS.sort.inject(container,CARS.sort.sortObjects);
		CARS.sort.sortObjects = {};
	},
	
	mmy: function(container,sortDirection) {
		CARS.sort.sortObjects = $('.modelListRow',container);
		$.each(CARS.sort.sortObjects, function(i,l) {
			// load the internal properties into the row object for easier manipulation
			this.makename = $('.makename',l).text();
			// drop the makename to lowercase
			this.makename = this.makename.toLowerCase();
			this.modelname = $('.modelname',l).text();
			this.year = $('.year',l).text();
			
		});
		
		switch (sortDirection) {
			case 'ascending':
				CARS.sort.sortObjects.sort(function(a,b) {
					if (a.makename > b.makename) {
						return 1;
					} else if (a.makename < b.makename) {
						return -1;
					} else {
						return 0;
					}
				});
				break;
			case 'descending':
				CARS.sort.sortObjects.sort(function(a,b) {
					if (a.makename < b.makename) {
						return 1;
					} else if (a.makename > b.makename) {
						return -1;
					} else {
						return 0;
					}
				});
				break;
			default:
				CARS.utility.logit('the basic sort has failed!');
				break;
		}
	
		CARS.sort.inject(container,CARS.sort.sortObjects);
		CARS.sort.sortObjects = {};
	}
};

// search namespace
CARS.search = {
	makeLoadingInterval: [], modelLoadingInterval: [], currentStock: [], currentBodystyles: [], getParentMakeInterval: [], modelLoadingIntervalSub: [], suppressAliases: false,
	/* function that auto-registers all widgets by form classname
	* 'cn': classname override
	* 's': value to indicate loading of js libs
	* 'sa': suppress selecting aliases
	*/
	selfRegister: function(cn,s,sa) {
		window.carscom = window.carscom || {};
		// add jQuery to the head
		if (window.jQuery && window.jQuery.cookie && window.carscom.zipCapture) { 
			cn = cn || 'cars-search-widget';
			this.suppressAliases = sa || false;
			$('form.'+cn).each(function(index) {
				this.id = this.id || 'form-' + String((new Date()).getTime()).replace(/\D/gi,'');
				CARS.search.initForm(this);
			});
		} else {
			if (!s) {
				CARS.utility.addLib('jQuery');
				CARS.utility.addLib('zipCapture');
			}
			var waitFunc = window.setTimeout(function() {
				CARS.search.selfRegister(cn,true,sa);
			}, 250);
		}
	},

	/* initialize a form as an inventory search 
	* Expects form elements named specifically
	* Event binding should be kept in this init if at all possible
	*/
	initForm: function(w) {
		var that = w;
		// initialize default/preferred data
		this.initPrefData(w);
		// setup event handlers
		$('select[name="make"]', w).change(function() {
			// set current make as preferred
			CARS.data.preferred.makeID = this.options[this.options.selectedIndex].value;
			CARS.data.preferred.makeName = this.options[this.options.selectedIndex].text;
			if (this.options[this.options.selectedIndex].value === '') {CARS.data.preferred.modelID = ''; CARS.data.preferred.modelName = 'all';};
			// get related models
			CARS.search.getModels(w,CARS.data.preferred.makeID);
		});
		if (typeof($('select[name="model"]', w)) !== 'undefined'){
			$('select[name="model"]', w).change(function() {
				// set current model as preferred
				CARS.data.preferred.modelID = this.options[this.options.selectedIndex].value;
				CARS.data.preferred.modelName = this.options[this.options.selectedIndex].text;
				if ($('select[name="make"] :selected',w).val() === '') {
					CARS.search.getParentMake(w);
				}
			});
		}
		
		if (w['stocktype'].length) {
			var stockSelected = false;
			for (var i=0; i<w['stocktype'].length; i++) {
				// find if a stocktype is selected
				if (w['stocktype'][i].checked == true) { stockSelected = true; }
				// register event handler
				// w['stocktype'][i].onchange = function() {
				$(w['stocktype'][i]).click(function() {
					// reset makes
					CARS.search.getMakes(w);
					// update 'button' text
					$('span[name="searchText"]',w).text('Search ' + CARS.utility.formatter.titleCase(this.value));
				});
			}
			// set a default stocktype
			if (!stockSelected) {
				var defaultStock = 0;
				for (var i=0; i<w['stocktype'].length; i++) {
					if (w['stocktype'][i].value == 'used') { defaultStock = i; }
				}
				w['stocktype'][defaultStock].checked = true;
			}
		};

		carscom.zipCapture.registerOnSuccess($('a[name="submit"]', w), function() {
			CARS.search.submitSearch(that);
		});
		
		// submit text link
		// overwrite action
		w.onsubmit = function() {
			$('a[name="submit"]', w).trigger('click');
			return false;
		}

		if (typeof(w['make']) !== 'undefined') {
			CARS.search.getMakes(w);
		}
		
	},

	/* function for pre-loaded 'preferred' defaults (SessionInfo)
	* possible form fields: 
	* stocktype, bsID
	* make, model (SessionInfo)
	* prMx, zc & rd
	*/
	initPrefData: function(w) {
		if ($.cookie('SessionInfo')) {
			var e = [];
			e = $.cookie('SessionInfo').split('|');
			if (e[1]) {
				var f = [];
				f = e[1].split('=');
				CARS.data.preferred.makeName = f[1];
				CARS.data.preferred.makeName = CARS.data.preferred.makeName.replace(/\+/g, " ");  //plus sign was causing names not to match and defaullting to all
			}
			if (e[3]) {
				var g = [];
				g = e[3].split('=');
				CARS.data.preferred.modelName = g[1];
				CARS.data.preferred.modelName = CARS.data.preferred.modelName.replace(/\+/g, " ");  //plus sign was causing names not to match and defaulting to all
			}
		};
		if ($.cookie('stockCert')) {
			var d = [];
			d = $.cookie('stockCert').split('|');
			if (d[0] === "U" && d[1] === "false") { CARS.data.preferred.stockType = 'used' };
			if (d[0] === "U" && d[1] === "true") { CARS.data.preferred.stockType = 'certified' }
			if (d[0] === "N" && d[1] === "false") { CARS.data.preferred.stockType = 'new' };
		};
		
		if ($.cookie('qsCookie')) {
			// qsCookie not returning on content searches
			var d = [];
			d = $.cookie('qsCookie').split('|');
			CARS.data.preferred.radius = d[0];
			CARS.data.preferred.zipcode = d[1];
			
			//radius
			if ($('input:select[name="rd"]',w)) {
				$('input:select[name="rd"]',w).each(function(index) {
					if ($(this).val() === CARS.data.preferred.radius) {
						$(this).attr("selected",true);
					}
				});
			}
			
			//zipcode
			if ($('input:text[name="zc"]',w)) {
				$('input:text[name="zc"]',w).val(CARS.data.preferred.zipcode);
			}
			
		}
		
		$('input:radio[name="stocktype"]',w).each(function(index) {
			if ($(this).val() === CARS.data.preferred.stockType) {
				$(this).attr("checked",true);
			}
		});

	},

	/* function to get search domain to go to
	* 'url': override of current url
	* returns: domain for search to be run on
	*/
	getSearchDomain: function(url) {
		var url = url || CARS.utility.getPropertyName();
		// development short-circuit
		if (url.match(/^(cj|cms|local)/)) { return url; }
		var searchDomain;
		var validDomain = url.match(/(cars|pickuptrucks)\.com/);
		if (validDomain) {
			searchDomain = 'www.' + validDomain[0];
		}
		return searchDomain || 'www.cars.com';
	},

	/* function for getting the current stocktype of the form we're dealing with
	* 'w': which form
	* returns: string of stocktype (new/used/cpo)
	*/
	getSelectedStock: function(w) {
		// no form, then default to used
		if (!w) { return 'used'; }
		// check for radio button options
		if (w['stocktype'].length) {
			for (var i=0; i<w['stocktype'].length; i++) {
				if (w['stocktype'][i].checked) { return w['stocktype'][i].value; }
			}
		// look for static value
		} else if (w['stocktype'].value) {
			return w['stocktype'].value;
		}
		// if we haven't found and returned a value, default to used
		return 'used';
	},

	/* method for getting current form bodystyles
	* 'w': which form
	* returns: comma-delimited string of bodystyle IDs
	*/
	getSelectedBodystyles: function(w) {
		// no form, then default to used
		var bstring = '';
		if (!w || !w['bodystyle']) { return bstring; }
		// check for radio button options
		if (w['bodystyle'].length) {
			for (var i=0; i<w['bodystyle'].length; i++) {
				if (w['bodystyle'][i].checked) {
					if (bstring) { bstring += ','; }
					bstring += w['bodystyle'][i].value;
				}
			} 
			// look for static value
		} else if (w['bodystyle'].value) {
			bstring = w['bodystyle'].value;
		}
		// if we haven't found and returned a value, default to used
		return bstring;
	},

	/* used to trim extra presentation logic (Aliases)
	* 's': string to trim
	* returns: trimmed model name
	*/
	trimModelName: function(s) {
		return s.replace(/^[\s-]+|\s+$/g,"");
	},

	/* generates URL to add data to DOM for populating widgets
	* 'mm': make/model indicator
	* 'stock': passed int stockType
	* 'bs': bodystyle
	* 'dm': for model case if make/model doesn't exist in new bodystyle
	* returns: name of variable it will populate in the DOM
	*/
	populateMDS: function(mm,stock,bs,dm) {
		var stockString, varName, action, bodyStyles, makeID, modelID;
		var bodyStyles = bs ? escape(bs) : '';
		var mdsURL = 'http://www.cars.com/for-sale/{ACTION}?varname=CARS.data.cache.{VARNAME}&loc=en';
		switch (stock) {
			case 'new': 
			stockString = '&cpo=&usd=&nw=Y'; 
			break;
			case 'certified': 
			stockString = '&cpo=Y&usd=Y&nw='; 
			break;
			default: 
			stockString = '&cpo=&usd=Y&nw='; 
			break;
		}
		switch (mm) {
			case 'make':
			varName = 'makeData_'+stock;
			action = 'GetMakeData.action';
			break;
			case 'parentMake':
			modelID = CARS.data.preferred.modelID || 'all';
			varName = 'parentMakeData_'+stock+'_'+modelID;
			action = 'GetMakeData.action';
			if (parseInt(modelID) > 0) { mdsURL = mdsURL + '&mdID=' + modelID; }
			break;
			case 'model':
			if (dm === true) {
				varName = 'modelData_'+stock+'_all';
			} else {
				makeID = CARS.data.preferred.makeID || 'all';
				varName = 'modelData_'+stock+'_'+makeID;
				if (parseInt(makeID) > 0) { mdsURL = mdsURL + '&mkID=' + makeID; } // only used makeID if it is a number
			}
			action = 'GetModelData.action';
			break;
		}
		// -- build masterdata URL
		if (bodyStyles) { 
			mdsURL += '&bsID=' + bodyStyles;
			varName += '_' + bodyStyles;
		}
		// add stockType...
		mdsURL += stockString;
		// clean varName (bodystyle and alias comma-delimited strings)
		varName = varName.replace(/[^\w]/gi,'');
		// swap out elements
		mdsURL = mdsURL.replace('{VARNAME}',varName).replace('{ACTION}',action);
		// call for data only if we don't already have the data
		if (!CARS.data.cache[varName]) { 
			CARS.utility.addToHead(mdsURL);
		}
		return varName;
	},

	getMakesSubFunction: function(w,dataVar,marker) {
		//if (CARS.data.cache[dataVar].options) {
		if (typeof(CARS.data.cache[dataVar]) === 'object') {
			if ( typeof(CARS.data.preferred.makeID) === 'undefined' && typeof(CARS.data.preferred.makeName) === 'string' ) {
				$.each(CARS.data.cache[dataVar].options, function(i,l) {
					if (l.name === CARS.data.preferred.makeName) {
						CARS.data.preferred.makeID = l.value;
					}
				});
			}
			window.clearInterval(CARS.search.makeLoadingInterval[w.id + marker]);
			CARS.search.populateSelect(w['make'],CARS.data.cache[dataVar].options);
			if (typeof(w['model']) !== 'undefined') { CARS.search.getModels(w); };
		}
	},
	
	/* initialize make population, triggers callback
	* 'w': which form
	*/
	getMakes: function(w) {
		this.currentStock[w.id] = this.getSelectedStock(w);
		this.currentBodystyles[w.id] = this.getSelectedBodystyles(w);
		var dataVar = this.populateMDS('make',this.currentStock[w.id],this.currentBodystyles[w.id]);
		this.emptySelect(w['make']);
		var marker = String((new Date()).getTime()).replace(/\D/gi,'');
		this.makeLoadingInterval[w.id + marker] = window.setInterval( function() { CARS.search.getMakesSubFunction(w,dataVar,marker); }, 100);
	},

	/* initialize model population, triggers callback
	* 'w': which form
	*/
	getModels: function(w) {
		if (typeof(w['model']) !== 'undefined') {
			var dataVar = this.populateMDS('model',this.currentStock[w.id],this.currentBodystyles[w.id]);
			this.emptySelect(w['model']);
			var marker = String((new Date()).getTime()).replace(/\D/gi,'');
			this.modelLoadingInterval[w.id + marker] = window.setInterval( function() { CARS.search.getModelsSubFunctionOne(w,dataVar,marker); }, 100);
		} 
	},
	
	getModelsSubFunctionOne: function(w,dataVar,marker) {
		if (typeof(CARS.data.cache[dataVar]) === 'object') {
			window.clearInterval(CARS.search.modelLoadingInterval[w.id + marker]);
			dataVar = CARS.search.checkMakeExists(w,dataVar);
			CARS.search.modelLoadingIntervalSub[w.id + marker] = window.setInterval( function() { CARS.search.getModelsSubFunctionTwo(w,dataVar,marker); }, 100);
		}
	},
	
	getModelsSubFunctionTwo: function(w,dataVar,marker) {	
		if (typeof(CARS.data.cache[dataVar]) === 'object') {
			window.clearInterval(CARS.search.modelLoadingIntervalSub[w.id + marker]);
			CARS.search.populateSelect(w['model'],CARS.data.cache[dataVar].options);
		}
	},
	
	getParentMakeSubFunction: function(w,dataVar) {
		CARS.search.emptySelect(w['model']);
		if (typeof(CARS.data.cache[dataVar]) === 'object') {
			window.clearInterval(CARS.search.getParentMakeInterval[w.id]);
				for (var i=0; i<w['make'].length; i++) {
					if (w['make'].options[i].value === CARS.data.cache[dataVar].options[1].value) {
						w['make'].options[i].selected = true;
						CARS.data.preferred.makeID = CARS.data.cache[dataVar].options[1].value;
						CARS.search.getModels(w);
					}
				}		
		}
	},

	/* function for getting parent make of a model
	* 'w': which form to set
	*/
	getParentMake: function(w) {
		var dataVar = CARS.search.populateMDS('parentMake',CARS.search.currentStock[w.id],CARS.search.currentBodystyles[w.id]);
		this.getParentMakeInterval[w.id] = window.setInterval( function() { CARS.search.getParentMakeSubFunction(w,dataVar); }, 100);
	},

	/* empty a dropdown, show 'loading'
	* 's': select to empty
	*/
	emptySelect: function(s) {
		var o = new Option('Loading...','');
		s.options.length = 0;
		s.options[0] = o;
	},

	/* populate a select with data; expects object of name/value combos
	* 's': which select (DOM)
	* 'd': what data (object)
	*/
	populateSelect: function(s,d) {
		s.options.length = 0;
		for (var i=0; i<d.length; i++) {
		  var o = new Option(d[i].name, d[i].value);
		  // suppressed aliased options
		  if (this.suppressAliases && d[i].value.split(',').length > 1) { 
		    o.disabled = 'disabled';
		    o.style.fontWeight = 'bold';
		  }
			// pre-pop make select
			if (s.name == 'make' && o.text == CARS.data.preferred.makeName) {
				o.selected = true;
			}
			// pre-pop model select
			if (s.name == 'model' && CARS.data.preferred.modelName && this.trimModelName(o.text) == this.trimModelName(CARS.data.preferred.modelName)) {
				o.selected = true;
			} 
			// append option
			s.options[i] = o;
		}
	},
	
	
	/* function to brute-force what make we have so that the
	* form doesn't sit with no models
	* 'w': the form to check
	*/
	checkMakeExists: function(w,dataVar) {
		CARS.search.currentStock[w.id] = CARS.search.getSelectedStock(w);
		if (CARS.data.cache[dataVar].options.length === 1) {
			var dm = true;
			//CARS.data.preferred.makeID = null;
			dataVar = this.populateMDS('model',this.currentStock[w.id],this.currentBodystyles[w.id],dm);
		}
		return dataVar;
	},

	/* function for actually generating and executing the Search URL
	* 'w': which form to submit
	* returns: null to kill off form action
	*/
	submitSearch: function(w) {
		if (!w || !w['submit']) { return false; };
		// build Search URLs
		var searchURL = 'http://' + this.getSearchDomain();
		// stocktype configuration
		switch (this.getSelectedStock(w)) {
			case 'new':
			searchURL += '/go/search/newBuyIndex.jsp?';
			searchURL += 'stkTyp=N&tracktype=newcc';
			break;
			case 'certified':
			searchURL += '/for-sale/searchresults.action?';
			searchURL += 'stkTyp=U&cpo=Y&tracktype=usedcc';
			break;
			default:
			searchURL += '/for-sale/searchresults.action?';
			searchURL += 'stkTyp=U&tracktype=usedcc';
			break;
		}
		// add bodystyles
		if (this.getSelectedBodystyles(w)) {
			var bodyStyles = this.getSelectedBodystyles(w).split(',');
			searchURL += '&bsId=' + bodyStyles.join('&bsId=');
		}
		// pick up rest of form fields by dynamically grabbing values
		var selects = w.getElementsByTagName('select');
		var inputs = w.getElementsByTagName('input');
		// process selects
		for (var i=0; i<selects.length; i++) {
			var e = selects[i];
			// console.log('selects loop: ' + i + ' of ' + selects.length);
			switch(e.name) {
				// special handler for make
				case 'make':
				if (e.options[e.options.selectedIndex].value) {
					searchURL += '&mkId=' + e.options[e.options.selectedIndex].value;
					searchURL += '&AmbMkId=' + e.options[e.options.selectedIndex].value;
					searchURL += '&AmbMkNm=' + e.options[e.options.selectedIndex].text;
					searchURL += '&make=' + e.options[e.options.selectedIndex].text;
				}
				break;
				// special handler for model
				case 'model':
				if (e.options[e.options.selectedIndex].value) {
					searchURL += '&AmbMdNm=' + escape(this.trimModelName(e.options[e.options.selectedIndex].text));
					searchURL += '&model=' + escape(this.trimModelName(e.options[e.options.selectedIndex].text));
					var mids = e.options[e.options.selectedIndex].value.split(',');
					if (mids.length > 1) { // handle aliases
						searchURL += '&alMdId=' + mids.shift();
						searchURL += '&mdId=' + mids.join('&mdId=');
						searchURL += '&AmbMdId=' + mids.join('&mdId=');
					} else {
						searchURL += '&mdId=' + mids[0];
						searchURL += '&AmbMdId=' + mids[0];
					}
				}
				break;
				// handle any other selects
				default:
				if (e.options[e.options.selectedIndex].value) {
					searchURL += '&' + e.name + '=' + e.options[e.options.selectedIndex].value;
				}
				break;
			}
		}
		// process inputs
		for (var j=0; j<inputs.length; j++) {
			var e = inputs[j];
			switch(e.name) {
				// handle stocktypes
				case 'stocktype':
				// handled by getStockType above
				break;
				case 'bodystyle':
				// handled via getBodyStyles above
				break;
				case 'submit':
				// ignore submit value
				break;
				default:
				if (e.type == 'radio' || e.type == 'checkbox') {
					if (e.checked) {
						searchURL += '&' + e.name + '=' + e.value;
					}
				} else if (e.value) {
					searchURL += '&' + e.name + '=' + e.value;
				} 
				break;
			}
		}
		// go speed go!
		window.location.href = searchURL;
		return false; // suppress form action
	}
}; // end of the Search namespace

CARS.bidiq = {
  myVals: [],
  // the parameters we are watching for
  watchedParams: 'detid,domainid,matchtype,t_se,t_media,t_cmapid,t_adgpid,t_adid,t_keyid,t_mtype',
  // create cookie helper function - should move to common lib
  cc: function (name,value,days,domain) {
    if (days) {
      var date = new Date();
      date.setTime(date.getTime()+(days*24*60*60*1000));
      var expires = "; expires="+date.toGMTString();
    }
    else { var expires = ""; }
    if (domain) { 
      domain = 'domain=' + domain + ';';
    } else { domain = ''; }
    document.cookie = name + "=" + value + expires + "; path=/;" + domain;
  },
  // grab and filter bidiq params from current URL
  parseParams: function() {
    var splitParams = window.location.search.replace('?','').split('&');
    for (var i=0; i<splitParams.length; i++) {
      // if the current param is one of the droids we're looking for
      if (this.watchedParams.match(splitParams[i].split('=')[0])) {
        // add to value collection
        this.myVals.push(splitParams[i]);
      }
    }
  },
  // wrapper around cc common function
  dropCookie: function() {
    this.cc('bidiq', this.myVals.join('&'), 30, '.'+CARS.utility.getTLD());
  },
  // initialization
  init: function() {
    this.parseParams();
    // if we have a collection params to track, drop cookie
    if (this.myVals.length > 0) {
      this.dropCookie();
    }
  }
};

CARS.adProducts.suggestedSearch = {
  // config
  iModel: {}, rModel: {}, invWidgets: [], iTrackingURLs: [], rTrackingURLs: [], iTrackingImages: [], rTrackingImages: [],  _invReported: false,  _resReported: false,
  // function for clearing saved vehicle cookie
  clearSavedVehicle: function() {
    var today = new Date();
    expires = new Date(today.getTime() + 30 * 1000 * 3600 * 24).toGMTString()
    document.cookie = 'SessionInfo=;expires=' + expires + ';path=/;domain=.' + location.hostname;
  },
  // function for pingback of reporting
  triggerInvPingback: function() {
    if (this._invReported) return;
      for (i=0; i<this.iTrackingURLs.length; i++) {
		this.iTrackingImages[i] = new Image();
		this.iTrackingImages[i].src = this.iTrackingURLs[i];
       }
    this._invReported = true;
  },
   triggerResPingback: function() {
    if (this._resReported) return;
      for (i=0; i<this.rTrackingURLs.length; i++) {
		this.rTrackingImages[i] = new Image();
		this.rTrackingImages[i].src = this.rTrackingURLs[i];
       }
    this._resReported = true;
  },
  // prep and check 
  init: function(p,w) {
    if (w=="inventory") {
      this.iMake = p.make;
      this.iModel = p.model;
      this.iTrackingURLs = p.trackingURLs; 
    }
    if (w=="research" || w=="both") {
      this.rMake = p.make;
      this.rModel.name = p.model;
      this.rTrackingURLs = p.trackingURLs;
    }
   // if (prePopSelectByText && popModels) {
      this.fire();
  //  }
  },

  // trigger
  fire: function() {
	var that = this;
  // pre-pop search
	$('form.cars-search-widget').each(function(index) {
		CARS.adProducts.suggestedSearch.invWidgets[index] = this;
	});
	
  if (this.iMake && this.invWidgets.length > 0) {
		CARS.data.preferred.makeName = this.iMake;
		CARS.data.preferred.modelName = this.iModel;
		$(this.invWidgets).each(function(index) {
			// within the function, this refers to the object we're interating over, not CARS.adProducts.suggestedSearch
			if ($('select[name=make] option:selected',this).text() !== that.iMake || $('select[name=model] option:selected',this).text() !== that.iModel) {
				CARS.search.getMakes(this);
			}
		});
		this.triggerInvPingback();
	}	
    //pre-pop crp
	if (this.rMake && document.crpWidget) {
      prePopSelectByText(document.crpWidget.makeid, this.rMake);
      popModels();
      prePopSelectByText(document.crpWidget.modelid, this.rModel.name);
      popYears();	
	    if (prePopSelectByText && popModels) {
		  if(this.iTrackingURLs[0] != this.rTrackingURLs[0]) {
		    this.triggerResPingback();
		  }
		}
		if (popModels) {
		  this.triggerResPingback();
		}
	     
    }
    // clear cookie if it was set
    this.clearSavedVehicle();
  },
  pingbackForHomepage: function (p) {
	this.iTrackingURLs = p; 
	if (this._invReported) return;
      for (i=0; i<this.iTrackingURLs.length; i++) {
		this.iTrackingImages[i] = new Image();
		this.iTrackingImages[i].src = this.iTrackingURLs[i];
       }
    this._invReported = true;
  }
} // end CARS.adProducts.suggestedSearch

CARS.adProducts.adLoader = {
	adArray: [], adId: 0,
	init: function() {
		this.selfRegister();
	},
	selfRegister: function() {
		var that = this;
		$('.ad-load').each(function(index) {
			that.move(this);
		});
	},
	move: function(w) {
		var that = this;
		adId = [];
		adSource = {};
		adTarget = {};
		// get the id of the current ad ad-[id]
		adId = w.id.split('-');
		adId = adId[2];
		
		adSource = $('#ad-container-' + adId).offset();
		adTarget = $('#ad-' + adId).offset();
		
		if ((adSource.top !==  adTarget.top) || (adSource.left !== adTarget.left) ) {
			$('#ad-' + adId).css({
				'position':'absolute', 
				'top': adSource.top + 'px', 
				'left': adSource.left + 'px'
			});
		}
	}
}

