﻿//****************************************************//
//********** CREATE FILTERS OBJECT *******************//
//****************************************************//
var filters = {
	data: {
		activeLocationID: null,
		activeCategoryID: null,
		selectedSubCategories: null,
		maximumSearchRadius: null,
		activeView: null,
		startingListingNumber: null,
		numberOfListings: null
	},
	urlHasHash: false,
	init: function() {

		// Grab query string parameters, put them into data
		var location = document.location.toString();
		if (location.indexOf("#") > 0) {
			this.urlHasHash = true;
		}

		location = location.replace("?", "#");
		var queryStringParameters = jQuery.query(location);
		if (queryStringParameters.category && !jQuery.isArray(queryStringParameters.category)) {
			var item = queryStringParameters.category;
			queryStringParameters.category = [];
			queryStringParameters.category.push(item);
		}

		// If query string parameter exists, set the filters data
		// else set the default value from hidden inputs to the filters data

		// Set default categories
		if (queryStringParameters.category) {
			this.data.selectedSubCategories = queryStringParameters.category;
		} else {
			this.data.selectedSubCategories = [];
		}

		// Set default number of listings
		if (queryStringParameters.count) {
			this.data.numberOfListings = parseInt(queryStringParameters.count);
		} else {
			this.data.numberOfListings = parseInt(jQuery('#uxDefaultNumberOfListings').val());
		}

		// Set default radius
		if (queryStringParameters.radius) {
			this.data.maximumSearchRadius = parseInt(queryStringParameters.radius);
		} else {
			this.data.maximumSearchRadius = parseInt(jQuery('#uxDefaultMaximumSearchRadius').val());
		}

		// Set default start listing number
		if (queryStringParameters.start) {
			this.data.startingListingNumber = parseInt(queryStringParameters.start);
		} else {
			this.data.startingListingNumber = parseInt(jQuery('#uxDefaultStartingListingNumber').val());
		}

		// Set default tab
		if (queryStringParameters.tab) {
			this.data.activeCategoryID = parseInt(queryStringParameters.tab);
		} else {
			this.data.activeCategoryID = parseInt(jQuery('#uxDefaultActiveCategoryID').val());
		}

		// Set default view: map or list
		if (queryStringParameters.view) {
			this.data.activeView = queryStringParameters.view;
		} else {
			this.data.activeView = jQuery('#uxDefaultActiveView').val();
		}

		// City Location ID never changes per city page
		this.data.activeLocationID = parseInt(jQuery("#uxActiveLocationID").val());

		/* Replace query strings with hashes in all links */
		replaceQueryStrings();
	},
	initPageView: function() {
		//if urlHasHash is true refresh page view here with categories
		if (this.urlHasHash) {
			refreshPageView(true);
		}
	},
	dataToString: function() {
		var dataString = "{";
		for (var key in this.data) {
			if (this.data.hasOwnProperty(key)) {
				if (key != "activeView") {
					dataString += key
						+ ": ";
					if (typeof this.data[key] == 'string') {
						dataString += "\""
							+ this.data[key]
							+ "\",";
					} else if (typeof this.data[key] == 'number') {
						dataString += this.data[key]
							+ ",";
					} else {
						dataString += "["
							+ this.data[key]
							+ "],";
					}
				}
			}
		}
		dataString = dataString.slice(0, dataString.length - 1);
		dataString += "}";
		return dataString;
	},
	resetToDefaultValue: function(keys) {
		jQuery(keys).each(function(i) {
			if (keys[i] == 'selectedSubCategories') {
				filters.data[keys[i]] = [];
			} else if (typeof filters.data[keys[i]] == 'number') {
				filters.data[keys[i]] = parseInt(jQuery("#uxDefault" + keys[i].capitalize()).val());
			} else {
				filters.data[keys[i]] = jQuery("#uxDefault" + keys[i].capitalize()).val();
			}
		});
	},
	getPageLinkHash: function(startingListingNumber, numberOfListings) {
		var linkHash = "#";
		linkHash += "tab=" + this.data.activeCategoryID
			+ "&radius=" + this.data.maximumSearchRadius
			+ "&view=" + this.data.activeView
			+ "&start=" + startingListingNumber
			+ "&count=" + numberOfListings;
		for (var i = 0; i < this.data.selectedSubCategories.length; i++) {
			linkHash += "&category=" + this.data.selectedSubCategories[i]
		}
		return linkHash;
	},
	getRadiusLinkHash: function(radius) {
		var linkHash = "#";
		linkHash += "tab=" + this.data.activeCategoryID
				+ "&radius=" + radius
				+ "&view=" + this.data.activeView
				+ "&start=" + this.data.startingListingNumber
				+ "&count=" + this.data.numberOfListings;
		for (var i = 0; i < this.data.selectedSubCategories.length; i++) {
			linkHash += "&category=" + this.data.selectedSubCategories[i]
		}
		return linkHash;
	},
	getViewLinkHash: function(view) {
		var linkHash = "#";
		linkHash += "tab=" + this.data.activeCategoryID
				+ "&radius=" + this.data.maximumSearchRadius
				+ "&view=" + view
				+ "&start=" + this.data.startingListingNumber
				+ "&count=" + this.data.numberOfListings;
		for (var i = 0; i < this.data.selectedSubCategories.length; i++) {
			linkHash += "&category=" + this.data.selectedSubCategories[i]
		}
		return linkHash;
	},
	getRefreshButtonHash: function() {
		var linkHash = "#";
		linkHash += "tab=" + this.data.activeCategoryID
					+ "&radius=" + this.data.maximumSearchRadius
					+ "&view=" + this.data.activeView
					+ "&start=" + this.data.startingListingNumber
					+ "&count=" + this.data.numberOfListings;
		for (var i = 0; i < this.data.selectedSubCategories.length; i++) {
			linkHash += "&category=" + this.data.selectedSubCategories[i]
		}
		return linkHash;
	}
};

