﻿/*
===============================================================================
IMPORTANT!!!

Changes to this file will affect all sites this is used on!
All changes that can affect the api should update the version number.
===============================================================================
*/

(function($) {

	$.dealerLocator = function(options) {

		this.defaults = $.extend({}, options || {});
		this.init();

		return this;
	};

	// Create shortcut for internal use
	var $dl = $.dealerLocator;

	$dl.dealers = null;
	$dl.map = null;
	$dl.geocoder = null;
	$dl.directions = null;
	$dl.mapManager = null;

	$dl.fn = $dl.prototype = {
		dealerLocator: '0.1'
	};

	$dl.fn.extend = $dl.extend = $.extend;

	$dl.fn.extend({

		init: function() {

			var me = this;

			this.loaded = false;

			// Set default values for required options
			this.defaults.mapHolder = this.defaults.mapHolder || $("#map_canvas");
			this.defaults.directionsResultId = this.defaults.directionsResultId || "dl-directions-result";
			this.defaults.btnSubmitId = this.defaults.btnSubmitId || "dl-btn-submit";
			this.defaults.inputId = this.defaults.inputId || "dl-input";
			this.defaults.dealerResultId = this.defaults.dealerResultId || "dl-result";
			this.defaults.defaultTextId = this.defaults.defaultTextId || "dl-default-text";
			this.defaults.userFiltersContainerId = this.defaults.userFiltersContainerId || "dl-filters";
			this.defaults.userFilters = this.defaults.userFilters || [];

			this.defaults.dealerCssClass = this.defaults.dealerCssClass || "dealer";

			this.defaults.defaultMapCenter = this.defaults.defaultMapCenter || new GLatLng(0, 0);
			this.defaults.defaultMapZoom = this.defaults.defaultMapZoom || 3;
			this.defaults.dealerDetailsZoomLevel = this.defaults.dealerDetailsZoomLevel || 10;
			this.defaults.dealersPerPage = this.defaults.dealersPerPage || 5;
			this.defaults.dealersWithNumbers = this.defaults.dealersWithNumbers || 9;
			this.defaults.minSearchResult = this.defaults.minSearchResult || 1;

			this.defaults.markers = this.defaults.markers || null;
			this.defaults.createMarkerIcon = this.defaults.createMarkerIcon || function() { return G_DEFAULT_ICON; };
			this.defaults.listIcons = this.defaults.listIcons || new Array();

			this.defaults.cookieExpireDays = this.defaults.cookieExpireDays || 365;

			this.defaults.dealersUrl = this.defaults.dealersUrl || null;
			this.defaults.directionsUrl = this.defaults.directionsUrl || null;

			var cluster1Icon = new GIcon();
			cluster1Icon.iconSize = new GSize(53, 52);
			cluster1Icon.image = "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m1.png";
			cluster1Icon.iconAnchor = new GPoint(26, 26);
			cluster1Icon.shadowSize = new GSize(0, 0);
			cluster1Icon.infoWindowAnchor = new GPoint(0, 0);

			var cluster2Icon = new GIcon();
			cluster2Icon.iconSize = new GSize(56, 55);
			cluster2Icon.image = "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m2.png";
			cluster2Icon.iconAnchor = new GPoint(28, 27);
			cluster2Icon.shadowSize = new GSize(0, 0);
			cluster2Icon.infoWindowAnchor = new GPoint(0, 0);

			var cluster3Icon = new GIcon();
			cluster3Icon.iconSize = new GSize(66, 65);
			cluster3Icon.image = "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m3.png";
			cluster3Icon.iconAnchor = new GPoint(33, 32);
			cluster3Icon.shadowSize = new GSize(0, 0);
			cluster3Icon.infoWindowAnchor = new GPoint(0, 0);

			this.defaults.cluster1Icon = this.defaults.cluster1Icon || cluster1Icon;
			this.defaults.cluster2Icon = this.defaults.cluster2Icon || cluster2Icon;
			this.defaults.cluster3Icon = this.defaults.cluster3Icon || cluster3Icon;

			this.defaults.useClustering = this.defaults.useClustering || false;
			this.defaults.clusterSize = this.defaults.clusterSize || 100;

			this.defaults.sponsoredDealerId = this.defaults.sponsoredDealerId || null;

			this.defaults.renderDealerInfo = this.defaults.renderDealerInfo || function() { return $(); };
			this.defaults.renderDealer = this.defaults.renderDealer || function() { return $(); };

			this.defaults.loaded = this.defaults.loaded || function() { };
			this.defaults.dealerRendered = this.defaults.dealerRendered || function() { };
			this.defaults.dealerInfoRendered = this.defaults.dealerInfoRendered || function() { };
			this.defaults.mapNumberChanged = this.defaults.mapNumberChanged || function() { };

			this.btnSubmit = $("#" + this.defaults.btnSubmitId);
			this.input = $("#" + this.defaults.inputId);
			this.directionsResult = $("#" + this.defaults.directionsResultId);
			this.dealersResults = $("#" + this.defaults.dealerResultId);
			this.filterContainer = $("#" + this.defaults.userFiltersContainerId);

			this.defaultText = this.dealersResults.find("#" + this.defaults.defaultTextId);

			this.bindFilters()

			this.defaults.dealersLoaded = function(dealers) {

				me.map = $dl.map = me.mapManager.map;
				me.geocoder = $dl.geocoder = me.mapManager.geocoder;
				me.directions = $dl.directions = me.mapManager.directions;
				me.dealers = $dl.dealers = dealers;
				me.clusters = [];
				me.bindEvents();
				me.addDealersOnMap();

				if ($("#" + me.defaults.inputId).attr("value") != "" && $("#" + me.defaults.inputId).attr("value") != undefined) {
					me.doSearch(me.input.attr("value"));
				}

				me.renderDealers();
			};

			this.mapManager = $dl.mapManager = new $.mapManager(this.defaults);
			this.mapManager.init();

			$(window).unload(function() {
				GUnload();
			});

		},

		bindFilters: function() {

			var me = this;

			me.activeFilters = [];
			me.createUserFilters(me.activeFilters);
		},

		createUserFilters: function(filters) {

			var me = this;

			$(this.defaults.userFilters).each(function() {

				var f = this;

				if (f.showOnlyIcon)
					return;

				var cb = $('<input type="checkbox" id="' + this.id + '" />').click(function() {

					if ($(this).is(":checked"))
						filters.push(f);
					else {
						$(filters).each(function(i, e) {
							if (f.id == e.id)
								filters.splice(i, 1);
						});
					}

					me.filterDealers(filters);

				});

				var label = $('<label for="' + this.id + '">' + this.label + '</label>');

				if (this.iconSmallUrl) {
					label.addClass("has-icon").css("background-image", "url(" + this.iconSmallUrl + ")");
				}

				$('<div class="user-filter"></div>').append(cb).append(label).appendTo(me.filterContainer);

				if (this.checked) {
					cb.attr("checked", "checked");
				}
			});
		},

		filterDealers: function(activeFilters) {

			var me = this;
			var changed = false;

			var dealersLength = me.dealers.length;
			while (dealersLength--) {

				var d = me.dealers[dealersLength];
				var dVisible = true;

				var c = activeFilters.length;
				while (c--) {
					if (!activeFilters[c].userMatchFilter(d)) {
						c = 0;
						dVisible = false;
					}
				}

				if (!dVisible) {
					if (!d.hidden) {
						me.map.removeOverlay(d.marker);
						changed = true;
						d.hidden = true;
					}


					if (d.el)
						d.el.hide();

				}
				else {
					if (d.hidden) {
						//me.map.addOverlay(d.marker);
						changed = true;
						d.hidden = false;
					}

				}
			}

			if (changed)
				GEvent.trigger(me.map, "moveend");
		},

		bindEvents: function() {

			var me = this;

			this.btnSubmit.click(function(e) {
				e.preventDefault();
				me.doSearch(me.input.attr("value"));
				return false;
			});

			this.input.keypress(function(e) {
				if (e.which == 13) {
					e.preventDefault();
					me.doSearch(me.input.attr("value"));
				}
			});

			this.input.autocompleteFunction($dl.mapsSearch, { "onItemSelect": function() { me.doSearch(me.input.attr("value")) }, autoFill: false, delay: 100 });

			GEvent.addListener(me.map, "moveend", function() {
				me.renderDealers();
			});
		},

		renderDealers: function() {

			var me = this;

			var zoom = me.map.getZoom();
			var bounds = me.map.getBounds();

			var unsortedDealersOnMap = new Array();

			$(me.dealers).each(function() {

				if (!this.hidden && bounds.containsLatLng(this.point)) {
					unsortedDealersOnMap.push(this);
				}
				else {
					me.removeMarker(this.marker);

					if (this.el != null)
						this.el.hide();
				}
			});

			var dealersOnMap = me.getClosestDealers(0, me.map.getCenter(), bounds, unsortedDealersOnMap);

			if (dealersOnMap.length > me.defaults.clusterSize && me.defaults.useClustering) {
				$(dealersOnMap).each(function() {
					me.removeMarker(this.marker);

					if (this.el != null)
						this.el.hide();
				});

				me.groupDealersByArea(dealersOnMap, function(groups) {

					$(me.clusters).each(function() {
						me.removeMarker(this.marker);
					});

					if (zoom != me.map.getZoom())
						return;

					$(groups).each(function() {

						me.addMarker(this.marker);
						me.clusters.push(this);
					});

					me.defaultText.show();

					if (!me.loaded && me.defaults.loaded != null && typeof me.defaults.loaded == "function") {
						me.defaults.loaded(me);
						this.loaded = true;
					}

				});
			}
			else if (dealersOnMap.length > 0) {

				me.removeAllCLusters();
				me.defaultText.hide();

				me.numberDealers(dealersOnMap);

				if (zoom < me.defaults.dealerDetailsZoomLevel)
					me.defaultText.show();

				if (!me.loaded && me.defaults.loaded != null && typeof me.defaults.loaded == "function") {
					me.loaded = true;
					me.defaults.loaded(me);
				}
			}
			else {
				me.removeAllCLusters();
				me.defaultText.show();

				if (!me.loaded && me.defaults.loaded != null && typeof me.defaults.loaded == "function") {
					me.defaults.loaded(me);
					this.loaded = true;
				}
			}
		},

		removeAllCLusters: function() {
			var me = this;
			var i = me.clusters.length;
			while (i--) {
				me.removeMarker(me.clusters[i].marker);
			}
		},

		groupDealersByArea: function(dealersOnMap, callback) {

			var me = this;

			var b = this.map.getBounds();
			var p1 = b.getNorthEast();
			var p2 = b.getSouthWest();
			var p = this.map.getCurrentMapType().getProjection();
			var zoom = this.map.getZoom();

			var ne = p.fromLatLngToPixel(p1, zoom);
			var sw = p.fromLatLngToPixel(p2, zoom);

			var tileSize = 64;

			var groups = [];
			var dealers = dealersOnMap;

			var startX = Math.round(sw.x / tileSize);
			var maxX = Math.round(ne.x / tileSize);
			var startY = Math.round(ne.y / tileSize) + 1;
			var maxY = Math.round(sw.y / tileSize) + 1;

			var f = function(dealers, x, y, zoom, tileSize, projection) {

				if (x == maxX && y == maxY) {
					callback(groups);
					return;
				}

				var result = me.groupDealerByGrid(dealers, x, y, zoom, tileSize, projection);

				if (result && result.group)
					groups.push(result.group);

				if (result && result.remainingDealers)
					dealers = result.remainingDealers;

				if (y == maxY) {
					x++;
					y = startY;
				}
				else
					y++;

				var caller = arguments.callee;

				setTimeout(function() {
					caller(dealers, x, y, zoom, tileSize, projection);
				}, 10);
			};

			f(dealers, startX, startY, zoom, tileSize, p);

			return [];
		},

		groupDealerByGrid: function(dealers, x, y, zoom, tileSize, projection) {

			var remainingDealers = [];
			var dealersInTile = [];

			var tsw = projection.fromPixelToLatLng(new GPoint(x * tileSize, (y + 1) * tileSize), zoom);
			var tne = projection.fromPixelToLatLng(new GPoint((x + 1) * tileSize, y * tileSize), zoom);
			var bounds = new GLatLngBounds(tsw, tne);

			var i = dealers.length;
			while (i--) {
				var d = dealers[i];
				if (bounds.containsLatLng(d.point)) {
					dealersInTile.push(d);
				}
				else {
					remainingDealers.push(d);
				}
			}

			if (dealersInTile.length > 0) {

				var group = {
					dealers: dealersInTile,
					marker: this.createCluster(bounds, dealersInTile),
					x: x,
					y: y,
					zoom: zoom,
					bounds: bounds
				};

				dealers = remainingDealers;
				return { group: group, remainingDealers: remainingDealers };
			}
			else {
				return null;
			}
		},

		createCluster: function(bounds, dealers) {

			var opts = {};

			var iconsSize = 1;
			if (dealers.length > 100)
				iconsSize = 3;
			else if (dealers.length > 10)
				iconsSize = 2;

			var icon = G_DEFAULT_ICON;
			if (iconsSize == 1) {
				icon = this.defaults.cluster1Icon;
			}
			else if (iconsSize == 2) {
				icon = this.defaults.cluster2Icon;
			}
			else if (iconsSize == 3) {
				icon = this.defaults.cluster3Icon;
			}

			var sumLng = 0;
			var sumLat = 0;

			$(dealers).each(function() {
				sumLng += this.marker.getLatLng().lng();
				sumLat += this.marker.getLatLng().lat();
			});

			var point = new GLatLng(sumLat / dealers.length, sumLng / dealers.length);

			opts.icon = icon;
			opts.title = dealers.length.toString();

			return new MarkerLight(point, opts);
		},

		addDealersOnMap: function() {

			if (this.dealers.length == 0)
				return;
			var c = this.dealers.length;
			while (c--) {
				var d = this.dealers[c];
				d.el = null;
				d.mapNumber = this.defaults.dealersWithNumbers + 1;
				this.createMarker(d);
			}
		},

		numberDealers: function(dealers) {

			var me = this;
			var zoom = me.map.getZoom();

			this.dealersResults[0].scrollTop = 0;

			$(dealers).each(function(i) {
				this.mapNumber = zoom < me.defaults.dealerDetailsZoomLevel ? me.defaults.dealersWithNumbers + 1 : i + 1;
				me.numberDealer(dealers, this);
			});
		},

		numberDealer: function(dealers, dealer) {

			this.updateMarker(dealer);

			if (this.map.getZoom() < this.defaults.dealerDetailsZoomLevel || (dealer.mapNumber > this.defaults.dealersPerPage && this.defaults.dealersPerPage != -1)) {
				if (dealer.el)
					dealer.el.hide();
				return;
			}

			var me = this;

			if (dealer.el == null)
				dealer.el = this.renderDealer(dealer);

			this.dealersResults.append(dealer.el);
			dealer.el.click(function() {
				me.renderDealerInfo(dealer);
			});

			dealer.el.attr("class", "");
			dealer.el.addClass(this.defaults.dealerCssClass).addClass("dealer-" + dealer.mapNumber).show();
		},

		updateMarker: function(dealer) {

			if (!dealer.marker || dealer.hidden)
				return;

			var icon = this.createIcon(dealer);

			if (dealer.marker.opts.icon.image != icon.image) {

				var marker = new MarkerLight(dealer.point, { icon: icon });

				this.map.removeOverlay(dealer.marker);
				dealer.marker = marker;
				this.map.addOverlay(dealer.marker);

				var me = this;
				GEvent.addListener(dealer.marker, "click", function() {
					me.renderDealerInfo(dealer);
				});

				this.defaults.mapNumberChanged(dealer, this.defaults.markers);
			}
			else if (!dealer.marker.isOnMap()) {
				this.map.addOverlay(dealer.marker);
			}
		},

		removeMarker: function(marker) {

			if (!marker || !marker.isOnMap())
				return;

			this.map.removeOverlay(marker);
		},

		addMarker: function(marker) {

			if (marker.isOnMap() || !marker)
				return;

			this.map.addOverlay(marker);
		},

		createMarker: function(dealer) {

			var icon = this.createIcon(dealer);
			dealer.marker = new MarkerLight(dealer.point, { icon: icon });
			var me = this;
			GEvent.addListener(dealer.marker, "click", function() {
				me.renderDealerInfo(dealer);
			});
		},

		renderDealer: function(dealer) {

			var el = $("<div></div>");
			var me = this;

			if (dealer.extendedDealer == null) {

				$dl.mapManager.getExtendedDealer(dealer.id, function(extendedDealer) {

					dealer.extendedDealer = extendedDealer;
					me.defaults.renderDealer(dealer, el, me.defaults.markers);
					me.defaults.dealerRendered(dealer, el, me.defaults.markers);
				});
			}
			else {

				this.defaults.renderDealer(dealer, el, this.defaults.markers);
				this.defaults.dealerRendered(dealer, el, this.defaults.markers);
			}

			return el;
		},

		renderDealerInfo: function(dealer) {

			var me = this;

			var el = $("<div></div>");

			if (dealer.extendedDealer == null) {

				var me = this;

				$dl.mapManager.getExtendedDealer(dealer.id, function(extendedDealer) {

					dealer.extendedDealer = extendedDealer;
					me.defaults.renderDealerInfo(dealer, el, me.defaults.markers);
					me.defaults.dealerInfoRendered(dealer, el, me.defaults.markers);
				});
			}
			else {

				this.defaults.renderDealerInfo(dealer, el, this.defaults.markers);
				this.defaults.dealerInfoRendered(dealer, el, this.defaults.markers);
			}

			this.dealersResults.prepend(el).addClass("dealer-info-visibe");

			if (this.dealers.length > 1) {
				var handler = GEvent.addListener(this.map, "moveend", function() {
					el.remove();
					me.dealersResults.removeClass("dealer-info-visibe");
					GEvent.removeListener(handler);
				});
			}

			return el;
		},

		getClosestDealers: function(count, point, bounds, dealers) {

			return $dl.mapManager.getClosestDealers(count, point, bounds, dealers);
		},

		doSearch: function(q) {

			var dealerHits = $dl.dealerSearchByExactName(q);

			if (dealerHits.length > 0) {

				this.map.setCenter(dealerHits[0].point, this.defaults.dealerDetailsZoomLevel);
				this.renderDealerInfo(dealerHits[0]);
			}
			else {

				var me = this;

				$dl.addressSearch(q, $dl.map.getBounds(), function(result) {

					if (result == null || result.length == 0)
						return;

					var point = new GLatLng(result[0].Point.coordinates[1], result[0].Point.coordinates[0]);

					var closestDealers = me.getClosestDealers(me.defaults.minSearchResult, point);

					if (closestDealers.length == 0) {
						me.map.setCenter(point, me.defaults.dealerDetailsZoomLevel);
						return;
					}

					var bounds = new GLatLngBounds();

					$(closestDealers).each(function() {
						bounds.extend(this.point);
					});

					var ne = bounds.getNorthEast();
					var sw = bounds.getSouthWest();

					var lat = point.lat() - sw.lat() > ne.lat() - point.lat() ?
								point.lat() + (point.lat() - sw.lat()) :
								point.lat() - (ne.lat() - point.lat());

					var lng = ne.lng() - point.lng() > point.lng() - sw.lng() ?
								point.lng() - (ne.lng() - point.lng()) :
								point.lng() + (point.lng() - sw.lng());

					bounds.extend(new GLatLng(lat, lng));

					var boundsZoom = me.map.getBoundsZoomLevel(bounds);
					if (boundsZoom > me.defaults.dealerDetailsZoomLevel)
						boundsZoom = me.defaults.dealerDetailsZoomLevel;

					me.map.setCenter(bounds.getCenter(), boundsZoom);

				});
			}

			if (this.defaults.cookieExpireDays != 0)
				$.cookie('DealerSearch', q, { expires: this.defaults.cookieExpireDays, path: '/' });
		},

		createIcon: function(dealer) {
			return this.defaults.createMarkerIcon(dealer, this.defaults.markers);
		}
	});

	$dl.mapsSearch = function(q, func) {
		return $dl.mapManager.mapsSearch(q, $dl.map.getBounds(), func);
	};

	$dl.addressSearch = function(q, bounds, func) {

		return $dl.mapManager.addressSearch(q, bounds, func);
	};

	$dl.dealerSearchByName = function(q) {

		return $dl.mapManager.dealerSearchByName(q);
	};

	$dl.dealerSearchByExactName = function(q) {
		return $dl.mapManager.dealerSearchByExactName(q);
	}

})(jQuery);


