history-debug.js revision 7171ee92f007bcaf39a1944e715d2514dd446c10
/**
* Provides browser history management functionality using a simple
*
* <p>
* The history-base module uses a simple object to store state. To integrate
* navigate between states, use history-hash.
* </p>
*
* @module history
* @submodule history-base
*/
/**
* The HistoryBase class provides basic state management functionality backed by
* an object. History state is shared globally among all instances and
* subclass instances of HistoryBase.
*
* <p>
* If provided, the optional <em>initialState</em> object will be merged with
* the current global state.
* </p>
*
* @class HistoryBase
* @uses EventTarget
* @constructor
* @param {Object} config (optional) configuration object, which may contain
* zero or more of the following properties:
*
* <dl>
* <dt>initialState (Object)</dt>
* <dd>
* </dd>
* </dl>
*/
Obj = Y.Object,
EVT_CHANGE = 'change',
NAME = 'historyBase',
HistoryBase = function (config) {
};
emitFacade : true,
prefix : 'history',
preventable: false,
queueable : true
});
}
// -- Public Static Properties -------------------------------------------------
/**
* Name of this component.
*
* @property NAME
* @type String
* @static
*/
// -- Initialization -------------------------------------------------------
/**
* Initializes this HistoryBase instance. This method is called by the
* constructor.
*
* @method _init
* @param {Object} config configuration object
* @protected
*/
/**
* Fired when the state changes. To be notified of all state changes
* regardless of the History or YUI instance that generated them,
* subscribe to this event on Y.Global. If you would rather be notified
* only about changes generated by this specific History instance,
* subscribe to this event on the instance.
*
* @event history:change
* @param {EventFacade} Event facade with the following additional
* properties:
*
* <dl>
* <dt>changed</dt>
* <dd>
* Object hash of state items that have been added or changed. The
* key is the item key, and the value is an object containing
* <code>newVal</code> and <code>prevVal</code> properties
* representing the values of the item both before and after the
* change. If the item was newly added, <code>prevVal</code> will be
* <code>undefined</code>.
* </dd>
*
* <dt>newVal</dt>
* <dd>
* change.
* </dd>
*
* <dt>prevVal</dt>
* <dd>
* change.
* </dd>
*
* <dt>removed</dt>
* <dd>
* removed. Values are the old values prior to removal.
* </dd>
* </dl>
*/
this.publish(EVT_CHANGE, {
broadcast: 2,
});
// If initialState was provided and is a simple object, merge it into
// the current state.
}
},
// -- Public Methods -------------------------------------------------------
/**
* Adds a state entry with new values for the specified key or keys. Any key
* with a <code>null</code> or <code>undefined</code> value will be removed
* from the state; all others will be merged into it.
*
* @method add
* the name of a single key
* @param {String|null} value (optional) if <em>state</em> is the name of a
* single key, <em>value</em> will become its new value
* @chainable
*/
var key;
state = {};
}
return this;
},
/**
* Returns the current value of the state parameter specified by
* parameters if no key is specified.
*
* @method get
* @param {String} key (optional) state parameter key
* @return {Object|mixed} value of the specified state parameter, or an
*/
if (key) {
} else {
}
},
/**
* Replaces the current state entry with new values for the specified
* parameters, just as with <code>add()</code>, except that no change events
* are generated.
*
* @method replace
* the name of a single key
* @param {String|null} value (optional) if <em>state</em> is the name of a
* single key, <em>value</em> will become its new value
* @chainable
*/
var key;
state = {};
}
return this;
},
// -- Protected Methods ----------------------------------------------------
/**
* Fires a dynamic "[key]Change" event.
*
* @method _fireChangeEvent
* @param {Object} value object hash containing <em>newVal</em> and
* <em>prevVal</em> properties for the changed item
* @param {String} key key of the item that was changed
* @protected
*/
// TODO: how to document this?
});
},
/**
* Fires a dynamic "[key]Remove" event.
*
* @method _fireRemoveEvent
* @param {mixed} value value of the item prior to its removal
* @param {String} key key of the item that was removed
* @protected
*/
// TODO: how to document this?
},
/**
* Called by _resolveChanges() when the state has changed. This method takes
* care of actually firing the history:change event if <em>silent</em> is
* <code>false</code>, or storing the new state if <em>silent</em> is
* <code>true</code>.
*
* @method _handleChanges
* @param {Object} changes resolved changes
* @param {Boolean} silent (optional) if <em>true</em>, no change events
* will be fired
* @protected
*/
if (silent) {
} else {
// Fire the global change event.
this.fire(EVT_CHANGE, {
});
}
},
/**
* Resolves the changes (if any) between <em>newState</em> and the current
* state and fires appropriate events if things have changed.
*
* @method _resolveChanges
* new state
* @param {Boolean} silent (optional) if <code>true</code>, no change events
* will be fired
* @protected
*/
var changed = {},
removed = {};
// Figure out what was added or changed.
};
isChanged = true;
}
}, this);
// Figure out what was removed.
// TODO: Could possibly improve performance slightly by not checking
// removed. Need to profile to see if it's actually worth it.
isChanged = true;
}
}, this);
if (isChanged) {
this._handleChanges({
}, silent);
}
},
/**
* Stores the specified state. Don't call this method directly; go through
* _resolveChanges() to ensure that changes are resolved and all events are
* fired properly.
*
* @method _storeState
* @param {Object} newState new state to store
* @param {Boolean} silent (optional) if <em>true</em>, the state change
* should be silent
* @protected
*/
},
// -- Protected Event Handlers ---------------------------------------------
/**
* Default change event handler.
*
* @method _defChangeFn
* @param {EventFacade} e state change event facade
* @protected
*/
_defChangeFn: function (e) {
this._storeState(e.newVal);
}
}, true);
Y.HistoryBase = HistoryBase;
/**
* The history-hash module adds the History class, which provides browser
* history management functionality backed by <code>window.location.hash</code>.
* This allows the browser's back and forward buttons to be used to navigate
* between states.
*
* @module history
* @submodule history-hash
*/
/**
* The History class 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.
*
* @class History
* @extends HistoryBase
* @constructor
*/
Obj = Y.Object,
// IE8 supports the hashchange event, but only in IE8 Standards
// Mode. However, IE8 in IE7 compatibility mode still defines the
// event but never fires it, so we can't just sniff for the event. We also
// can't just sniff for IE8, since other browsers have begun to support this
// event as well.
};
// -- Initialization -------------------------------------------------------
// Use the bookmarked state as the initialState if no initialState was
// specified.
this.constructor.parseHash();
// Subscribe to the synthetic hashchange event (defined below) to handle
// changes.
},
// -- Protected Methods ----------------------------------------------------
var constructor = this.constructor;
// 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).
}
},
// -- Protected Event Handlers ---------------------------------------------
/**
* Handler for hashchange events.
*
* @method _afterHashChange
* @protected
*/
_afterHashChange: function (e) {
}
}, {
// -- Public Static Properties ---------------------------------------------
NAME: 'history',
/**
* Whether or not this browser supports the window.onhashchange event
* natively. Note that even if this is <code>true</code>, you may still want
* to use History's synthetic hashchange event since it normalizes
* implementation differences and fixes spec violations across various
* browsers.
*
* @property nativeHashChange
* @type Boolean
* @default false
* @static
*/
// -- Protected Static Properties ------------------------------------------
/**
*
* @property _REGEX_HASH
* @type RegExp
* @protected
* @static
*/
_REGEX_HASH: /([^\?#&]+)=([^&]+)/g,
// -- Public Static Methods ------------------------------------------------
/**
* pairs.
*
* @method createHash
* @return {String} location hash string
* @static
*/
createHash: function (params) {
hash = [];
}
});
},
/**
* Wrapper around <code>decodeURIComponent()</code> that also converts +
* chars into spaces.
*
* @method _decode
* @param {String} string string to decode
* @return {String} decoded string
* @static
*/
},
/**
* Wrapper around <code>encodeURIComponent()</code> that converts spaces to
* + chars.
*
* @method encode
* @param {String} string string to encode
* @return {String} encoded string
* @static
*/
},
/**
* Gets the current location hash, minus the preceding '#' character.
*
* @method getHash
* @return {String} current location hash
* @static
*/
// 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.
} : function () {
}),
/**
* Gets the current bookmarkable URL.
*
* @method getUrl
* @return {String} current bookmarkable URL
* @static
*/
getUrl: function () {
},
/**
* pairs. If <em>hash</em> is not specified, the current location hash will
* be used.
*
* @method parseHash
* @param {String} hash (optional) location hash string
* @static
*/
i,
params = {};
for (i = 0; i < len; ++i) {
}
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.
*
* @method replaceHash
* @param {String} hash new location hash
* @static
*/
replaceHash: function (hash) {
},
/**
* Sets the browser's location hash to the specified string.
*
* @method setHash
* @param {String} hash new location hash
* @static
*/
}
});
// -- Synthetic hashchange Event -----------------------------------------------
// Synthetic hashchange event to normalize hashchange differences across
// browsers, and to provide hashchange for browsers that don't natively support
// it.
// TODO: how to document this?
// Ignore this subscriber 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.
}
},
// but also not documented. Also, subscriber counts don't seem to be
// updated after detach().
}
}
});
if (nativeHashChange) {
// Wrap the browser's native hashchange event.
// TODO: would there be any benefit to making this an overridable
// protected method?
});
});
}, win);
} else {
// Begin polling for location hash changes if there's not already a global
// poll running.
// cache. This works around a nasty Safari bug when the back button
// is used to return from a page on another domain, but results in
// slightly worse performance. This bug is not present in Chrome.
//
// Current as of Safari 4.0.5 (6531.22.7).
}
});
});
}
}, null, true);
}
}
/**
* The history-hash-ie module improves IE6/7 support in history-hash by using a
* hidden iframe to create entries in IE's browser history. This module is only
* needed if IE6/7 support is necessary; it's not needed for any other browser.
*
* @module history
* @submodule history-hash-ie
*/
};
} else {
}
};
/**
* Updates the history iframe with the specified hash.
*
* @method _updateIframe
* @param {String} hash location hash
* @param {Boolean} replace (optional) if <code>true</code>, the current
* history state will be replaced without adding a new history entry
* @protected
* @static
* @for History
*/
if (replace) {
} else {
}
};
if (!iframe) {
// Create an iframe to store history state.
'<iframe src="javascript:0" style="display:none"/>'
));
// Don't add the iframe to the DOM until the DOM is ready, lest we
// frighten IE.
Y.on('domready', function () {
// The iframe is appended to the documentElement rather than the
// body. Keeping it outside the body prevents scrolling on the
// initial page load (hat tip to Ben Alman and jQuery BBQ for this
// technique).
// Update the iframe with the initial location hash, if any. This
// will create an initial history entry that the user can return to
// after the state has changed.
});
// Listen for hashchange events and keep the parent window's location
// hash in sync with the hash stored in the iframe.
Y.on('hashchange', function (e) {
}
}, win);
}
}
YUI.add('history', function(Y){}, '@VERSION@' ,{use:['history-base', 'history-hash', 'history-hash-ie']});