function replaceQueryStrings() {
	jQuery(".tabs2 a, .radius-filter-container a, .pagination2 a")
		.each(function() {
			jQuery(this).attr("href", jQuery(this).attr("href").replace("?", "#"));
		});
}

String.prototype.capitalize = function() {
	return this.replace(/\w+/g, function(a) {
		return a.charAt(0).toUpperCase() + a.substr(1);
	});
};


//****************************************************//
//********** INITIALIZE PAGE *************************//
//****************************************************//
jQuery(document).ready(function() {

	// Make GetCities web service call only if search box exists
	if (jQuery("#keyword input").length > 0) {
		initCitySearch();
	} else {

		/* Initialize functions for details page */
		filters.init();

		initExpandableDescription();
		initTabs();
		initFilters();
		initPagination();

		filters.initPageView();
		if (!filters.urlHasHash) {
			initMap();
		}
	}
});


//****************************************************//
//********** INITIALIZE SEARCH PAGE ******************//
//****************************************************//
function initCitySearch() {
	jQuery.ajax({
		type: "POST",
		url: "/Services/CityService.asmx/GetCities",
		data: "{}",
		contentType: "application/json; charset=utf-8",
		dataType: "json",
		success: initAutoSuggest
	});

	jQuery("#keyword input").data("originalValue", jQuery("#keyword input").val());
	jQuery("#keyword input").focus(function() {
		var input = jQuery(this);
		if (input.val() == input.data("originalValue")) {
			input.val("");
		}
		input.removeClass("inactive");
	});
	jQuery("#keyword input").blur(function() {
		var input = jQuery(this);
		if (input.val() == "") {
			input.val(input.data("originalValue"));
			input.addClass("inactive");
		}
	});
}

function initAutoSuggest(json) {
	json = eval("(" + json.d + ")");
	jQuery("#keyword input").autocompleteArray(
		json.cities,
		json.settings
	);
}

function findValue(li) {
	if (li == null) return;
	// if coming from an AJAX call, let's use the CityId as the value
	if (!!li.extra) var sValue = li.extra[0];
	// otherwise, let's just display the value in the text box
	else var sValue = li.selectValue;
}

function selectItem(li) {
	findValue(li);
}

//****************************************************//
//********** INITIALIZE DETAILES PAGE ****************//
//****************************************************//

//***** Initialize Expand Collapse City Description *****//
function initExpandableDescription() {
	var description = jQuery("#cityDescription");
	var expandText = "More";
	var collapseText = "Collapse";
	var introHeight = 19.5;

	// If the city description is long enough, add the expand link and hide part of the description.
	if (description.height() / 16.5 > 15) {
		description.css({ "height": introHeight + "em", "overflow": "hidden" });

		// Create expand/collapse link
		var expandLink = jQuery("<a class='expand-trigger' href=''></a>")
        .text(expandText)
        .click(function(event) {
        	if (jQuery(this).text() == collapseText) {
        		jQuery(this).text(expandText);
        		jQuery("#cityDescription").css({ "height": introHeight + "em", "overflow": "hidden" });
        	} else {
        		jQuery(this).text(collapseText);
        		jQuery("#cityDescription").css({ "height": "auto", "overflow": "visible" });
        	}
        	jQuery(this).toggleClass("open");
        	event.preventDefault();
        });
		description.after(expandLink);
	}
}