(function($) {

	$.drivingDirections = function(options) {

		this.defaults = $.extend({}, options || {});
		this.init();

		return this;
	};

	// Create shortcut for internal use
	var $dd = $.drivingDirections;

	$dd.dealers = null;
	$dd.map = null;
	$dd.geocoder = null;
	$dd.mapManager = null;
	$dd.directions = null;

	$dd.fn = $dd.prototype = {
		drivingDirections: '0.1'
	};

	$dd.fn.extend = $dd.extend = $.extend;

	$dd.fn.extend({

		init: function() {

			var me = this;

			this.defaults.mapHolder = this.defaults.mapHolder || $("#map_canvas");
			this.defaults.fromInput = this.defaults.fromInput || $("#from");
			this.defaults.toInput = this.defaults.toInput || $("#to");
			this.defaults.submit = this.defaults.submit || null;
			this.defaults.result = this.defaults.result || null;
			this.defaults.defaultMapCenter = this.defaults.defaultMapCenter || new GLatLng(0, 0);
			this.defaults.defaultMapZoom = this.defaults.defaultMapZoom || 3;
			this.defaults.from = this.defaults.from || "";
			this.defaults.to = this.defaults.to || "";
			this.defaults.dealerId = this.defaults.dealerId || "";

			this.defaults.txtHeadline = this.defaults.txtHeadline || "Driving directions from {0} to {1}";
			this.defaults.txtError = this.defaults.txtError || "The address you searched for can not be found, please try again!";
			this.defaults.loaded = this.defaults.loaded || function() { };


			this.defaults.dealersLoaded = function(dealers) {

				me.map = $dd.map = me.mapManager.map;
				me.geocoder = $dd.geocoder = me.mapManager.geocoder;
				me.directions = $dd.directions = me.mapManager.directions;

				me.dealers = $dd.dealers = dealers;

				me.directions = $dd.directions = new GDirections(me.map, me.defaults.result[0]);

				me.bindEvents();

				if (me.defaults.dealerId != "") {
					var c = me.dealers.length;
					var tid = me.defaults.dealerId.toLowerCase();
					while (c--) {
						if (me.dealers[c].id.toLowerCase() == tid) {
							me.defaults.toInput.attr("value", me.dealers[c].miniDealer.n);
							me.defaults.to = me.dealers[c].miniDealer.n;
						}
					}
				}

				if (me.defaults.from != "" && me.defaults.to != "") {
					var from = me.defaults.fromInput.attr("value");
					var to = me.defaults.toInput.attr("value");
					me.loadDirectionsFromQuery(me.getQuery(from, to), from, to);
				}

				if ($.isFunction(me.defaults.loaded))
					me.defaults.loaded();
			};

			this.defaults.fromInput.attr("value", this.defaults.from);
			this.defaults.toInput.attr("value", this.defaults.to);

			this.mapManager = $dd.mapManager = new $.mapManager(this.defaults);
			this.mapManager.init();
		},

		bindEvents: function() {

			var me = this;

			$(this.defaults.fromInput).autocompleteFunction($dd.mapsSearch, { autoFill: false, delay: 100 });
			$(this.defaults.toInput).autocompleteFunction($dd.mapsSearch, { autoFill: false, delay: 100 });


			this.defaults.submit.click(function(e) {
				e.preventDefault();
				var from = me.defaults.fromInput.attr("value");
				var to = me.defaults.toInput.attr("value");
				me.loadDirectionsFromQuery(me.getQuery(from, to), from, to);
				return false;
			});

			me.defaults.toInput.keypress(function(e) {
				if (e.which == 13) {
					e.preventDefault();
					var from = me.defaults.fromInput.attr("value");
					var to = me.defaults.toInput.attr("value");
					me.loadDirectionsFromQuery(me.getQuery(from, to), from, to);
				}
			});

			me.defaults.fromInput.keypress(function(e) {
				if (e.which == 13) {
					e.preventDefault();
					var from = me.defaults.fromInput.attr("value");
					var to = me.defaults.toInput.attr("value");
					me.loadDirectionsFromQuery(me.getQuery(from, to), from, to);
				}
			});

			GEvent.addListener(this.directions, "error", function() {
				me.defaults.result.empty();
				me.defaults.result.append('<h2 class="error">' + me.defaults.txtError + '</h2>');
			});
		},

		getQuery: function(fromValue, toValue) {

			var query = null;

			var fromD = $dd.dealerSearchByExactName(fromValue);

			if (fromD.length > 0) {
				query = "from: " + fromD[0].point.lat() + "," + fromD[0].point.lng();
			}
			else {
				query = "from: " + fromValue;
			}

			var toD = $dd.dealerSearchByExactName(toValue);

			if (toD.length > 0) {
				query += " to: " + toD[0].point.lat() + "," + toD[0].point.lng();
			}
			else {
				query += " to: " + toValue;
			}

			return query;
		},

		loadDirectionsFromQuery: function(query, fromValue, toValue) {
			this.defaults.result.empty();
			this.defaults.result.append("<h2>" + this.defaults.txtHeadline.replace("{0}", fromValue).replace("{1}", toValue) + "</h2>");
			this.directions.load(query);

			var result = this.directions.getStatus();
		}
	});

	$dd.mapsSearch = function(q, func) {
		return $dd.mapManager.mapsSearch(q, $dd.map.getBounds(), func);
	};

	$dd.addressSearch = function(q, bounds, func) {

		return $dd.mapManager.addressSearch(q, bounds, func);
	};

	$dd.dealerSearchByName = function(q) {

		return $dd.mapManager.dealerSearchByName(q);
	};

	$dd.dealerSearchByExactName = function(q) {

		return $dd.mapManager.dealerSearchByExactName(q);
	};

})(jQuery);



