(function ($) {
	// jQuery instance plugins
	$.extend({
		/**
		 * Sets url parameters based on a diff algorithm:  new params are added to the existing parameters,
		 * and conflict parameters are replaced with the newest.
		 * @param {Object} params is an object of key/value pairs to convert to url parameters
		 * @return {Object} Returns the jQuery object.
		 */
		setUrlParams: function (params) {
			var urlize = function (arg, x, y, z) {
				x = x || '?';	// parameter string prefix
				y = y || '&';	// parameter pair delimiter
				z = z || '=';	// key / value delimiter
				var c, i, l, s = '', v;
				var urlencode = function (str) {
					return escape(str).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27');
				};
				switch (typeof arg) {
					case 'object':
						if (arg) {
							for (i in arg)
								if (typeof (v = urlize(arg[i])) !== 'function') {
									if (s) { s += y; }
									s += urlencode(urlize(i)) + z + urlencode(v);
								}
							return (s.length >= 1 ? x : '') + s;
						} else { return 'null'; }
					case 'string': return arg;
					case 'number': return String(arg);
					default: return 'null';
				}
			},
			cleaned = function (obj) {
				for (var x in obj) {
					if (obj[x] === '') {
						delete obj[x];
					}
				}
				return obj;
			},
			updateUrl = function (params) {
				var currentUrl = window.location.toString();	// remove existing URL params
				currentUrl = currentUrl.replace(/\?(.+)$/, '');
				window.location = currentUrl + (params || '');
			};
			
			params = $.extend({}, $.getUrlParams(), params || {});
			updateUrl(urlize(cleaned(params)));
			
			return this;
		},
		/**
		 * Gets current url parameters.
		 * @return {Object} Returns an object representing the current url parameters.
		 */
		 getUrlParams: function () {
			var deurlize = function (str) {
				var obj = {};
				var items = str.split('&'),
					currentItem;
				for (var i = 0; i < items.length; i++) {
					currentItem = items[i].split('=');
					if (currentItem instanceof Array) {
						obj[currentItem[0]] = (currentItem[1] || '').replace(/\%2C/g, ',').replace(/\%20/g, ' ') || undefined;
					}
				}
				return obj;
			};
			
			return deurlize(((window.location.toString().split('?')[1] || '').split('#')[0]) || '');
		}
	});
	
	// jQuery prototype plugins
	$.fn.extend({
		/**
		 * Adds default values to input boxes that disappear on-click and on-submit.
		 * @param {String} movieUrl is the url of the movie to write to the page.
		 * @return {Object} Returns the element within which the movie was written.
		 */
		defaultInput: function (settings) {
			return $(this).each(function () {
				// default value
				$('input[default], textarea[default]', this).each(function () {
					if (!$(this).val() && ($(this).val() !== $(this).attr('default'))) {
						$(this).val($(this).attr('default')).addClass('default').focus(function () {
							if ($(this).val() === $(this).attr('default'))
								$(this).removeClass('default').val('');
						}).blur(function () {
							if (!$(this).val() || ($(this).val() === $(this).attr('default')))
								$(this).val($(this).attr('default')).addClass('default');
						});
					}
				});
				$(this).submit(function () {
					$('input[default], textarea[default]', this).each(function () {
						var self = this;
						if ($(this).val() === $(this).attr('default'))
							$(this).val('');
						setTimeout(function () {
							$(self).addClass('default').val($(self).attr('default'));
						}, 500);
					});
				});
			});
		},
		/**
		 * Writes a QuickTime movie to the page in a cross-browser compatible manner.
		 * @param {String} movieUrl is the url of the movie to write to the page.
		 * @return {Object} Returns the element within which the movie was written.
		 */
		attachQuickTimeMovie: function(movieUrl) {
			this.each(function() {
				if(navigator.userAgent.indexOf('MSIE') != -1) {
					this.innerHTML = [
						'<OBJECT CLASSID=\"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B\" WIDTH=\"320\" HEIGHT=\"240\" CODEBASE=\"http://www.apple.com/qtactivex/qtplugin.cab\"><PARAM name=\"src\" VALUE=\"',
						movieUrl,
						'\"><PARAM name=\"autoplay\" VALUE=\"true\"><PARAM name=\"controller\" VALUE=\"false\"></OBJECT>'
					].join('');
				} else {
					this.innerHTML = '';
					var movie = document.createElement('embed');
					movie.type = 'video/quicktime';
					movie.src = movieUrl;
					movie.width = '320';
					movie.height = '240';
					movie.autoplay = 'true';
					movie.controller = 'false';
					this.appendChild(movie);
				}
			});
			return this;
		},
		/**
		 * Enables url-parameter-based SmartBrowsing from a set of select boxes.
		 * @param {Object} fields is an object of SmartBrowse fields as jQuery objects.
		 * @param {Object} resetEl is a jQuery object of the element which resets SmartBrowse.
		 * @return {Object} Returns the selected set as a jQuery object.
		 */
		smartbrowse: function (settings) {
			settings = $.extend({
				fields: {
					type: $('#sbType'),
					year: $('#sbYear'),
					make: $('#sbMake'),
					model: $('#sbModel'),
					price: $('#sbPrice'),
					bodystyle: $('#sbBodystyle'),
					accountId: $('#sbLocation')
				}
			}, settings);
			
			// TODO:  this should be a global utility
			var getCurrentOptions = function () {
					var options = {};
					$.each(settings.fields, function (i) {
						options[i] = ($(this).val() !== 'all') ? $(this).val() : '';
					});
					return options;
				},
				sbChange = function () {
					var options = getCurrentOptions();
					
					/*
					 * special cases
					 */
					// if current page isn't 1, reset it
					if ($.getUrlParams().start) {
						options.start = '';
					}
					// if user selected 'certified' type, use a special param
					if ($(this).is('#sbType') && ($(this).val() === 'certified')) {
						options.type = '';
						options.certified = 'true';
					} else if ($(this).is('#sbType') && ($(this).val() !== 'certified')) {
						options.certified = '';
					}
					if ($(this).is('#sbMake')) {
					    options.model = '';
					}

					
					$.setUrlParams(options);
				};
			
			$.each(settings.fields, function () {
				$(this).change(sbChange);
			});
			
			return $(this);
		},
		/**
		 * Enables url-parameter-based sorting on a set of elements.
		 * @param {Object} settings
		 * @return {Object} Returns the selected set as a jQuery object.
		 */
		sorting: function (settings) {
			settings = $.extend({
				urlParam: 'sortby',
				direction: 'asc',
				classes: {
					selected: 'ddcSelected',
					ascending: 'asc',
					descending: 'desc'
				},
				sortToggles: {
					year: '.sortYear',
					make: '.sortMake',
					model: '.sortModel',
					price: '.sortPrice',
					mileage: '.sortMileage',
					trim: '.sortTrim'
				}
			}, settings);
			
			var self = this,
				all;
			
			// determine if a default sort was set in the markup
			// if not, set it
			var hasDefaultSort = false;
			$.each(settings.sortToggles, function (i, val) {
				all = (all || $(val)).add(val);
				hasDefaultSort = ($(val, self).hasClass(settings.classes.selected)) ? true : hasDefaultSort;
			});
			
			// attach click handlers
			$(all).click(function () {
				var paramObj = {};
				var sort = $(this).attr('title');
				
				if (!$(this).hasClass(settings.classes.selected)) {
					$('.' + settings.classes.selected, self).removeClass(settings.classes.selected);
				} else {
					// if the current item _is_ selected, reverse the sort order
					sort = (settings.direction === 'asc') ? sort.replace(/asc/, 'desc') : sort.replace(/desc/, 'asc');
				}
				$(this).addClass(settings.classes.selected);
				
				paramObj[settings.urlParam] = sort;
				$.setUrlParams(paramObj);
				
				return false;
			});
			
			return $(this);
		},
		/**
		 * Enables paging by manipulating the 'start' url parameter.
		 * @param {Object} settings
		 * @return {Object} Returns the selected set as a jQuery object.
		 */
		paging: function (settings) {
			settings = $.extend({
				next: '.ddcNextPage',
				prev: '.ddcPrevPage'
			}, settings);
			
			var changeStart = function () {
				start = $(this).attr('href').replace(/.*\?start=(.+)\#/, '$1');
				if (start !== '#') {
					$.setUrlParams({start: start ? start : ''});
				}
				return false;
			};
			
			return $(this).each(function () {
				$.each(['next', 'prev'], function () {
					$(settings[this]).click(changeStart);
				});
				
				$('ul[pages]', this).each(function () {
					var pages = +$(this).attr('pages') || 1;
					
					for (var i = 0; i < pages; i++) {
						$('<li><a href="?start=' + (i * 35) + '#">' + (i + 1) + '</a></li>').appendTo(this).find('a').click(changeStart);
					}
				});
			});
		},
		/**
		 * Enables resetting of a list of url parameters
		 * @param {Object} settings settings.fields is an array of strings representing specific url parameters to reset
		 * @return {Object} Returns the selected set as a jQuery object.
		 */
		reset: function (settings) {
			settings = $.extend({
				fields: ['type', 'certified', 'year', 'make', 'model', 'price', 'bodystyle', 'accountId', 'sortby', 'start'],
				resetValues: {	// some parameters shouldn't be reset to null, but a specific value
					type: '0'	// reset type to 0, not null
				}
			}, settings);
			
			var params = {};
			for (var i = 0; i < settings.fields.length; i++) {
				// reset to the specified reset value, otherwise empty string
				params[settings.fields[i]] = (settings.resetValues[settings.fields[i]] || '');
			}
			
			return $(this).each(function () {
				$(this).click(function () {
					$.setUrlParams(params);
					return false;
				});
			});
		}
	});
})(jQuery);