//***** Initialize Tabs *****//
function initTabs() {
	// Appends click behavior to tab links
	jQuery(".tabs2 a").click(function(event) {
	
		// Update Filter Data
		filters.data.activeCategoryID = parseInt(jQuery(this).attr("href").substring(5));
		filters.resetToDefaultValue([
			"selectedSubCategories",
			"maximumSearchRadius",
			"activeView",
			"startingListingNumber",
			"numberOfListings"]);

		refreshPageView(true);
	});
}

//***** Initialize Category and Radius Filters, including re-creating markup *****//
function initFilters() {

	// String used for filters
	var commonStrings = {
		"container_class": "trigger-container",
		"trigger_class": "dropdown-trigger",
		"trigger_active_class": "open",
		"dropdown_class": "filter-dropdown",
		"dropdown_show_class": "show"
	};

	var categoryStrings = {
		"category_class": "category-filter-container",
		"category_trigger_text": "Select Specific Categories",
		"select_text": "Select All",
		"select_id": "select-all",
		"clear_text": "Clear All",
		"clear_id": "clear-all"
	};

	var radiusStrings = {
		"radius_class": "radius-filter-container",
		"span1": "Show listings within",
		"span2": "of the city\'s center."
	};

	// Turn downlevel Markup into Uplevel
	buildDropdownMarkup(commonStrings, categoryStrings, radiusStrings);

	// Attach event handlers to uplevel filter markup
	attachFilterEventHandlers(commonStrings, categoryStrings, radiusStrings);

	// Resize the width of the category dropdown based on the width of the checkbox labels
	resizeCategoryDropdown();
}

// Turn downlevel filter markup into uplevel markup
function buildDropdownMarkup(commonStrings, categoryStrings, radiusStrings) {
	var anchor = jQuery("#cityFilters");
	var introSpan = jQuery("#cityFilters .intro2");
	var categoryHTML = jQuery("#cityFilters fieldset:eq(0)");
	var refreshButton = jQuery("#cityFilters .button-left");
	var radiusHTML = jQuery("#cityFilters fieldset:eq(1)");
	var amenitiesLink = jQuery("#cityFilters #amenitiesLink");

	// Wipe markup
	jQuery("#cityFilters").html("");

	// Begin rebuild markup
	introSpan.appendTo("#cityFilters");

	// Build category dropdown markup
	var catDiv = jQuery('<div></div>')
        .appendTo(anchor)
        .addClass(commonStrings.container_class + " " + categoryStrings.category_class + " cf");
	jQuery('<a href=""></a>')
        .text(categoryStrings.category_trigger_text)
        .appendTo(catDiv)
        .addClass(commonStrings.trigger_class);
	var catDropdown = jQuery('<div></div>')
        .appendTo(catDiv)
        .addClass(commonStrings.dropdown_class);

	// Build select all/ clear all list
	var catSelectClearList = jQuery('<ul class="linklist4 cf"></ul>')
		.appendTo(catDropdown);

	// Build select all Link
	var selectListItem = jQuery('<li class="first"></li>')
		.appendTo(catSelectClearList);
	jQuery('<a href="">' + categoryStrings.select_text + '</a>')
		.appendTo(selectListItem)
		.attr("id", categoryStrings.select_id);

	// Build clear all link
	var clearListitem = jQuery('<li></li>')
		.appendTo(catSelectClearList);
	jQuery('<a href="">' + categoryStrings.clear_text + '</a>')
		.appendTo(clearListitem)
		.attr("id", categoryStrings.clear_id);

	// Append checkbox list from downlevel
	categoryHTML.appendTo(catDropdown);

	// Check categories based on query string/hash
	checkCheckBoxes();

	// Append "refresh listings" button
	refreshButton.appendTo(categoryHTML);

	// Append amenities link
	amenitiesLink.appendTo(catDropdown);

	// Build radius dropdown markup
	var radDiv = jQuery('<div></div>')
        .appendTo(anchor)
        .addClass(radiusStrings.radius_class);
	jQuery('<span>' + radiusStrings.span1 + '</span>')
        .appendTo(radDiv);
	var triggerDiv = jQuery('<div></div>')
        .appendTo(radDiv)
        .addClass(commonStrings.container_class);
	jQuery('<a href=""></a>')
        .appendTo(triggerDiv)
        .text((filters.data.maximumSearchRadius == 1) ? filters.data.maximumSearchRadius + " mile" : filters.data.maximumSearchRadius + " miles")
        .data('radiusValue', filters.data.maximumSearchRadius)
        .addClass(commonStrings.trigger_class);
	var radDropdown = jQuery('<div></div>')
        .appendTo(triggerDiv)
        .addClass(commonStrings.dropdown_class);

	var radiusList = jQuery('<ul></ul>');
	radiusHTML.find('option').each(function(i) {
		// For each radius option, create a listitem and link 
		var listitem = jQuery('<li></li>');
		var radiusLink = jQuery('<a href="' + filters.getRadiusLinkHash(parseInt(jQuery(this).text())) + '"></a>')
            .appendTo(listitem)
            .text(jQuery(this).text())
		if (parseInt(radiusLink.text()) == filters.data.maximumSearchRadius) {
			radiusLink.addClass("disabled");
		}
		listitem.appendTo(radiusList);
	});
	radiusList.appendTo(radDropdown);

	jQuery('<span>' + radiusStrings.span2 + '</span>')
        .appendTo(radDiv);
}