(function($) {

	$.dealerMiniBox = function(options) {

		this.defaults = $.extend({}, options || {});
		this.init();

		return this;
	};

	// Create shortcut for internal use
	var $dmb = $.dealerMiniBox;

	$dmb.dlBase = null;

	$dmb.fn = $dmb.prototype = {
		dealerMiniBox: '0.1'
	};

	$dmb.fn.extend = $dmb.extend = $.extend;

	$dmb.fn.extend({

		init: function() {

			var me = this;

			this.defaults.input = this.defaults.input || null;

			this.defaults.dealersLoaded = function(dealers) {
				me.bindEvents();
			};

			this.dlBase = $dmb.dlBase = new $.dealerLocatorBase(this.defaults);
			this.dlBase.init();
		},

		bindEvents: function() {
			this.defaults.input.autocompleteFunction($dmb.mapsSearch, { autoFill: false, delay: 100 });
		}
	});

	$dmb.mapsSearch = function(q, func) {
		return $dmb.dlBase.mapsSearch(q, null, func);
	};

})(jQuery);



(function($) {

	$.mapManager = function(options) {

		this.defaults = $.extend({}, options || {});

		return this;
	};

	// Create shortcut for internal use
	var $mm = $.mapManager;

	$mm.dealers = null;
	$mm.map = null;
	$mm.dlBase = null;

	$mm.fn = $mm.prototype = {
		dealerManager: '0.1'
	};

	$mm.fn.extend = $mm.extend = $.extend;

	$mm.fn.extend({

		init: function() {

			var me = this;

			this.defaults.mapHolder = this.defaults.mapHolder || $("#map_canvas");
			this.defaults.defaultMapCenter = this.defaults.defaultMapCenter || new GLatLng(0, 0);
			this.defaults.defaultMapZoom = this.defaults.defaultMapZoom || 3;

			var height = this.defaults.mapHolder.height();
			var width = this.defaults.mapHolder.width();

			var parent = this.defaults.mapHolder.parent();

			var useOffsetFix = (height == 0 || width == 0);

			if (useOffsetFix)
				this.defaults.mapHolder.css({ "position": "absolute", "top": "-9999px", "left": "-9999px" }).appendTo($("body"));

			this.map = $mm.map = new GMap2(this.defaults.mapHolder[0]);
			this.map.setCenter(this.defaults.defaultMapCenter, this.defaults.defaultMapZoom);
			this.map.setUIToDefault();

			if (useOffsetFix)
				me.defaults.mapHolder.css({ "position": "relative", "left": "auto", "top": "auto" }).appendTo(parent);

			var dealersLoaded = this.defaults.dealersLoaded;

			this.defaults.dealersLoaded = function(dealers) {
				me.dealers = $mm.dealers = dealers;
				if (dealersLoaded != null && typeof dealersLoaded == "function")
					dealersLoaded(dealers);
			};

			this.dlBase = $mm.dlBase = new $.dealerLocatorBase(this.defaults);
			this.dlBase.init();
		},

		resetMap: function() {
			this.map.checkResize();
		},

		getClosestDealers: function(count, point, bounds, dealers) {

			if (typeof (dealers) == "undefined" || dealers == null)
				dealers = this.dealers;

			if (dealers.length == 0)
				return new Array();

			var d = new Array();

			if (bounds != null) {
				d = $.grep(dealers, function(n, i) {
					return bounds.containsLatLng(n.point) && !n.hidden;
				});
			}
			else {
				d = $.grep(dealers, function(n, i) {
					return !n.hidden;
				});
			}

			$(d).each(function() {
				this.distanceFromCenter = this.point.distanceFrom(point);
			});

			var sortedDealers = d.sort(function(a, b) {
				return a.distanceFromCenter - b.distanceFromCenter;
			});

			if (count == 0)
				return sortedDealers;

			return sortedDealers.slice(0, count);
		},

		mapsSearch: function(q, bounds, func) {
			return this.dlBase.mapsSearch(q, bounds, func);
		},

		addressSearch: function(q, bounds, func) {

			return this.dlBase.addressSearch(q, bounds, func);
		},

		dealerSearchByName: function(q) {
			return this.dlBase.dealerSearchByName(q);
		},

		dealerSearchByExactName: function(q) {
			return this.dlBase.dealerSearchByExactName(q);
		},

		getExtendedDealer: function(dealerId, callback) {
			return this.dlBase.getExtendedDealer(dealerId, callback);
		}
	});

})(jQuery);


