Show:
                            /**
                             * Provides browser history management backed by
                             * <code>window.location.hash</code>, as well as convenience methods for working
                             * with the location hash and a synthetic <code>hashchange</code> event that
                             * normalizes differences across browsers.
                             *
                             * @module history
                             * @submodule history-hash
                             * @since 3.2.0
                             * @class HistoryHash
                             * @extends HistoryBase
                             * @constructor
                             * @param {Object} config (optional) Configuration object. See the HistoryBase
                             *   documentation for details.
                             */
                            
                            var HistoryBase = Y.HistoryBase,
                                Lang        = Y.Lang,
                                YArray      = Y.Array,
                                YObject     = Y.Object,
                                GlobalEnv   = YUI.namespace('Env.HistoryHash'),
                            
                                SRC_HASH    = 'hash',
                            
                                hashNotifiers,
                                oldHash,
                                oldUrl,
                                win             = Y.config.win,
                                useHistoryHTML5 = Y.config.useHistoryHTML5;
                            
                            function HistoryHash() {
                                HistoryHash.superclass.constructor.apply(this, arguments);
                            }
                            
                            Y.extend(HistoryHash, HistoryBase, {
                                // -- Initialization -------------------------------------------------------
                                _init: function (config) {
                                    var bookmarkedState = HistoryHash.parseHash();
                            
                                    // If an initialState was provided, merge the bookmarked state into it
                                    // (the bookmarked state wins).
                                    config = config || {};
                            
                                    this._initialState = config.initialState ?
                                            Y.merge(config.initialState, bookmarkedState) : bookmarkedState;
                            
                                    // Subscribe to the synthetic hashchange event (defined below) to handle
                                    // changes.
                                    Y.after('hashchange', Y.bind(this._afterHashChange, this), win);
                            
                                    HistoryHash.superclass._init.apply(this, arguments);
                                },
                            
                                // -- Protected Methods ----------------------------------------------------
                                _change: function (src, state, options) {
                                    // Stringify all values to ensure that comparisons don't fail after
                                    // they're coerced to strings in the location hash.
                                    YObject.each(state, function (value, key) {
                                        if (Lang.isValue(value)) {
                                            state[key] = value.toString();
                                        }
                                    });
                            
                                    return HistoryHash.superclass._change.call(this, src, state, options);
                                },
                            
                                _storeState: function (src, newState) {
                                    var decode  = HistoryHash.decode,
                                        newHash = HistoryHash.createHash(newState);
                            
                                    HistoryHash.superclass._storeState.apply(this, arguments);
                            
                                    // Update the location hash with the changes, but only if the new hash
                                    // actually differs from the current hash (this avoids creating multiple
                                    // history entries for a single state).
                                    //
                                    // We always compare decoded hashes, since it's possible that the hash
                                    // could be set incorrectly to a non-encoded value outside of
                                    // HistoryHash.
                                    if (src !== SRC_HASH && decode(HistoryHash.getHash()) !== decode(newHash)) {
                                        HistoryHash[src === HistoryBase.SRC_REPLACE ? 'replaceHash' : 'setHash'](newHash);
                                    }
                                },
                            
                                // -- Protected Event Handlers ---------------------------------------------
                            
                                /**
                                 * Handler for hashchange events.
                                 *
                                 * @method _afterHashChange
                                 * @param {Event} e
                                 * @protected
                                 */
                                _afterHashChange: function (e) {
                                    this._resolveChanges(SRC_HASH, HistoryHash.parseHash(e.newHash), {});
                                }
                            }, {
                                // -- Public Static Properties ---------------------------------------------
                                NAME: 'historyHash',
                            
                                /**
                                 * Constant used to identify state changes originating from
                                 * <code>hashchange</code> events.
                                 *
                                 * @property SRC_HASH
                                 * @type String
                                 * @static
                                 * @final
                                 */
                                SRC_HASH: SRC_HASH,
                            
                                /**
                                 * <p>
                                 * Prefix to prepend when setting the hash fragment. For example, if the
                                 * prefix is <code>!</code> and the hash fragment is set to
                                 * <code>#foo=bar&baz=quux</code>, the final hash fragment in the URL will
                                 * become <code>#!foo=bar&baz=quux</code>. This can be used to help make an
                                 * Ajax application crawlable in accordance with Google's guidelines at
                                 * <a href="http://code.google.com/web/ajaxcrawling/">http://code.google.com/web/ajaxcrawling/</a>.
                                 * </p>
                                 *
                                 * <p>
                                 * Note that this prefix applies to all HistoryHash instances. It's not
                                 * possible for individual instances to use their own prefixes since they
                                 * all operate on the same URL.
                                 * </p>
                                 *
                                 * @property hashPrefix
                                 * @type String
                                 * @default ''
                                 * @static
                                 */
                                hashPrefix: '',
                            
                                // -- Protected Static Properties ------------------------------------------
                            
                                /**
                                 * Regular expression used to parse location hash/query strings.
                                 *
                                 * @property _REGEX_HASH
                                 * @type RegExp
                                 * @protected
                                 * @static
                                 * @final
                                 */
                                _REGEX_HASH: /([^\?#&=]+)=?([^&=]*)/g,
                            
                                // -- Public Static Methods ------------------------------------------------
                            
                                /**
                                 * Creates a location hash string from the specified object of key/value
                                 * pairs.
                                 *
                                 * @method createHash
                                 * @param {Object} params object of key/value parameter pairs
                                 * @return {String} location hash string
                                 * @static
                                 */
                                createHash: function (params) {
                                    var encode = HistoryHash.encode,
                                        hash   = [];
                            
                                    YObject.each(params, function (value, key) {
                                        if (Lang.isValue(value)) {
                                            hash.push(encode(key) + '=' + encode(value));
                                        }
                                    });
                            
                                    return hash.join('&');
                                },
                            
                                /**
                                 * Wrapper around <code>decodeURIComponent()</code> that also converts +
                                 * chars into spaces.
                                 *
                                 * @method decode
                                 * @param {String} string string to decode
                                 * @return {String} decoded string
                                 * @static
                                 */
                                decode: function (string) {
                                    return decodeURIComponent(string.replace(/\+/g, ' '));
                                },
                            
                                /**
                                 * Wrapper around <code>encodeURIComponent()</code> that converts spaces to
                                 * + chars.
                                 *
                                 * @method encode
                                 * @param {String} string string to encode
                                 * @return {String} encoded string
                                 * @static
                                 */
                                encode: function (string) {
                                    return encodeURIComponent(string).replace(/%20/g, '+');
                                },
                            
                                /**
                                 * Gets the raw (not decoded) current location hash, minus the preceding '#'
                                 * character and the hashPrefix (if one is set).
                                 *
                                 * @method getHash
                                 * @return {String} current location hash
                                 * @static
                                 */
                                getHash: (Y.UA.gecko ? function () {
                                    // Gecko's window.location.hash returns a decoded string and we want all
                                    // encoding untouched, so we need to get the hash value from
                                    // window.location.href instead. We have to use UA sniffing rather than
                                    // feature detection, since the only way to detect this would be to
                                    // actually change the hash.
                                    var location = Y.getLocation(),
                                        matches  = /#(.*)$/.exec(location.href),
                                        hash     = matches && matches[1] || '',
                                        prefix   = HistoryHash.hashPrefix;
                            
                                    return prefix && hash.indexOf(prefix) === 0 ?
                                                hash.replace(prefix, '') : hash;
                                } : function () {
                                    var location = Y.getLocation(),
                                        hash     = location.hash.substring(1),
                                        prefix   = HistoryHash.hashPrefix;
                            
                                    // Slight code duplication here, but execution speed is of the essence
                                    // since getHash() is called every 50ms to poll for changes in browsers
                                    // that don't support native onhashchange. An additional function call
                                    // would add unnecessary overhead.
                                    return prefix && hash.indexOf(prefix) === 0 ?
                                                hash.replace(prefix, '') : hash;
                                }),
                            
                                /**
                                 * Gets the current bookmarkable URL.
                                 *
                                 * @method getUrl
                                 * @return {String} current bookmarkable URL
                                 * @static
                                 */
                                getUrl: function () {
                                    return location.href;
                                },
                            
                                /**
                                 * Parses a location hash string into an object of key/value parameter
                                 * pairs. If <i>hash</i> is not specified, the current location hash will
                                 * be used.
                                 *
                                 * @method parseHash
                                 * @param {String} hash (optional) location hash string
                                 * @return {Object} object of parsed key/value parameter pairs
                                 * @static
                                 */
                                parseHash: function (hash) {
                                    var decode = HistoryHash.decode,
                                        i,
                                        len,
                                        match,
                                        matches,
                                        param,
                                        params = {},
                                        prefix = HistoryHash.hashPrefix,
                                        prefixIndex;
                            
                                    hash = Lang.isValue(hash) ? hash : HistoryHash.getHash();
                            
                                    if (prefix) {
                                        prefixIndex = hash.indexOf(prefix);
                            
                                        if (prefixIndex === 0 || (prefixIndex === 1 && hash.charAt(0) === '#')) {
                                            hash = hash.replace(prefix, '');
                                        }
                                    }
                            
                                    matches = hash.match(HistoryHash._REGEX_HASH) || [];
                            
                                    for (i = 0, len = matches.length; i < len; ++i) {
                                        match = matches[i];
                            
                                        param = match.split('=');
                            
                                        if (param.length > 1) {
                                            params[decode(param[0])] = decode(param[1]);
                                        } else {
                                            params[decode(match)] = '';
                                        }
                                    }
                            
                                    return params;
                                },
                            
                                /**
                                 * Replaces the browser's current location hash with the specified hash
                                 * and removes all forward navigation states, without creating a new browser
                                 * history entry. Automatically prepends the <code>hashPrefix</code> if one
                                 * is set.
                                 *
                                 * @method replaceHash
                                 * @param {String} hash new location hash
                                 * @static
                                 */
                                replaceHash: function (hash) {
                                    var location = Y.getLocation(),
                                        base     = location.href.replace(/#.*$/, '');
                            
                                    if (hash.charAt(0) === '#') {
                                        hash = hash.substring(1);
                                    }
                            
                                    location.replace(base + '#' + (HistoryHash.hashPrefix || '') + hash);
                                },
                            
                                /**
                                 * Sets the browser's location hash to the specified string. Automatically
                                 * prepends the <code>hashPrefix</code> if one is set.
                                 *
                                 * @method setHash
                                 * @param {String} hash new location hash
                                 * @static
                                 */
                                setHash: function (hash) {
                                    var location = Y.getLocation();
                            
                                    if (hash.charAt(0) === '#') {
                                        hash = hash.substring(1);
                                    }
                            
                                    location.hash = (HistoryHash.hashPrefix || '') + hash;
                                }
                            });
                            
                            // -- Synthetic hashchange Event -----------------------------------------------
                            
                            // TODO: YUIDoc currently doesn't provide a good way to document synthetic DOM
                            // events. For now, we're just documenting the hashchange event on the YUI
                            // object, which is about the best we can do until enhancements are made to
                            // YUIDoc.
                            
                            /**
                            Synthetic <code>window.onhashchange</code> event that normalizes differences
                            across browsers and provides support for browsers that don't natively support
                            <code>onhashchange</code>.
                            
                            This event is provided by the <code>history-hash</code> module.
                            
                            @example
                            
                                YUI().use('history-hash', function (Y) {
                                  Y.on('hashchange', function (e) {
                                    // Handle hashchange events on the current window.
                                  }, Y.config.win);
                                });
                            
                            @event hashchange
                            @param {EventFacade} e Event facade with the following additional
                              properties:
                            
                            <dl>
                              <dt>oldHash</dt>
                              <dd>
                                Previous hash fragment value before the change.
                              </dd>
                            
                              <dt>oldUrl</dt>
                              <dd>
                                Previous URL (including the hash fragment) before the change.
                              </dd>
                            
                              <dt>newHash</dt>
                              <dd>
                                New hash fragment value after the change.
                              </dd>
                            
                              <dt>newUrl</dt>
                              <dd>
                                New URL (including the hash fragment) after the change.
                              </dd>
                            </dl>
                            @for YUI
                            @since 3.2.0
                            **/
                            
                            hashNotifiers = GlobalEnv._notifiers;
                            
                            if (!hashNotifiers) {
                                hashNotifiers = GlobalEnv._notifiers = [];
                            }
                            
                            Y.Event.define('hashchange', {
                                on: function (node, subscriber, notifier) {
                                    // Ignore this subscription if the node is anything other than the
                                    // window or document body, since those are the only elements that
                                    // should support the hashchange event. Note that the body could also be
                                    // a frameset, but that's okay since framesets support hashchange too.
                                    if (node.compareTo(win) || node.compareTo(Y.config.doc.body)) {
                                        hashNotifiers.push(notifier);
                                    }
                                },
                            
                                detach: function (node, subscriber, notifier) {
                                    var index = YArray.indexOf(hashNotifiers, notifier);
                            
                                    if (index !== -1) {
                                        hashNotifiers.splice(index, 1);
                                    }
                                }
                            });
                            
                            oldHash = HistoryHash.getHash();
                            oldUrl  = HistoryHash.getUrl();
                            
                            if (HistoryBase.nativeHashChange) {
                                // Wrap the browser's native hashchange event if there's not already a
                                // global listener.
                                if (!GlobalEnv._hashHandle) {
                                    GlobalEnv._hashHandle = Y.Event.attach('hashchange', function (e) {
                                        var newHash = HistoryHash.getHash(),
                                            newUrl  = HistoryHash.getUrl();
                            
                                        // Iterate over a copy of the hashNotifiers array since a subscriber
                                        // could detach during iteration and cause the array to be re-indexed.
                                        YArray.each(hashNotifiers.concat(), function (notifier) {
                                            notifier.fire({
                                                _event : e,
                                                oldHash: oldHash,
                                                oldUrl : oldUrl,
                                                newHash: newHash,
                                                newUrl : newUrl
                                            });
                                        });
                            
                                        oldHash = newHash;
                                        oldUrl  = newUrl;
                                    }, win);
                                }
                            } else {
                                // Begin polling for location hash changes if there's not already a global
                                // poll running.
                                if (!GlobalEnv._hashPoll) {
                                    GlobalEnv._hashPoll = Y.later(50, null, function () {
                                        var newHash = HistoryHash.getHash(),
                                            facade, newUrl;
                            
                                        if (oldHash !== newHash) {
                                            newUrl = HistoryHash.getUrl();
                            
                                            facade = {
                                                oldHash: oldHash,
                                                oldUrl : oldUrl,
                                                newHash: newHash,
                                                newUrl : newUrl
                                            };
                            
                                            oldHash = newHash;
                                            oldUrl  = newUrl;
                            
                                            YArray.each(hashNotifiers.concat(), function (notifier) {
                                                notifier.fire(facade);
                                            });
                                        }
                                    }, null, true);
                                }
                            }
                            
                            Y.HistoryHash = HistoryHash;
                            
                            // HistoryHash will never win over HistoryHTML5 unless useHistoryHTML5 is false.
                            if (useHistoryHTML5 === false || (!Y.History && useHistoryHTML5 !== true &&
                                    (!HistoryBase.html5 || !Y.HistoryHTML5))) {
                                Y.History = HistoryHash;
                            }