function checkCheckBoxes() {
	for (var i = 0; i < filters.data.selectedSubCategories.length; i++) {
		jQuery("#category_" + i).attr("checked", true);
	}
}

//***** Attach event handlers to uplevel filter markup *****//
function attachFilterEventHandlers(commonStrings, categoryStrings, radiusStrings) {

	// Append click behavior for dropdown trigger links
	jQuery("#cityFilters a.dropdown-trigger").click(function(event) {
		toggleDropdown(this);
		event.preventDefault();
	});

	// Append click behavior to select all link
	jQuery("#" + categoryStrings.select_id)
		.click(function(event) {
			jQuery("." + commonStrings.dropdown_class + " input[type='checkbox']")
				.attr("checked", true);
			event.preventDefault();
		});

	// Append click behavior to clear all link
	jQuery('#' + categoryStrings.clear_id)
		.click(function(event) {
			jQuery("." + commonStrings.dropdown_class + " input[type='checkbox']")
				.attr("checked", false);
			event.preventDefault();
		});

	// Append click behavior to refresh listings button
	jQuery('.' + categoryStrings.category_class + ' button')
		.click(function(event) {

			// Update Filter Data
			filters.data.selectedSubCategories = [];
			jQuery("." + commonStrings.dropdown_class + " input[type='checkbox']:checked").each(function() {
				filters.data.selectedSubCategories.push(jQuery(this).val());
			});
			filters.resetToDefaultValue([
				"startingListingNumber",
				"numberOfListings"]);
			var hash = filters.getRefreshButtonHash();
			document.location = hash;

			refreshPageView(false);
			event.preventDefault();
		});

	// Append click behavior to radius dropdown links
	jQuery("." + radiusStrings.radius_class + " ." + commonStrings.dropdown_class + " a")
		.click(function(event) {
			updateRadiusLinkClasses(commonStrings, radiusStrings, this);

			// Update Filter Data
			filters.data.selectedSubCategories = [];
			filters.data.maximumSearchRadius = jQuery(".radius-filter-container .dropdown-trigger").data('radiusValue'); ;
			jQuery("." + commonStrings.dropdown_class + " input[type='checkbox']:checked").each(function() {
				filters.data.selectedSubCategories.push(jQuery(this).val());
			});
			filters.resetToDefaultValue([
				"startingListingNumber",
				"numberOfListings"]);

			refreshPageView(true);
		});
}

//***** Toggle classes on dropdown link and dropdown div *****//
function toggleDropdown(dropdownLink) {
	jQuery(dropdownLink)
        .toggleClass("open")
        .parent("div")
        .find(".filter-dropdown")
        .toggleClass("show");
}

//***** Update radius link classes *****//
function updateRadiusLinkClasses(commonStrings, radiusStrings, newActiveLink) {
	jQuery("." + radiusStrings.radius_class + " ." + commonStrings.trigger_class)
        .text(jQuery(newActiveLink).text())
        .data('radiusValue', parseInt(jQuery(newActiveLink).text()));
	jQuery("." + radiusStrings.radius_class + " .disabled").removeClass("disabled");
	jQuery(newActiveLink).addClass("disabled");
}