var dlb;

(function($) {

	$.dealerLocatorBase = function(options) {

		this.defaults = $.extend({}, options || {});
		dlb = this;
		return this;
	};

	// Create shortcut for internal use
	var $dlb = $.dealerLocatorBase;

	$dlb.dealers = null;
	$dlb.geocoder = null;

	$dlb.fn = $dlb.prototype = {
		dealerLocatorBase: '0.1'
	};

	$dlb.fn.extend = $dlb.extend = $.extend;

	$dlb.fn.extend({

		init: function() {

			this.defaults.dealersUrl = this.defaults.dealersUrl || "http://rest.dim.husqvarna.com/rest/gethqdealers.ashx";
			this.defaults.dealerUrl = this.defaults.dealerUrl || "http://rest.dim.husqvarna.com/rest/gethqdealer.ashx";
			this.defaults.dealersLoaded = this.defaults.dealersLoaded || null;
			this.defaults.brandId = this.defaults.brandId || null;
			this.defaults.node = this.defaults.node || null;
			this.defaults.channel = this.defaults.channel || null;
			this.defaults.filters = this.defaults.filters || null;
			this.defaults.createDealerName = this.defaults.createDealerName || function(d) { return d.miniDealer.n; };

			this.defaults.geoCodePrefix = this.defaults.geoCodePrefix || null;

			this.defaults.sponsoredDealerId = this.defaults.sponsoredDealerId || null;

			this.geocoder = $dlb.geocoder = new GClientGeocoder();

			this.dealers = $dlb.dealers = new Array();

			this.loadDealers(this.defaults.dealersLoaded);
		},

		loadDealers: function(callback) {

			var me = this;

			this.getDealers(new Array(), 0, 2000, function(dealers) {

				$(dealers).each(function(i) {

					var dealer = { miniDealer: this, extendedDealer: null };

					dealer.id = this.i;
					dealer.hidden = false;
					var p = this.p.split(",");
					dealer.point = new GLatLng(p[0], p[1]);

					if (me.defaults.sponsoredDealerId == null || me.defaults.sponsoredDealerId.toLowerCase() == dealer.id.toLowerCase()) {
						me.dealers.push(dealer);
					}
				});

				if (me.defaults.sponsoredDealerId != null && me.dealers.length == 0 && dealers.length > 0) {
					$(dealers).each(function(i) {
						var dealer = { miniDealer: this, extendedDealer: null };
						dealer.id = this.i;
						var p = this.p.split(",");
						dealer.point = new GLatLng(p[0], p[1]);
						me.dealers.push(dealer);
					});
				}

				if (callback != null && typeof callback == "function")
					callback(me.dealers);

			});
		},

		getExtendedDealer: function(dealerId, callback) {

			var url = this.defaults.dealerUrl + "?dealer=" + dealerId + (this.defaults.channel != null ? "&channel=" + this.defaults.channel : "") + "&callback=?";

			$.getJSON(url, function(dealer) {
				callback(dealer);
			});
		},

		getDealers: function(dealers, page, pageSize, callback) {

			var me = this;

			me.dealersLoadedCallback = callback;
			me.tempDealers = dealers;
			me.tempPage = page;
			me.tempPageSize = pageSize;

			var url = this.defaults.dealersUrl + "?pageNumber=" + page + "&pageSize=" + pageSize + (this.defaults.channel != null ? "&channel=" + this.defaults.channel : "") + (this.defaults.brandId != null ? "&brand=" + this.defaults.brandId : "") + (this.defaults.node != null ? "&node=" + this.defaults.node : "") + "&callback=dlb.callbackHandler";

			if (me.defaults.filters != null && me.defaults.filters.length > 0) {
				var filter = "&filter=";

				$(me.defaults.filters).each(function() {
					filter += this.progId + "," + this.expected + "|";
				});

				url += filter;
			}

			jQuery.ajax({ url: url, dataType: "script", type: "GET", cache: false, callback: null, data: null });
		},

		callbackHandler: function(data) {

			if (data.length == 0) {
				this.dealersLoadedCallback(this.tempDealers);
			}
			else {
				var result = this.tempDealers.concat(data);
				this.getDealers(result, this.tempPage + 1, this.tempPageSize, this.dealersLoadedCallback);
			}
		},

		mapsSearch: function(q, bounds, func) {

			var me = this;

			var data = new Array();

			$(this.dealerSearchByName(q)).each(function() {
				data.push([me.defaults.createDealerName(this)]);
			});

			this.addressSearch(q, bounds, function(result) {

				$(result).each(function() {
					data.push([this.address]);
				});

				func(data);
			});
		},

		addressSearch: function(q, bounds, func) {

			var data = new Array();

			if (this.geocoder) {

				/*
				var oldBounds = this.geocoder.getViewport();

				if (bounds != undefined && bounds != null)
					this.geocoder.setViewport(bounds);
				*/

				if (this.defaults.geoCodePrefix != null)
					q = this.defaults.geoCodePrefix + " " + q;

				this.geocoder.getLocations(q, function(response) {

					if (response && response.Status.code == 200) {

						$(response.Placemark).each(function() {
							data.push(this);
						});
					}

					func(data);
				});

				/*
				if (bounds != undefined & bounds != null)
					this.geocoder.setViewport(oldBounds);
				*/
			}
		},

		dealerSearchByName: function(q) {

			var me = this;

			if (q == "")
				return new Array();

			var result = $.grep(this.dealers, function(o, i) {
				return me.defaults.createDealerName(o).toLowerCase().indexOf(q.toLowerCase()) == 0 && !o.hidden;
			});

			return result;
		},

		dealerSearchByExactName: function(q) {

			var me = this;

			if (q == "")
				return new Array();

			var result = $.grep(this.dealers, function(o, i) {
				return me.defaults.createDealerName(o).toLowerCase() == q.toLowerCase() && !o.hidden;
			});

			return result;
		},

		dealerSearchById: function(id) {

			var result = $.grep(this.dealers, function(o, i) {
				return o.id == id;
			});

			return result;
		}
	});

})(jQuery);


