/*!
* jQuery BBQ: Back Button & Query Library - v1.1 - 1/9/2010
* http://benalman.com/projects/jquery-bbq-plugin/
* 
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/

// Script: jQuery BBQ: Back Button & Query Library
//
// *Version: 1.1, Last updated: 1/9/2010*
// 
// Project Home - http://benalman.com/projects/jquery-bbq-plugin/
// GitHub       - http://github.com/cowboy/jquery-bbq/
// Source       - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
// (Minified)   - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (3.7kb)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
// 
// Basic AJAX     - http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
// Advanced AJAX  - http://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
// jQuery UI Tabs - http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
// Deparam        - http://benalman.com/code/projects/jquery-bbq/examples/deparam/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.3.2, 1.4a2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.1.
// Unit Tests      - http://benalman.com/code/projects/jquery-bbq/unit/
// 
// About: Release History
// 
// 1.1   - (1/9/2010) Broke out the jQuery BBQ event.special window.onhashchange
//         functionality into a separate plugin for users who want just the
//         basic event & back button support, without all the extra awesomeness
//         that BBQ provides. This plugin will be included as part of jQuery BBQ,
//         but also be available separately. See <jQuery hashchange event>
//         plugin for more information. Also added the $.bbq.removeState method
//         and added additional $.deparam examples.
// 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
//         location.hash would report incorrectly if the hash contained the ?
//         character. Also $.param.querystring and $.param.fragment will no
//         longer parse params out of a URL that doesn't contain ? or #,
//         respectively.
// 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
//         a "This page contains both secure and nonsecure items." warning when
//         used on an https:// page.
// 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
//         Compatibility View" modes erroneously report that the browser
//         supports the native window.onhashchange event, a slightly more
//         robust test needed to be added.
// 1.0   - (10/2/2009) Initial release

(function($, window) {
	'$:nomunge'; // Used by YUI compressor.

	// Some convenient shortcuts.
	var undefined,
    loc = window.location,
    aps = Array.prototype.slice,
    decode = decodeURIComponent,

	// Method / object references.
    jq_param = $.param,
    jq_param_fragment,
    jq_deparam,
    jq_deparam_fragment,
    jq_bbq = $.bbq = $.bbq || {},
    jq_bbq_pushState,
    jq_bbq_getState,
    jq_elemUrlAttr,
    jq_event_special = $.event.special,

	// Reused strings.
    str_hashchange = 'hashchange',
    str_querystring = 'querystring',
    str_fragment = 'fragment',
    str_elemUrlAttr = 'elemUrlAttr',
    str_href = 'href',
    str_src = 'src',

	// Reused RegExp.
    re_trim_querystring = /^.*\?|#.*$/g,
    re_trim_fragment = /^.*\#/,

	// Used by jQuery.elemUrlAttr.
    elemUrlAttr_cache = {};

	// A few commonly used bits, broken out to help reduce minified file size.

	function is_string(arg) {
		return typeof arg === 'string';
	};

	// Why write the same function twice? Let's curry! Mmmm, curry..

	function curry(func) {
		var args = aps.call(arguments, 1);

		return function() {
			return func.apply(this, args.concat(aps.call(arguments)));
		};
	};

	// Get location.hash (or what you'd expect location.hash to be) sans any
	// leading #. Thanks for making this necessary, Firefox!
	function get_fragment(url) {
		return url.replace(/^[^#]*#?(.*)$/, '$1');
	};

	// Get location.search (or what you'd expect location.search to be) sans any
	// leading #. Thanks for making this necessary, IE6!
	function get_querystring(url) {
		return url.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/, '$1');
	};

	// Section: Param (to string)
	// 
	// Method: jQuery.param.querystring
	// 
	// Retrieve the query string from a URL or if no arguments are passed, the
	// current window.location.
	// 
	// Usage:
	// 
	// > jQuery.param.querystring( [ url ] );
	// 
	// Arguments:
	// 
	//  url - (String) A URL containing query string params to be parsed. If url
	//    is not passed, the current window.location is used.
	// 
	// Returns:
	// 
	//  (String) The parsed query string, with any leading "?" removed.
	//

	// Method: jQuery.param.querystring (build url)
	// 
	// Merge a URL, with or without pre-existing query string params, plus any
	// object, params string or URL containing query string params into a new URL.
	// 
	// Usage:
	// 
	// > jQuery.param.querystring( url, params [, merge_mode ] );
	// 
	// Arguments:
	// 
	//  url - (String) A valid URL for params to be merged into. This URL may
	//    contain a query string and/or fragment (hash).
	//  params - (String) A params string or URL containing query string params to
	//    be merged into url.
	//  params - (Object) A params object to be merged into url.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	// 
	//    * 0: params in the params argument will override any query string
	//         params in url.
	//    * 1: any query string params in url will override params in the params
	//         argument.
	//    * 2: params argument will completely replace any query string in url.
	// 
	// Returns:
	// 
	//  (String) Either a params string with urlencoded data or a URL with a
	//    urlencoded query string in the format 'a=b&c=d&e=f'.

	// Method: jQuery.param.fragment
	// 
	// Retrieve the fragment (hash) from a URL or if no arguments are passed, the
	// current window.location.
	// 
	// Usage:
	// 
	// > jQuery.param.fragment( [ url ] );
	// 
	// Arguments:
	// 
	//  url - (String) A URL containing fragment (hash) params to be parsed. If
	//    url is not passed, the current window.location is used.
	// 
	// Returns:
	// 
	//  (String) The parsed fragment (hash) string, with any leading "#" removed.

	// Method: jQuery.param.fragment (build url)
	// 
	// Merge a URL, with or without pre-existing fragment (hash) params, plus any
	// object, params string or URL containing fragment (hash) params into a new
	// URL.
	// 
	// Usage:
	// 
	// > jQuery.param.fragment( url, params [, merge_mode ] );
	// 
	// Arguments:
	// 
	//  url - (String) A valid URL for params to be merged into. This URL may
	//    contain a query string and/or fragment (hash).
	//  params - (String) A params string or URL containing fragment (hash) params
	//    to be merged into url.
	//  params - (Object) A params object to be merged into url.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	// 
	//    * 0: params in the params argument will override any fragment (hash)
	//         params in url.
	//    * 1: any fragment (hash) params in url will override params in the
	//         params argument.
	//    * 2: params argument will completely replace any query string in url.
	// 
	// Returns:
	// 
	//  (String) Either a params string with urlencoded data or a URL with a
	//    urlencoded fragment (hash) in the format 'a=b&c=d&e=f'.

	function jq_param_sub(is_fragment, get_func, url, params, merge_mode) {
		var result,
      qs,
      matches,
      url_params,
      hash;

		if (params !== undefined) {
			// Build URL by merging params into url string.

			// matches[1] = url part that precedes params, not including trailing ?/#
			// matches[2] = params, not including leading ?/#
			// matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
			matches = url.match(is_fragment ? /^([^#]*)\#?(.*)$/ : /^([^#?]*)\??([^#]*)(#?.*)/);

			// Get the hash if in 'querystring' mode, and it exists.
			hash = matches[3] || '';

			if (merge_mode === 2 && is_string(params)) {
				// If merge_mode is 2 and params is a string, merge the fragment / query
				// string into the URL wholesale, without converting it into an object.
				qs = params.replace(is_fragment ? re_trim_fragment : re_trim_querystring, '');

			} else {
				// Convert relevant params in url to object.
				url_params = jq_deparam(matches[2]);

				params = is_string(params)

				// Convert passed params string into object.
          ? jq_deparam[is_fragment ? str_fragment : str_querystring](params)

				// Passed params object.
          : params;

				qs = merge_mode === 2 ? params                              // passed params replace url params
          : merge_mode === 1 ? $.extend({}, params, url_params)  // url params override passed params
          : $.extend({}, url_params, params);                     // passed params override url params

				// Convert params object to a string.
				qs = jq_param(qs);
			}

			// Build URL from the base url, querystring and hash. In 'querystring'
			// mode, ? is only added if a query string exists. In 'fragment' mode, #
			// is always added.
			result = matches[1] + (is_fragment ? '#' : qs || !matches[1] ? '?' : '') + qs + hash;

		} else {
			// If URL was passed in, parse params from URL string, otherwise parse
			// params from window.location.
			result = get_func(url !== undefined ? url : loc[str_href]);
		}

		return result;
	};

	jq_param[str_querystring] = curry(jq_param_sub, 0, get_querystring);
	jq_param[str_fragment] = jq_param_fragment = curry(jq_param_sub, 1, get_fragment);

	// Section: Deparam (from string)
	// 
	// Method: jQuery.deparam
	// 
	// Deserialize a params string into an object, optionally coercing numbers,
	// booleans, null and undefined values; this method is the counterpart to the
	// internal jQuery.param method.
	// 
	// Usage:
	// 
	// > jQuery.deparam( params [, coerce ] );
	// 
	// Arguments:
	// 
	//  params - (String) A params string to be parsed.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false if omitted.
	// 
	// Returns:
	// 
	//  (Object) An object representing the deserialized params string.

	$.deparam = jq_deparam = function(params, coerce) {
		var obj = {},
      coerce_types = { 'true': !0, 'false': !1, 'null': null };

		// Iterate over all name=value pairs.
		$.each(params.replace(/\+/g, ' ').split('&'), function(j, v) {
			var param = v.split('='),
        key = decode(param[0]),
        val,
        cur = obj,
        i = 0,

			// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
			// into its component parts.
        keys = key.split(']['),
        keys_last = keys.length - 1;

			// If the first keys part contains [ and the last ends with ], then []
			// are correctly balanced.
			if (/\[/.test(keys[0]) && /\]$/.test(keys[keys_last])) {
				// Remove the trailing ] from the last keys part.
				keys[keys_last] = keys[keys_last].replace(/\]$/, '');

				// Split first keys part into two parts on the [ and add them back onto
				// the beginning of the keys array.
				keys = keys.shift().split('[').concat(keys);

				keys_last = keys.length - 1;
			} else {
				// Basic 'foo' style key.
				keys_last = 0;
			}

			// Are we dealing with a name=value pair, or just a name?
			if (param.length === 2) {
				val = decode(param[1]);

				// Coerce values.
				if (coerce) {
					val = val && !isNaN(val) ? +val              // number
            : val === 'undefined' ? undefined         // undefined
            : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
            : val;                                                // string
				}

				if (keys_last) {
					// Complex key, build deep object structure based on a few rules:
					// * The 'cur' pointer starts at the object top-level.
					// * [] = array push (n is set to array length), [n] = array if n is 
					//   numeric, otherwise object.
					// * If at the last keys part, set the value.
					// * For each keys part, if the current level is undefined create an
					//   object or array based on the type of the next keys part.
					// * Move the 'cur' pointer to the next level.
					// * Rinse & repeat.
					for (; i <= keys_last; i++) {
						key = keys[i] === '' ? cur.length : keys[i];
						cur = cur[key] = i < keys_last
              ? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : [])
              : val;
					}

				} else {
					// Simple key, even simpler rules, since only scalars and shallow
					// arrays are allowed.

					if ($.isArray(obj[key])) {
						// val is already an array, so push on the next value.
						obj[key].push(val);

					} else if (obj[key] !== undefined) {
						// val isn't an array, but since a second value has been specified,
						// convert val into an array.
						obj[key] = [obj[key], val];

					} else {
						// val is a scalar.
						obj[key] = val;
					}
				}

			} else if (key) {
				// No value was defined, so set something meaningful.
				obj[key] = coerce
          ? undefined
          : '';
			}
		});

		return obj;
	};

	// Method: jQuery.deparam.querystring
	// 
	// Parse the query string from a URL or the current window.location,
	// deserializing it into an object, optionally coercing numbers, booleans,
	// null and undefined values.
	// 
	// Usage:
	// 
	// > jQuery.deparam.querystring( [ url ] [, coerce ] );
	// 
	// Arguments:
	// 
	//  url - (String) An optional params string or URL containing query string
	//    params to be parsed. If url is omitted, the current window.location
	//    is used.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false if omitted.
	// 
	// Returns:
	// 
	//  (Object) An object representing the deserialized params string.

	// Method: jQuery.deparam.fragment
	// 
	// Parse the fragment (hash) from a URL or the current window.location,
	// deserializing it into an object, optionally coercing numbers, booleans,
	// null and undefined values.
	// 
	// Usage:
	// 
	// > jQuery.deparam.fragment( [ url ] [, coerce ] );
	// 
	// Arguments:
	// 
	//  url - (String) An optional params string or URL containing fragment (hash)
	//    params to be parsed. If url is omitted, the current window.location
	//    is used.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false if omitted.
	// 
	// Returns:
	// 
	//  (Object) An object representing the deserialized params string.

	function jq_deparam_sub(is_fragment, url_or_params, coerce) {
		if (url_or_params === undefined || typeof url_or_params === 'boolean') {
			// url_or_params not specified.
			coerce = url_or_params;
			url_or_params = jq_param[is_fragment ? str_fragment : str_querystring]();
		} else {
			url_or_params = is_string(url_or_params)
        ? url_or_params.replace(is_fragment ? re_trim_fragment : re_trim_querystring, '')
        : url_or_params;
		}

		return jq_deparam(url_or_params, coerce);
	};

	jq_deparam[str_querystring] = curry(jq_deparam_sub, 0);
	jq_deparam[str_fragment] = jq_deparam_fragment = curry(jq_deparam_sub, 1);

	// Section: Element manipulation
	// 
	// Method: jQuery.elemUrlAttr
	// 
	// Get the internal "Default URL attribute per tag" list, or augment the list
	// with additional tag-attribute pairs, in case the defaults are insufficient.
	// 
	// In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
	// is used to determine which attribute contains the URL to be modified, if
	// an "attr" param is not specified.
	// 
	// Default Tag-Attribute List:
	// 
	//  a      - href
	//  base   - href
	//  iframe - src
	//  img    - src
	//  input  - src
	//  form   - action
	//  link   - href
	//  script - src
	// 
	// Usage:
	// 
	// > jQuery.elemUrlAttr( [ tag_attr ] );
	// 
	// Arguments:
	// 
	//  tag_attr - (Object) An object containing a list of tag names and their
	//    associated default attribute names in the format { tag: 'attr', ... } to
	//    be merged into the internal tag-attribute list.
	// 
	// Returns:
	// 
	//  (Object) An object containing all stored tag-attribute values.

	// Only define function and set defaults if function doesn't already exist, as
	// the urlInternal plugin will provide this method as well.
	$[str_elemUrlAttr] || ($[str_elemUrlAttr] = function(obj) {
		return $.extend(elemUrlAttr_cache, obj);
	})({
		a: str_href,
		base: str_href,
		iframe: str_src,
		img: str_src,
		input: str_src,
		form: 'action',
		link: str_href,
		script: str_src
	});

	jq_elemUrlAttr = $[str_elemUrlAttr];

	// Method: jQuery.fn.querystring
	// 
	// Update URL attribute in one or more elements, merging the current URL (with
	// or without pre-existing query string params) plus any params object or
	// string into a new URL, which is then set into that attribute. Like
	// <jQuery.param.querystring (build url)>, but for all elements in a jQuery
	// collection.
	// 
	// Usage:
	// 
	// > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
	// 
	// Arguments:
	// 
	//  attr - (String) Optional name of an attribute that will contain a URL to
	//    merge params or url into. See <jQuery.elemUrlAttr> for a list of default
	//    attributes.
	//  params - (Object) A params object to be merged into the URL attribute.
	//  params - (String) A URL containing query string params, or params string
	//    to be merged into the URL attribute.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	//    
	//    * 0: params in the params argument will override any params in attr URL.
	//    * 1: any params in attr URL will override params in the params argument.
	//    * 2: params argument will completely replace any query string in attr
	//         URL.
	// 
	// Returns:
	// 
	//  (jQuery) The initial jQuery collection of elements, but with modified URL
	//  attribute values.

	// Method: jQuery.fn.fragment
	// 
	// Update URL attribute in one or more elements, merging the current URL (with
	// or without pre-existing fragment/hash params) plus any params object or
	// string into a new URL, which is then set into that attribute. Like
	// <jQuery.param.fragment (build url)>, but for all elements in a jQuery
	// collection.
	// 
	// Usage:
	// 
	// > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
	// 
	// Arguments:
	// 
	//  attr - (String) Optional name of an attribute that will contain a URL to
	//    merge params into. See <jQuery.elemUrlAttr> for a list of default
	//    attributes.
	//  params - (Object) A params object to be merged into the URL attribute.
	//  params - (String) A URL containing fragment (hash) params, or params
	//    string to be merged into the URL attribute.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified, and is as-follows:
	//    
	//    * 0: params in the params argument will override any params in attr URL.
	//    * 1: any params in attr URL will override params in the params argument.
	//    * 2: params argument will completely replace any fragment (hash) in attr
	//         URL.
	// 
	// Returns:
	// 
	//  (jQuery) The initial jQuery collection of elements, but with modified URL
	//  attribute values.

	function jq_fn_sub(mode, force_attr, params, merge_mode) {
		if (!is_string(params) && typeof params !== 'object') {
			// force_attr not specified.
			merge_mode = params;
			params = force_attr;
			force_attr = undefined;
		}

		return this.each(function() {
			var that = $(this),

			// Get attribute specified, or default specified via $.elemUrlAttr.
        attr = force_attr || jq_elemUrlAttr()[(this.nodeName || '').toLowerCase()] || '',

			// Get URL value.
        url = attr && that.attr(attr) || '';

			// Update attribute with new URL.
			that.attr(attr, jq_param[mode](url, params, merge_mode));
		});

	};

	$.fn[str_querystring] = curry(jq_fn_sub, str_querystring);
	$.fn[str_fragment] = curry(jq_fn_sub, str_fragment);

	// Section: History, hashchange event
	// 
	// Method: jQuery.bbq.pushState
	// 
	// Adds a 'state' into the browser history at the current position, setting
	// location.hash and triggering any bound <window.onhashchange> event
	// callbacks (provided the new state is different than the previous state).
	// 
	// If no arguments are passed, an empty state is created, which is just a
	// shortcut for jQuery.bbq.pushState( {}, 2 ).
	// 
	// Usage:
	// 
	// > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
	// 
	// Arguments:
	// 
	//  params - (String) A serialized params string or a hash string beginning
	//    with # to merge into location.hash.
	//  params - (Object) A params object to merge into location.hash.
	//  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
	//    specified (unless a hash string beginning with # is specified, in which
	//    case merge behavior defaults to 2), and is as-follows:
	// 
	//    * 0: params in the params argument will override any params in the
	//         current state.
	//    * 1: any params in the current state will override params in the params
	//         argument.
	//    * 2: params argument will completely replace current state.
	// 
	// Returns:
	// 
	//  Nothing.
	// 
	// Additional Notes:
	// 
	//  * Setting an empty state may cause the browser to scroll.
	//  * Unlike the fragment and querystring methods, if a hash string beginning
	//    with # is specified as the params agrument, merge_mode defaults to 2.

	jq_bbq.pushState = jq_bbq_pushState = function(params, merge_mode) {
		if (is_string(params) && /^#/.test(params) && merge_mode === undefined) {
			// Params string begins with # and merge_mode not specified, so completely
			// overwrite window.location.hash.
			merge_mode = 2;
		}

		var has_args = params !== undefined,
		// Merge params into window.location using $.param.fragment.
      url = jq_param_fragment(loc[str_href], has_args ? params : {}, has_args ? merge_mode : 2);

		// Set new window.location.href. If hash is empty, use just # to prevent
		// browser from reloading the page. Note that Safari 3 & Chrome barf on
		// location.hash = '#'.
		loc[str_href] = url + (/#/.test(url) ? '' : '#');
	};


	// Method: jQuery.bbq.getState
	// 
	// Retrieves the current 'state' from the browser history, parsing
	// location.hash for a specific key or returning an object containing the
	// entire state, optionally coercing numbers, booleans, null and undefined
	// values.
	// 
	// Usage:
	// 
	// > jQuery.bbq.getState( [ key ] [, coerce ] );
	// 
	// Arguments:
	// 
	//  key - (String) An optional state key for which to return a value.
	//  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
	//    undefined to their actual value. Defaults to false.
	// 
	// Returns:
	// 
	//  (Anything) If key is passed, returns the value corresponding with that key
	//    in the location.hash 'state', or undefined. If not, an object
	//    representing the entire 'state' is returned.

	jq_bbq.getState = jq_bbq_getState = function(key, coerce) {
		return key === undefined || typeof key === 'boolean'
      ? jq_deparam_fragment(key) // 'key' really means 'coerce' here
      : jq_deparam_fragment(coerce)[key];
	};

	// Method: jQuery.bbq.removeState
	// 
	// Remove one or more keys from the current browser history 'state', creating
	// a new state, setting location.hash and triggering any bound
	// <window.onhashchange> event callbacks (provided the new state is different
	// than the previous state).
	// 
	// If no arguments are passed, an empty state is created, which is just a
	// shortcut for jQuery.bbq.pushState( {}, 2 ).
	// 
	// Usage:
	// 
	// > jQuery.bbq.removeState( [ key [, key ... ] ] );
	// 
	// Arguments:
	// 
	//  key - (String) One or more key values to remove from the current state,
	//    passed as individual arguments.
	//  key - (Array) A single array argument that contains a list of key values
	//    to remove from the current state.
	// 
	// Returns:
	// 
	//  Nothing.
	// 
	// Additional Notes:
	// 
	//  * Setting an empty state may cause the browser to scroll.

	jq_bbq.removeState = function(arr) {
		var state = {};

		// If one or more arguments is passed..
		if (arr !== undefined) {

			// Get the current state.
			state = jq_bbq_getState();

			// For each passed key, delete the corresponding property from the current
			// state.
			$.each($.isArray(arr) ? arr : arguments, function(i, v) {
				delete state[v];
			});
		}

		// Set the state, completely overriding any existing state.
		jq_bbq_pushState(state, 2);
	};

	// Event: window.onhashchange
	// 
	// Usage in 1.4a2 and newer:
	// 
	// In 1.4a2 and newer, the event object that is passed into the callback is
	// augmented with an additional e.fragment property that contains the current
	// document location.hash state as a string, as well as an e.getState method.
	// 
	// e.fragment is equivalent to the output of <jQuery.param.fragment>, and
	// e.getState() is equivalent to <jQuery.bbq.getState>, except that they refer
	// to the event-specific state value stored in the event object, instead of
	// the current window.location, allowing the event object to be referenced
	// later, even if window.location has changed.
	// 
	// > $(window).bind( 'hashchange', function(e) {
	// >   var hash_str = e.fragment,
	// >     param_obj = e.getState(),
	// >     param_val = e.getState( 'param_name' ),
	// >     param_val_coerced = e.getState( 'param_name', true );
	// >   ...
	// > });
	// 
	// Usage in 1.3.2:
	// 
	// In 1.3.2, the event object is unable to be augmented as in 1.4a2+, so the
	// fragment state isn't bound to the event object and must instead be parsed
	// using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
	// 
	// > $(window).bind( 'hashchange', function(e) {
	// >   var hash_str = $.param.fragment(),
	// >     param_obj = $.bbq.getState(),
	// >     param_val = $.bbq.getState( 'param_name' ),
	// >     param_val_coerced = $.bbq.getState( 'param_name', true );
	// >   ...
	// > });
	// 
	// Additional Notes:
	// 
	// * See <jQuery hashchange event> for more detailed information.

	jq_event_special[str_hashchange] = $.extend(jq_event_special[str_hashchange], {

		// Augmenting the event object with the .fragment property and .getState
		// method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
		// work, but the event won't be augmented)
		add: function(handler, data, namespaces) {
			return function(e) {
				// e.fragment is set to the value of location.hash (with any leading #
				// removed) at the time the event is triggered.
				var hash = e[str_fragment] = jq_param_fragment();

				// e.getState() works just like $.bbq.getState(), but uses the
				// e.fragment property stored on the event object.
				e.getState = function(key, coerce) {
					return key === undefined || typeof key === 'boolean'
            ? jq_deparam(hash, key) // 'key' really means 'coerce' here
            : jq_deparam(hash, coerce)[key];
				};

				handler.apply(this, arguments);
			};
		}

	});

})(jQuery, this);

/*!
* jQuery hashchange event - v1.0 - 1/9/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
* 
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/

// Script: jQuery hashchange event
//
// *Version: 1.0, Last updated: 1/9/2010*
// 
// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
// GitHub       - http://github.com/cowboy/jquery-hashchange/
// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (1.1kb)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// This working example, complete with fully commented code, illustrate one way
// in which this plugin can be used.
// 
// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.3.2, 1.4a2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.
// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
// 
// About: Known issues
// 
// While this jQuery hashchange event implementation is quite stable and robust,
// there are a few unfortunate browser bugs surrounding expected hashchange
// event-based behaviors, independent of any JavaScript window.onhashchange
// abstraction. See the following examples for more information:
// 
// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
// 
// About: Release History
// 
// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
//         window.onhashchange functionality into a separate plugin for users
//         who want just the basic event & back button support, without all the
//         extra awesomeness that BBQ provides. This plugin will be included as
//         part of jQuery BBQ, but also be available separately.

(function($, window) {
	'$:nomunge'; // Used by YUI compressor.

	// A convenient shortcut.
	var loc = window.location,

	// Method / object references.
    fake_onhashchange,
    jq_event_special = $.event.special,

	// Reused strings.
    str_hashchange = 'hashchange',

	// IE6/7 specifically need some special love when it comes to back-button
	// support, so let's do a little browser sniffing..
    browser = $.browser,
    is_old_ie = browser.msie && browser.version < 8,

	// Does the browser support window.onhashchange? Test for IE version, since
	// IE8 incorrectly reports this when in "IE7" or "IE8 Compatibility View"!
    supports_onhashchange = 'on' + str_hashchange in window && !is_old_ie;

	// Get location.hash (or what you'd expect location.hash to be) sans any
	// leading #. Thanks for making this necessary, Firefox!
	function get_fragment(url) {
		url = url || loc.href;
		return url.replace(/^[^#]*#?(.*)$/, '$1');
	};

	// Property: jQuery.hashchangeDelay
	// 
	// The numeric interval (in milliseconds) at which the <window.onhashchange>
	// polling loop executes. Defaults to 100.

	$[str_hashchange + 'Delay'] = 100;

	// Event: window.onhashchange
	// 
	// Fired when window.location.hash changes. In browsers that support it, the
	// native window.onhashchange event is used (IE8, FF3.6), otherwise a polling
	// loop is initialized, running every <jQuery.hashchangeDelay> milliseconds
	// to see if the hash has changed. In IE 6 and 7, a hidden IFRAME is created
	// to allow the back button and hash-based history to work.
	// 
	// Usage:
	// 
	// > $(window).bind( 'hashchange', function(e) {
	// >   var hash = location.hash;
	// >   ...
	// > });
	// 
	// Additional Notes:
	// 
	// * The polling loop and iframe are not created until at least one callback
	//   is actually bound to 'hashchange'.
	// * If you need the bound callback(s) to execute immediately, in cases where
	//   the page 'state' exists on page load (via bookmark or page refresh, for
	//   example) use $(window).trigger( 'hashchange' );

	jq_event_special[str_hashchange] = $.extend(jq_event_special[str_hashchange], {

		// Called only when the first 'hashchange' event is bound to window.
		setup: function() {
			// If window.onhashchange is supported natively, there's nothing to do..
			if (supports_onhashchange) { return false; }

			// Otherwise, we need to create our own. And we don't want to call this
			// until the user binds to the event, just in case they never do, since it
			// will create a polling loop and possibly even a hidden IFRAME.
			fake_onhashchange.start();
		},

		// Called only when the last 'hashchange' event is unbound from window.
		teardown: function() {
			// If window.onhashchange is supported natively, there's nothing to do..
			if (supports_onhashchange) { return false; }

			// Otherwise, we need to stop ours (if possible).
			fake_onhashchange.stop();
		}

	});

	// fake_onhashchange does all the work of triggering the window.onhashchange
	// event for browsers that don't natively support it, including creating a
	// polling loop to watch for hash changes and in IE 6/7 creating a hidden
	// IFRAME to enable back and forward.
	fake_onhashchange = (function() {
		var self = {},
      timeout_id,
      iframe,
      set_history,
      get_history;

		// Initialize. In IE 6/7, creates a hidden IFRAME for history handling.
		function init() {
			// Most browsers don't need special methods here..
			set_history = get_history = function(val) { return val; };

			// But IE6/7 do!
			if (is_old_ie) {

				// Create hidden IFRAME at the end of the body.
				iframe = $('<iframe src="javascript:0"/>').hide().appendTo('body')[0].contentWindow;

				// Get history by looking at the hidden IFRAME's location.hash.
				get_history = function() {
					return get_fragment(iframe.document.location.href);
				};

				// Set a new history item by opening and then closing the IFRAME
				// document, *then* setting its location.hash.
				set_history = function(hash, history_hash) {
					if (hash !== history_hash) {
						var doc = iframe.document;
						doc.open().close();
						doc.location.hash = '#' + hash;
					}
				};

				// Set initial history.
				set_history(get_fragment());
			}
		};

		// Start the polling loop.
		self.start = function() {
			// Polling loop is already running!
			if (timeout_id) { return; }

			// Remember the initial hash so it doesn't get triggered immediately.
			var last_hash = get_fragment();

			// Initialize if not yet initialized.
			set_history || init();

			// This polling loop checks every $.hashchangeDelay milliseconds to see if
			// location.hash has changed, and triggers the 'hashchange' event on
			// window when necessary.
			(function loopy() {
				var hash = get_fragment(),
          history_hash = get_history(last_hash);

				if (hash !== last_hash) {
					set_history(last_hash = hash, history_hash);

					$(window).trigger(str_hashchange);

				} else if (history_hash !== last_hash) {
					loc.href = loc.href.replace(/#.*/, '') + '#' + history_hash;
				}

				timeout_id = setTimeout(loopy, $[str_hashchange + 'Delay']);
			})();
		};

		// Stop the polling loop, but only if an IE6/7 IFRAME wasn't created. In
		// that case, even if there are no longer any bound event handlers, the
		// polling loop is still necessary for back/next to work at all!
		self.stop = function() {
			if (!iframe) {
				timeout_id && clearTimeout(timeout_id);
				timeout_id = 0;
			}
		};

		return self;
	})();

})(jQuery, this);