//***** Set width of category dropdown based on category label lengths  *****//
function resizeCategoryDropdown() {

	//if (jQuery.browser.msie && jQuery.browser.version == 6.0) checkboxWidth = 32;
	var currentWidest = 0;
	jQuery(".category-filter-container .filter-dropdown").css("display", "block");
	
	// Specify width of dropdown based on widest label (category) element
	checkboxWidth = jQuery("#cityFilters .filter-dropdown input[type='checkbox']").width();
	
	jQuery(".category-filter-container .filter-dropdown label").each(function(i) {
		if (jQuery(this).width() > currentWidest) { currentWidest = jQuery(this).width(); }
	});
	jQuery(".category-filter-container .filter-dropdown").css("display", "none");
	jQuery(".category-filter-container .filter-dropdown").css("width", [currentWidest + checkboxWidth + 15] + "px")
}

//***** Initialize Pagination *****//
function initPagination() {
	buildViewMarkup();
	attachPaginationEventHandlers();
}

function buildViewMarkup() {

	var listViewContainer = jQuery("<ul class='view-toggle cf'></ul>");
	var showLinkItem = jQuery("<li class='first'></li>")
		.appendTo(listViewContainer);
	var showLink = jQuery("<a>Show Map</a>")
		.appendTo(showLinkItem)
		.attr("href", filters.getViewLinkHash("map"));

	var hideLinkItem = jQuery("<li class='last'></li>")
		.hide()
		.appendTo(listViewContainer);
	var hideLink = jQuery("<a>Hide Map</a>")
		.appendTo(hideLinkItem)
		.attr("href", filters.getViewLinkHash("list"));

	jQuery(".pagination2").before(listViewContainer);
}

function attachPaginationEventHandlers() {

	// Append click behavior to show all link
	jQuery(".show-all")
		.live('click', function() {
			filters.data.selectedSubCategories = [];
			jQuery(".filter-dropdown input[type='checkbox']:checked").each(function() {
				filters.data.selectedSubCategories.push(jQuery(this).val());
			});
			filters.data.numberOfListings = (filters.data.numberOfListings == 0) ? parseInt(jQuery('#uxDefaultNumberOfListings').val()) : 0;
			filters.resetToDefaultValue([
				"startingListingNumber"
				]);

			refreshPageView(false);
		});

	// Append click behavior to disabled pagination links
	jQuery(".pagination2 a.disabled")
		.live("click", function(event) {
			event.preventDefault();
		});

	// Append click behavior to pagination links which are not disabled
	jQuery(".pagination2 a:not(.disabled)")
			.live("click", function(event) {

				// TODO: move this
				filters.data.selectedSubCategories = [];
				jQuery(".filter-dropdown input[type='checkbox']:checked").each(function() {
					filters.data.selectedSubCategories.push(jQuery(this).val());
				});
				var paginationParameters = jQuery.query(jQuery(this).attr("href"));
				filters.data.numberOfListings = parseInt(paginationParameters.count);
				filters.data.startingListingNumber = parseInt(paginationParameters.start);

				refreshPageView(false);
			});

	// Append click behavior for pagination links and list/map view links
	jQuery(".view-toggle li a").click(function(event) {
		toggleView(this, true);
		filters.data.selectedSubCategories = [];
		jQuery(".filter-dropdown input[type='checkbox']:checked").each(function() {
			filters.data.selectedSubCategories.push(jQuery(this).val());
		});
		filters.data.activeView = (filters.data.activeView == "map") ? "list" : "map";
		jQuery(".view-toggle li").toggle();
	});

	// Append click behavior for  listings view on map links
	jQuery("#listingsdiv ul.linklist4 a:contains('View on Map')").live("click", function(event) {
		toggleView(this, false);
		filters.data.selectedSubCategories = [];
		jQuery(".filter-dropdown input[type='checkbox']:checked").each(function() {
			filters.data.selectedSubCategories.push(jQuery(this).val());
		});
		filters.data.activeView = "map";
	});
}

function toggleView(link, showhideLink) {

	// TODO: Potentially send map a listing to pop open 

	// If link is disabled, do nothing
	if (jQuery(link).hasClass("disabled")) {
		return false;
	}
	// If link is not disabled, then toggle the map
	if (jQuery("#myMap").is(":hidden")) {
		jQuery("#myMap").slideFadeToggle("slow");
		jQuery(".view-toggle li a").text("Hide Map");
		jQuery.scrollTo(".tab-container", 1000);
	} else {
		if (showhideLink) {
			jQuery("#myMap").slideFadeToggle("slow");
			jQuery(".view-toggle li a").text("Show Map");
		}
		jQuery.scrollTo(".tab-container", 1000);
	}
}

//***** Initialize the map *****//
function initMap() {
	getListings(defaultGetListingsCallback);
}