jQuery.autocomplete = function(input, options) {
	// Create a link to self
	var me = this;

	// Create jQuery object for input element
	var $input = $(input).attr("autocomplete", "off");

	// Apply inputClass if necessary
	if (options.inputClass) $input.addClass(options.inputClass);

	// Create results
	var results = document.createElement("div");
	// Create jQuery object for results
	var $results = $(results);
	$results.hide().addClass(options.resultsClass).css("position", "absolute");
	if (options.width > 0) $results.css("width", options.width);

	// Add to body element
	$("body").append(results);

	input.autocompleter = me;

	var timeout = null;
	var prev = "";
	var active = -1;
	var cache = {};
	var keyb = false;
	var hasFocus = false;
	var lastKeyPressCode = null;

	// flush cache
	function flushCache() {
		cache = {};
		cache.data = {};
		cache.length = 0;
	};

	// flush cache
	flushCache();

	// if there is a data array supplied
	if (options.data != null) {
		var sFirstChar = "", stMatchSets = {}, row = [];

		// no url was specified, we need to adjust the cache length to make sure it fits the local data store
		if (typeof options.url != "string") options.cacheLength = 1;

		// loop through the array and create a lookup structure
		for (var i = 0; i < options.data.length; i++) {
			// if row is a string, make an array otherwise just reference the array
			row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);

			// if the length is zero, don't add to list
			if (row[0].length > 0) {
				// get the first character
				sFirstChar = row[0].substring(0, 1).toLowerCase();
				// if no lookup array for this character exists, look it up now
				if (!stMatchSets[sFirstChar]) stMatchSets[sFirstChar] = [];
				// if the match is a string
				stMatchSets[sFirstChar].push(row);
			}
		}

		// add the data items to the cache
		for (var k in stMatchSets) {
			// increase the cache size
			options.cacheLength++;
			// add to the cache
			addToCache(k, stMatchSets[k]);
		}
	}

	$input
	.keydown(function(e) {
		// track last key pressed
		lastKeyPressCode = e.keyCode;
		switch (e.keyCode) {
			case 38: // up
				e.preventDefault();
				moveSelect(-1);
				break;
			case 40: // down
				e.preventDefault();
				moveSelect(1);
				break;
			case 9:  // tab
			case 13: // return
				if (selectCurrent()) {
					// make sure to blur off the current field
					$input.get(0).blur();
					e.preventDefault();
				}
				break;
			default:
				active = -1;
				if (timeout) clearTimeout(timeout);
				timeout = setTimeout(function() { onChange(); }, options.delay);
				break;
		}
	})
	.focus(function() {
		// track whether the field has focus, we shouldn't process any results if the field no longer has focus
		hasFocus = true;
	})
	.blur(function() {
		// track whether the field has focus
		hasFocus = false;
		hideResults();
	});

	hideResultsNow();

	function onChange() {
		// ignore if the following keys are pressed: [del] [shift] [capslock]
		if (lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32)) return $results.hide();
		var v = $input.val();
		if (v == prev) return;
		prev = v;
		if (v.length >= options.minChars) {
			$input.addClass(options.loadingClass);
			requestData(v);
		} else {
			$input.removeClass(options.loadingClass);
			$results.hide();
		}
	};

	function moveSelect(step) {

		var lis = $("li", results);
		if (!lis) return;

		active += step;

		if (active < 0) {
			active = 0;
		} else if (active >= lis.size()) {
			active = lis.size() - 1;
		}

		lis.removeClass("ac_over");

		$(lis[active]).addClass("ac_over");

		// Weird behaviour in IE
		// if (lis[active] && lis[active].scrollIntoView) {
		// 	lis[active].scrollIntoView(false);
		// }

	};

	function selectCurrent() {
		var li = $("li.ac_over", results)[0];
		if (!li) {
			var $li = $("li", results);
			if (options.selectOnly) {
				if ($li.length == 1) li = $li[0];
			} else if (options.selectFirst) {
				li = $li[0];
			}
		}
		if (li) {
			selectItem(li);
			return true;
		} else {
			return false;
		}
	};

	function selectItem(li) {
		if (!li) {
			li = document.createElement("li");
			li.extra = [];
			li.selectValue = "";
		}
		var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
		input.lastSelected = v;
		prev = v;
		$results.html("");
		$input.val(v);
		hideResultsNow();
		if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1);
	};

	// selects a portion of the input string
	function createSelection(start, end) {
		// get a reference to the input element
		var field = $input.get(0);
		if (field.createTextRange) {
			var selRange = field.createTextRange();
			selRange.collapse(true);
			selRange.moveStart("character", start);
			selRange.moveEnd("character", end);
			selRange.select();
		} else if (field.setSelectionRange) {
			field.setSelectionRange(start, end);
		} else {
			if (field.selectionStart) {
				field.selectionStart = start;
				field.selectionEnd = end;
			}
		}
		field.focus();
	};

	// fills in the input box w/the first match (assumed to be the best match)
	function autoFill(sValue) {
		// if the last user key pressed was backspace, don't autofill
		if (lastKeyPressCode != 8) {
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(prev.length));
			// select the portion of the value not typed by the user (so the next character will erase)
			createSelection(prev.length, sValue.length);
		}
	};

	function showResults() {
		// get the position of the input field right now (in case the DOM is shifted)
		var pos = findPos(input);
		// either use the specified width, or autocalculate based on form element
		var iWidth = (options.width > 0) ? options.width : $input.width();
		// reposition
		$results.css({
			width: parseInt(iWidth) + "px",
			top: (pos.y + input.offsetHeight) + "px",
			left: pos.x + "px"
		}).show();
	};

	function hideResults() {
		if (timeout) clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	};

	function hideResultsNow() {
		if (timeout) clearTimeout(timeout);
		$input.removeClass(options.loadingClass);
		if ($results.is(":visible")) {
			$results.hide();
		}
		if (options.mustMatch) {
			var v = $input.val();
			if (v != input.lastSelected) {
				selectItem(null);
			}
		}
	};

	function receiveData(q, data) {
		if (data) {
			$input.removeClass(options.loadingClass);
			results.innerHTML = "";

			// if the field no longer has focus or if there are no matches, do not display the drop down
			if (!hasFocus || data.length == 0) return hideResultsNow();

			if ($.browser.msie) {
				// we put a styled iframe behind the calendar so HTML SELECT elements don't show through
				$results.append(document.createElement('iframe'));
			}
			results.appendChild(dataToDom(data));
			// autofill in the complete box w/the first match as long as the user hasn't entered in more data
			if (options.autoFill && ($input.val().toLowerCase() == q.toLowerCase())) autoFill(data[0][0]);
			showResults();
		} else {
			hideResultsNow();
		}
	};

	function parseData(data) {
		if (!data) return null;
		var parsed = [];
		var rows = data.split(options.lineSeparator);
		for (var i = 0; i < rows.length; i++) {
			var row = $.trim(rows[i]);
			if (row) {
				parsed[parsed.length] = row.split(options.cellSeparator);
			}
		}
		return parsed;
	};

	function dataToDom(data) {
		var ul = document.createElement("ul");
		var num = data.length;

		// limited results to a max number
		if ((options.maxItemsToShow > 0) && (options.maxItemsToShow < num)) num = options.maxItemsToShow;

		for (var i = 0; i < num; i++) {
			var row = data[i];
			if (!row) continue;
			var li = document.createElement("li");
			if (options.formatItem) {
				li.innerHTML = options.formatItem(row, i, num);
				li.selectValue = row[0];
			} else {
				li.innerHTML = row[0];
				li.selectValue = row[0];
			}
			var extra = null;
			if (row.length > 1) {
				extra = [];
				for (var j = 1; j < row.length; j++) {
					extra[extra.length] = row[j];
				}
			}
			li.extra = extra;
			ul.appendChild(li);
			$(li).hover(
				function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
				function() { $(this).removeClass("ac_over"); }
			).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });
		}
		return ul;
	};

	function requestData(q) {
		if (!options.matchCase) q = q.toLowerCase();
		var data = options.cacheLength ? loadFromCache(q) : null;
		// recieve the cached data
		if (data) {
			receiveData(q, data);
			// if an AJAX url has been supplied, try loading the data now
		} else if ((typeof options.url == "string") && (options.url.length > 0)) {
			$.get(makeUrl(q), function(data) {
				data = parseData(data);
				addToCache(q, data);
				receiveData(q, data);
			});
			// if there's been no data found, remove the loading class
		}
		else if (typeof options.func == "function") {

			options.func(q, function(result) {

				//addToCache(q, result);
				receiveData(q, result);
			});
		} else {
			$input.removeClass(options.loadingClass);
		}
	};

	function makeUrl(q) {
		var url = options.url + "?q=" + encodeURI(q);
		for (var i in options.extraParams) {
			url += "&" + i + "=" + encodeURI(options.extraParams[i]);
		}
		return url;
	};

	function loadFromCache(q) {
		if (!q) return null;
		if (cache.data[q]) return cache.data[q];
		if (options.matchSubset) {
			for (var i = q.length - 1; i >= options.minChars; i--) {
				var qs = q.substr(0, i);
				var c = cache.data[qs];
				if (c) {
					var csub = [];
					for (var j = 0; j < c.length; j++) {
						var x = c[j];
						var x0 = x[0];
						if (matchSubset(x0, q)) {
							csub[csub.length] = x;
						}
					}
					return csub;
				}
			}
		}
		return null;
	};

	function matchSubset(s, sub) {
		if (!options.matchCase) s = s.toLowerCase();
		var i = s.indexOf(sub);
		if (i == -1) return false;
		return i == 0 || options.matchContains;
	};

	this.flushCache = function() {
		flushCache();
	};

	this.setExtraParams = function(p) {
		options.extraParams = p;
	};

	function addToCache(q, data) {
		if (!data || !q || !options.cacheLength) return;
		if (!cache.length || cache.length > options.cacheLength) {
			flushCache();
			cache.length++;
		} else if (!cache[q]) {
			cache.length++;
		}
		cache.data[q] = data;
	};

	function findPos(obj) {
		var curleft = obj.offsetLeft || 0;
		var curtop = obj.offsetTop || 0;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
		return { x: curleft, y: curtop };
	}
}

