/**
* @class Ext.util.History
* History management component that allows you to register arbitrary tokens that signify application
* history state on navigation actions. You can then handle the history {@link #change} event in order
* to reset your application UI to the appropriate state when the user navigates forward or backward through
* the browser history stack.
* @singleton
*/
Ext.define('Ext.util.History', {
singleton: true,
alternateClassName: 'Ext.History',
mixins: {
observable: 'Ext.util.Observable'
},
constructor: function() {
var me = this;
me.oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict && Ext.isIE8;
me.iframe = null;
me.hiddenField = null;
me.ready = false;
me.currentToken = null;
},
getHash: function() {
var href = window.location.href,
i = href.indexOf("#");
return i >= 0 ? href.substr(i + 1) : null;
},
doSave: function() {
this.hiddenField.value = this.currentToken;
},
handleStateChange: function(token) {
this.currentToken = token;
this.fireEvent('change', token);
},
updateIFrame: function(token) {
var html = '' +
Ext.util.Format.htmlEncode(token) +
'
';
try {
var doc = this.iframe.contentWindow.document;
doc.open();
doc.write(html);
doc.close();
return true;
} catch (e) {
return false;
}
},
checkIFrame: function () {
var me = this,
contentWindow = me.iframe.contentWindow;
if (!contentWindow || !contentWindow.document) {
Ext.Function.defer(this.checkIFrame, 10, this);
return;
}
var doc = contentWindow.document,
elem = doc.getElementById("state"),
oldToken = elem ? elem.innerText : null,
oldHash = me.getHash();
Ext.TaskMgr.start({
run: function () {
var doc = contentWindow.document,
elem = doc.getElementById("state"),
newToken = elem ? elem.innerText : null,
newHash = me.getHash();
if (newToken !== oldToken) {
oldToken = newToken;
me.handleStateChange(newToken);
window.top.location.hash = newToken;
oldHash = newToken;
me.doSave();
} else if (newHash !== oldHash) {
oldHash = newHash;
me.updateIFrame(newHash);
}
},
interval: 50,
scope: me
});
me.ready = true;
me.fireEvent('ready', me);
},
startUp: function () {
var me = this;
me.currentToken = me.hiddenField.value || this.getHash();
if (me.oldIEMode) {
me.checkIFrame();
} else {
var hash = me.getHash();
Ext.TaskMgr.start({
run: function () {
var newHash = me.getHash();
if (newHash !== hash) {
hash = newHash;
me.handleStateChange(hash);
me.doSave();
}
},
interval: 50,
scope: me
});
me.ready = true;
me.fireEvent('ready', me);
}
},
/**
* The id of the hidden field required for storing the current history token.
* @type String
* @property
*/
fieldId: Ext.baseCSSPrefix + 'history-field',
/**
* The id of the iframe required by IE to manage the history stack.
* @type String
* @property
*/
iframeId: Ext.baseCSSPrefix + 'history-frame',
/**
* Initialize the global History instance.
* @param {Boolean} onReady (optional) A callback function that will be called once the history
* component is fully initialized.
* @param {Object} scope (optional) The scope (this
reference) in which the callback is executed. Defaults to the browser window.
*/
init: function (onReady, scope) {
var me = this;
if (me.ready) {
Ext.callback(onReady, scope, [me]);
return;
}
if (!Ext.isReady) {
Ext.onReady(function() {
me.init(onReady, scope);
});
return;
}
me.hiddenField = Ext.getDom(me.fieldId);
if (me.oldIEMode) {
me.iframe = Ext.getDom(me.iframeId);
}
me.addEvents(
/**
* @event ready
* Fires when the Ext.History singleton has been initialized and is ready for use.
* @param {Ext.History} The Ext.History singleton.
*/
'ready',
/**
* @event change
* Fires when navigation back or forwards within the local page's history occurs.
* @param {String} token An identifier associated with the page state at that point in its history.
*/
'change'
);
if (onReady) {
me.on('ready', onReady, scope, {single: true});
}
me.startUp();
},
/**
* Add a new token to the history stack. This can be any arbitrary value, although it would
* commonly be the concatenation of a component id and another id marking the specifc history
* state of that component. Example usage:
*
// Handle tab changes on a TabPanel
tabPanel.on('tabchange', function(tabPanel, tab){
Ext.History.add(tabPanel.id + ':' + tab.id);
});
* @param {String} token The value that defines a particular application-specific history state
* @param {Boolean} preventDuplicates When true, if the passed token matches the current token
* it will not save a new history step. Set to false if the same state can be saved more than once
* at the same history stack location (defaults to true).
*/
add: function (token, preventDup) {
var me = this;
if (preventDup !== false) {
if (me.getToken() === token) {
return true;
}
}
if (me.oldIEMode) {
return me.updateIFrame(token);
} else {
window.top.location.hash = token;
return true;
}
},
/**
* Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
*/
back: function() {
window.history.go(-1);
},
/**
* Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
*/
forward: function(){
window.history.go(1);
},
/**
* Retrieves the currently-active history token.
* @return {String} The token
*/
getToken: function() {
return this.ready ? this.currentToken : this.getHash();
}
});