//****************************************************//
//********** REFRESH PAGE VIEW  **********************//
//****************************************************//
function refreshPageView(updateCategory) {

	// Collapse filter dropdowns
	jQuery(".filter-dropdown").removeClass("show");
	jQuery(".dropdown-trigger").removeClass("open");

	// Show loading animation
	jQuery("#loading-screen").css("display", "block").fadeTo("fast", 0.90);

	// Send the request data to the listings webservice
	getListings(defaultGetListingsCallback);

	// Update categories
	if (updateCategory) {
		// TODO: Show loading animation

		// Send the request data to the categories webservice
		jQuery.ajax({
			type: "POST",
			url: "/Services/CityService.asmx/GetCategories",
			data: filters.dataToString(),
			contentType: "application/json; charset=utf-8",
			dataType: "json",
			success: function(json) {
				// Parse JSON result
				json = eval("(" + json.d + ")");

				// Refresh the categories in the dropdown
				updateCategories(json);
				// TODO: Hide loading animation
			}
		});
	}
}

function getListings(listingsCallback) {
	jQuery.ajax({
		type: "POST",
		url: "/Services/CityService.asmx/GetListings",
		data: filters.dataToString(),
		contentType: "application/json; charset=utf-8",
		dataType: "json",
		success: listingsCallback
	});
}

function defaultGetListingsCallback(json) {

	// Parse JSON result
	json = eval("(" + json.d + ")");

	updateTabClasses();

	if (filters.data.activeView == "map" && jQuery("#myMap:hidden").length > 0) {
		jQuery("#myMap").slideFadeToggle("slow");
		jQuery(".view-toggle li a").text("Hide Map");
	}
	
	// Update the listings in the list view
	var listings = json.listings;
	updateListings(listings);
	updateMapPins(listings);

	// Update pagination
	var totalNumberOfListings = json.totalNumberOfListings;

	updatePagination(totalNumberOfListings);

	updateLinkHashes();

	// Hide loading animation
	jQuery("#loading-screen").fadeTo("fast", 0, function() { jQuery("#loading-screen").css("display", "none") });
}

//***** Update tab classes *****//
function updateTabClasses() {
	jQuery(".tabs2 .active").removeClass("active");
	jQuery(".tabs2 a[href='#tab=" + filters.data.activeCategoryID + "']").parent("li").addClass("active");
}

function updateListings(listings) {

	// Clear out the listings container
	var listingsContainer = jQuery("#listingsdiv").html("");
	var list = jQuery("<ul class='list1'></ul")
    	.appendTo(listingsContainer);

	// For each listing json object, build a listitem and append it to the list
	if (listings.length > 0) {
		for (var i = 0; i < listings.length; i++) {
			var listing = buildListingMarkup(listings[i]);
			listing.appendTo(list);
		}
	} else {
		jQuery("<li><p>Unfortunately, there are no listings that match this criteria. Be sure to adjust the \"Mile Radius from City Center\" option above to see more listings.</p></li>")
			.appendTo(list);
	}
}

function updateMapPins(listings) {
	// ANDY YOUR CODE GOES HERE!
}

function buildListingMarkup(listing) {

	var commonStrings = {
		"sponsored_text": "[SPONSORED LISTING]",
		"miles_text": " miles from city center",
		"view_text": "View on Map"
	};

	// Build the listitem for the listing
	var listitem = jQuery("<li></li>").addClass("cf");
	jQuery("<div class='listingnumber'><span><em>" + listing.listingNumber + "</em></span></div>")
		.appendTo(listitem);
	var listingDiv = jQuery("<div class='listingnumberitem'></div>")
		.appendTo(listitem);

	// Check if the listing has a photo
	if (listing.photoUrl) {
		jQuery("<a href='" + listing.localUrl + "'><img alt='" + listing.name + "' src='" + listing.photoUrl + "' /></a>")
		.appendTo(listingDiv);
	}
	jQuery("<h3 class='heading2alt'><a href='" + listing.localUrl + "'>" + listing.name + "</a></h3>")
		.appendTo(listingDiv);

	// Check if the listing is sponsored
	if (listing.isSponsoredLocation) {
		jQuery("<h4>" + commonStrings.sponsored_text + "</h4>")
			.appendTo(listingDiv);
	}
	jQuery("<p>" + listing.description + "<p>")
		.appendTo(listingDiv);
	var horizontalList = jQuery("<ul class='linklist4 cf'><li class='first'><p>" + listing.distanceFromActiveLocation.toFixed(2) + " " + commonStrings.miles_text + "</p></li><li><a href='" + filters.getViewLinkHash("map") + "'>" + commonStrings.view_text + "</a></li></ul>")
		.appendTo(listingDiv);
	return listitem;
}