jQuery.fn.autocomplete = function(url, options, data, func) {
	// Make sure options exists
	options = options || {};
	// Set url as option
	options.url = url;
	// set some bulk local data
	options.data = (data != null && (typeof data == "object") && (data.constructor == Array)) ? data : null;

	options.func = typeof func == "function" ? func : null;

	// Set default values for required options
	options.inputClass = options.inputClass || "ac_input";
	options.resultsClass = options.resultsClass || "ac_results";
	options.lineSeparator = options.lineSeparator || "\n";
	options.cellSeparator = options.cellSeparator || "|";
	options.minChars = options.minChars || 1;
	options.delay = options.delay || 400;
	options.matchCase = options.matchCase || 0;
	options.matchSubset = options.matchSubset || 1;
	options.matchContains = options.matchContains || 0;
	options.cacheLength = options.cacheLength || 1;
	options.mustMatch = options.mustMatch || 0;
	options.extraParams = options.extraParams || {};
	options.loadingClass = options.loadingClass || "ac_loading";
	options.selectFirst = options.selectFirst || false;
	options.selectOnly = options.selectOnly || false;
	options.maxItemsToShow = options.maxItemsToShow || -1;
	options.autoFill = options.autoFill || false;
	options.width = parseInt(options.width, 10) || 0;

	this.each(function() {
		var input = this;
		new jQuery.autocomplete(input, options);
	});

	// Don't break the chain
	return this;
}

jQuery.fn.autocompleteArray = function(data, options) {
	return this.autocomplete(null, options, data, null);
}

jQuery.fn.autocompleteFunction = function(func, options) {
	return this.autocomplete(null, options, null, func);
}

jQuery.fn.indexOf = function(e) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] == e) return i;
	}
	return -1;
};


/**
* Cookie plugin
*
* Copyright (c) 2006 Klaus Hartl (stilbuero.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/

/**
* Create a cookie with the given name and value and other optional parameters.
*
* @example $.cookie('the_cookie', 'the_value');
* @desc Set the value of a cookie.
* @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
* @desc Create a cookie with all available options.
* @example $.cookie('the_cookie', 'the_value');
* @desc Create a session cookie.
* @example $.cookie('the_cookie', null);
* @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
*       used when the cookie was set.
*
* @param String name The name of the cookie.
* @param String value The value of the cookie.
* @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
* @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
*                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
*                             If set to null or omitted, the cookie will be a session cookie and will not be retained
*                             when the the browser exits.
* @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
* @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
* @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
*                        require a secure protocol (like HTTPS).
* @type undefined
*
* @name $.cookie
* @cat Plugins/Cookie
* @author Klaus Hartl/klaus.hartl@stilbuero.de
*/

/**
* Get the value of a cookie with the given name.
*
* @example $.cookie('the_cookie');
* @desc Get the value of a cookie.
*
* @param String name The name of the cookie.
* @return The value of the cookie.
* @type String
*
* @name $.cookie
* @cat Plugins/Cookie
* @author Klaus Hartl/klaus.hartl@stilbuero.de
*/
jQuery.cookie = function(name, value, options) {
	if (typeof value != 'undefined') { // name and value given, set cookie
		options = options || {};
		if (value === null) {
			value = '';
			options.expires = -1;
		}
		var expires = '';
		if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
			var date;
			if (typeof options.expires == 'number') {
				date = new Date();
				date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
			} else {
				date = options.expires;
			}
			expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
		}
		// CAUTION: Needed to parenthesize options.path and options.domain
		// in the following expressions, otherwise they evaluate to undefined
		// in the packed version for some reason...
		var path = options.path ? '; path=' + (options.path) : '';
		var domain = options.domain ? '; domain=' + (options.domain) : '';
		var secure = options.secure ? '; secure' : '';
		document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
	} else { // only name given, get cookie
		var cookieValue = null;
		if (document.cookie && document.cookie != '') {
			var cookies = document.cookie.split(';');
			for (var i = 0; i < cookies.length; i++) {
				var cookie = jQuery.trim(cookies[i]);
				// Does this cookie string begin with the name we want?
				if (cookie.substring(0, name.length + 1) == (name + '=')) {
					cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
					break;
				}
			}
		}
		return cookieValue;
	}
};