function updateCategories(categories) {

	// Clear out the checkbox list, and replace them with the recieved categories
	var categoriesList = jQuery(".category-filter-container .filter-dropdown fieldset ul").html("");
	for (var j = 0; j < categories.length; j++) {

		var category = categories[j];
		jQuery("<li class='cf'><input type='checkbox' value='" + category.id + "' id='category_" + category.id + "' /><label for='category_" + category.id + "' >" + category.name + " (" + category.count + ")</label></li>")
			.appendTo(categoriesList);
	}

	// Show/hide amenities link
	if (filters.data.activeCategoryID == 16) {
		jQuery("#amenitiesLink").show();
	} else {
		jQuery("#amenitiesLink").hide();
	}

	// Resize the width of the category dropdown based on the width of the checkbox labels
	resizeCategoryDropdown();
	checkCheckBoxes();
}

function updateLinkHashes() {
	// Update Hashes on Radius Links

	jQuery(".radius-filter-container .filter-dropdown li a").each(function() {
		jQuery(this).attr("href", filters.getRadiusLinkHash(parseInt(jQuery(this).text())));
	});
}

function updatePagination(totalNumberOfListings) {

	var numberOfListings = (filters.data.numberOfListings == 0) ? totalNumberOfListings : filters.data.numberOfListings;
	
	var pageParameters = {
		'firstPageNumber': 1,
		'lastPageNumber': Math.ceil(totalNumberOfListings / numberOfListings),
		'currentPageNumber': Math.floor(filters.data.startingListingNumber / numberOfListings) + 1,
		'totalNumberOfPages': Math.ceil(totalNumberOfListings / numberOfListings),
		'startListingNumber': filters.data.startingListingNumber,
		'numberOfListings': filters.data.numberOfListings,
		'endListingNumber': filters.data.startingListingNumber + numberOfListings,
		'totalNumberOfListings': totalNumberOfListings
	};

	pageParameters = calculatePageLinksToDisplay(pageParameters);
	buildPaginationMarkup(pageParameters);
}

function calculatePageLinksToDisplay(pageParameters) {

	// We start by assuming all pages will be displayed.
	var NumberOfContextualLinks = 4;

	// We then calculate the total number of page links that should be displayed.
	// This is equal to the number of contextual links on the left, plus the
	// number of contextual links on the right, plus one for the current page.
	var TotalNumberOfPageLinks = (2 * NumberOfContextualLinks) + 1;


	// If the number of page links we need to display is less than the total
	// number of page links, we need to narrow our range.
	if (pageParameters.totalNumberOfPages < TotalNumberOfPageLinks) {
		if (pageParameters.currentPageNumber - NumberOfContextualLinks < 1) {
			// If the current page number is too close to page one to show the
			// desired number of contextual links, we adjust the last page link
			// as needed.
			pageParameters.lastPageNumber = pageParameters.totalNumberOfPages;
		}
		else if (pageParameters.currentPageNumber + NumberOfContextualLinks > pageParameters.totalNumberOfPages) {
			// If the current page number is too close to the last page to show
			// the desired number of contextual links, we adjust the first page
			// link as needed.
		}
		else {
			// If we have adequate room for the contextual links on either side
			// of the current page, we adjust both the first and last page links.
			pageParameters.firstPageNumber = pageParameters.currentPageNumber - NumberOfContextualLinks;
			pageParameters.lastPageNumber = pageParameters.currentPageNumber + NumberOfContextualLinks;
		}
		
	// If the number of page links we need to display is more than the total
	// number of page links
	} else {
		if (pageParameters.currentPageNumber <= NumberOfContextualLinks+1) {
			// If the current page number is too close to page one, we just show
			// the number of contextual links
			pageParameters.lastPageNumber = TotalNumberOfPageLinks;
		} else if (pageParameters.currentPageNumber > pageParameters.totalNumberOfPages - NumberOfContextualLinks) {
			// If the current page number is too close to the last page show
			// the desired number of contextual links, we adjust the first page
			// link as needed.
			pageParameters.firstPageNumber = pageParameters.totalNumberOfPages - TotalNumberOfPageLinks;
			pageParameters.lastPageNumber = pageParameters.totalNumberOfPages;
		} else {
			// If we have adequate room for the contextual links on either side
			// of the current page, we adjust both the first and last page links.
			pageParameters.firstPageNumber = pageParameters.currentPageNumber - NumberOfContextualLinks;
			pageParameters.lastPageNumber = pageParameters.currentPageNumber + NumberOfContextualLinks;
		}
	}

	// Calculate the "Now showing x-x0 of xx" values
	pageParameters.endListingNumber = pageParameters.startListingNumber + pageParameters.numberOfListings - 1;

	// If endListingNumber is greater than the total number of listings,
	// this occurs when there are fewer than 10 results in all,
	// then set the endListingNumber to the totalListings value.
	if (pageParameters.endListingNumber > pageParameters.totalNumberOfListings) {
		pageParameters.endListingNumber = pageParameters.totalNumberOfListings;
	}
	return (pageParameters);
}

function buildPaginationMarkup(pageParameters) {

	// If count is 0, create a "show fewer" link
	// else rebuild markup for the pagination links
	if (filters.data.numberOfListings == 0) {

		// Update the "Now showing x-x0 of xx"
		jQuery(".pagination-container p").html("<em>Now showing <strong>" + pageParameters.startListingNumber + "-" + pageParameters.totalNumberOfListings + "</strong> of <strong>" + pageParameters.totalNumberOfListings + "</strong></em>");

		// Rebuild markup for pagination
		jQuery(".pagination2").each(function() {
			var paginationList = jQuery(this);
			paginationList.html("");

			var showallListItem = jQuery("<li></li>");

			var showallLink = jQuery("<a class='show-all' rel='1' href='" + filters.getPageLinkHash(filters.data.startingListingNumber, parseInt(jQuery('#uxDefaultNumberOfListings').val())) + "'>Show Fewer</a>")
			.appendTo(showallListItem);

			showallListItem.appendTo(paginationList);
		});
	} else {

		// Update the "Now showing x-x0 of xx"
		jQuery(".pagination-container p").html("<em>Now showing <strong>" + pageParameters.startListingNumber + "-" + pageParameters.endListingNumber + "</strong> of <strong>" + pageParameters.totalNumberOfListings + "</strong></em>");

		// Rebuild markup for pagination
		jQuery(".pagination2").each(function() {
			var paginationList = jQuery(this);
			paginationList.html("");

			var previousListItem = jQuery("<li class='first'></li>");
			var previousLink = jQuery("<a href='" + filters.getPageLinkHash([pageParameters.startListingNumber - pageParameters.numberOfListings], pageParameters.numberOfListings) + "'>&#171; Previous</a>")
				.appendTo(previousListItem);
			if (pageParameters.currentPageNumber == 1) {
				previousLink.addClass("disabled");
			}
			previousListItem.appendTo(paginationList);

			for (var i = pageParameters.firstPageNumber; i <= pageParameters.lastPageNumber; i++) {
				var pageListItem = jQuery("<li><a href='" + filters.getPageLinkHash((i - 1) * pageParameters.numberOfListings + 1, pageParameters.numberOfListings) + "'>" + i + "</a></li>");
				if (i == pageParameters.currentPageNumber) pageListItem.find("a").addClass("disabled");
				pageListItem.appendTo(paginationList);
			}

			var showallListItem = jQuery("<li></li>");
			var showallLink = jQuery("<a class='show-all' href='" + filters.getPageLinkHash(1, 0) + "'>Show All</a>")
				.appendTo(showallListItem);
			showallListItem.appendTo(paginationList);

			var nextListItem = jQuery("<li class='last'></li>");
			var nextLink = jQuery("<a href='" + filters.getPageLinkHash([pageParameters.startListingNumber + pageParameters.numberOfListings], pageParameters.numberOfListings) + "'>Next &#187;</a>")
				.appendTo(nextListItem);
			if (pageParameters.currentPageNumber == pageParameters.lastPageNumber) {
				nextLink.addClass("disabled");
			}
			nextListItem.appendTo(paginationList);
		});
	}
}

jQuery.query = function(q) {
	var r = {};
	q = q.replace(/^.*#/, ''); // remove the leading #
	q = q.replace(/\&$/, ''); // remove the trailing &
	jQuery.each(q.split('&'), function() {
		var key = this.split('=')[0];
		var val = this.split('=')[1];
		// ingnore empty values
		if (val) {
			if (jQuery.isArray(r[key])) {
				r[key].push(val);
			}
			else if (r[key]) {
				var item = r[key];
				r[key] = [];
				r[key].push(item);
				r[key].push(val);
			}
			else {
				r[key] = val;
			}
		}
	});
	return r;
};

jQuery.fn.slideFadeToggle = function(speed, easing, callback) {
	return this.animate({ opacity: 'toggle', height: 'toggle' }, speed, easing, callback);
};