function searchById(dl, id) {
	var c = dl.dealers.length;
	var tid = id.toLowerCase();
	while (c--) {
		if (dl.dealers[c].id.toLowerCase() == tid) {
			$("#dlSearchBox").attr("value", dl.dealers[c].miniDealer.n);
			dl.doSearch(dl.dealers[c].miniDealer.n);
			return true;
		}
	}

	return false;
}

/* A Bar is a simple overlay that outlines a lat/lng bounds on the
* map. It has a border of the given weight and color and can optionally
* have a semi-transparent background color.
* @param latlng {GLatLng} Point to place bar at.
* @param opts {Object Literal} Passes configuration options - 
*   weight, color, height, width, text, and offset.
*/
function MarkerLight(latlng, opts) {

	this.latlng = latlng;

	if (!opts) opts = {};

	this.opts = opts;

	this.opts.icon = opts.icon || G_DEFAULT_ICON;
	this.opts.icon.iconSize = opts.icon.iconSize || G_DEFAULT_ICON.iconSize;
	this.opts.icon.iconSize.height = opts.icon.iconSize.height || 32;
	this.opts.icon.iconSize.width = opts.icon.iconSize.width || 32;
	this.opts.icon.iconAnchor = opts.icon.iconAnchor || new GPoint(0, 0);

	if (opts.icon.shadow) {
		this.opts.icon.shadow = opts.icon.shadow;
		this.opts.icon.shadowSize.height = opts.icon.shadowSize.height || 32;
		this.opts.icon.shadowSize.width = opts.icon.shadowSize.width || 32;
	}

	this.opts.title = opts.title || "";

	this.clicked_ = 0;

	this.isAdded = false;
}

/* MarkerLight extends GOverlay class from the Google Maps API
*/
MarkerLight.prototype = new GOverlay();

/* Creates the DIV representing this MarkerLight.
* @param map {GMap2} Map that bar overlay is added to.
*/
MarkerLight.prototype.initialize = function(map) {

	// Create the DIV representing our MarkerLight
	var div = document.createElement("div");
	div.style.position = "absolute";
	div.style.paddingLeft = "0px";
	div.style.cursor = "pointer";
	div.style.lineHeight = (this.opts.icon.iconAnchor.y * 2) + "px";
	div.style.textAlign = "center";
	div.style.width = this.opts.icon.iconSize.width + "px";
	div.style.height = this.opts.icon.iconSize.height + "px";
	div.style.color = "#ffffff";
	div.style.zIndex = this.getZIndex(this);

	var img = document.createElement("img");
	img.src = this.opts.icon.image;
	img.style.width = this.opts.icon.iconSize.width + "px";
	img.style.height = this.opts.icon.iconSize.height + "px";
	img.className = "png";
	img.style.position = "absolute";
	img.style.top = 0;
	img.style.left = 0;
	img.style.zIndex = -1;
	div.appendChild(img);

	if (this.opts.title.length > 0) {
		div.appendChild(document.createTextNode(this.opts.title));
	}

	var me = this;

	GEvent.addDomListener(div, "click", function(event) {
		me.clicked_ = 1;
		GEvent.trigger(me, "click");
	});

	this.map = map;
	this.div = div;

	if (this.opts.icon.shadow != null) {

		var shadowDiv = document.createElement("div");
		shadowDiv.style.position = "absolute";
		shadowDiv.style.paddingLeft = "0px";

		var shadowImg = document.createElement("img");
		shadowImg.src = this.opts.icon.shadow;
		shadowImg.style.width = this.opts.icon.shadowSize.width + "px";
		shadowImg.style.height = this.opts.icon.shadowSize.height + "px";
		shadowImg.className = "png";
		shadowDiv.appendChild(shadowImg);

		this.shadowDiv = shadowDiv;
	}
	else {
		this.shadowDiv = null;
	}
};

/* Remove the main DIV from the map pane
*/
MarkerLight.prototype.remove = function() {

	if (!this.isAdded)
		return;

	this.isAdded = false;

	if (this.div && this.div.parentNode)
		this.div.parentNode.removeChild(this.div);

	if (this.shadowDiv && this.shadowDiv.parentNode)
		this.shadowDiv.parentNode.removeChild(this.shadowDiv);
};

/* Copy our data to a new MarkerLight
* @return {MarkerLight} Copy of bar
*/
MarkerLight.prototype.copy = function() {
	return new MarkerLight(this.latlng, this.opts);
};

/* Redraw the MarkerLight based on the current projection and zoom level
* @param force {boolean} Helps decide whether to redraw overlay
*/
MarkerLight.prototype.redraw = function(force) {

	// We only need to redraw if the coordinate system has changed
	if (!force) return;

	if (!this.isAdded) {

		this.map.getPane(G_MAP_MARKER_PANE).appendChild(this.div);

		if (this.shadowDiv != null)
			this.map.getPane(G_MAP_MARKER_SHADOW_PANE).appendChild(this.shadowDiv);

		this.isAdded = true;
	}

	var divPixel = this.map.fromLatLngToDivPixel(this.latlng);

	this.div.style.width = this.opts.icon.iconSize.width + "px";
	this.div.style.left = (divPixel.x - this.opts.icon.iconAnchor.x) + "px";
	this.div.style.height = (this.opts.icon.iconSize.height) + "px";
	this.div.style.top = (divPixel.y - this.opts.icon.iconAnchor.y) + "px";

	if (this.shadowDiv != null) {
		this.shadowDiv.style.width = this.opts.icon.shadowSize.width + "px";
		this.shadowDiv.style.left = (divPixel.x - this.opts.icon.iconAnchor.x) + "px"
		this.shadowDiv.style.height = (this.opts.icon.shadowSize.height) + "px";
		this.shadowDiv.style.top = (divPixel.y - this.opts.icon.iconAnchor.y) + "px";
	}
};

MarkerLight.prototype.getZIndex = function(m) {
	return GOverlay.getZIndex(m.getPoint().lat()) - m.clicked_ * 10000;
}

MarkerLight.prototype.getPoint = function() {
	return this.latlng;
};

MarkerLight.prototype.getLatLng = function() {
	return this.latlng;
};

MarkerLight.prototype.isOnMap = function() {
	return this.isAdded;
};

MarkerLight.prototype.setStyle = function(style) {
	for (s in style) {
		this.div.style[s] = style[s];
	}
};
