#if COPYRIGHT
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//------------------------------------------------------------------------------
#endif
// History fields ("entry" in the code always designates a string representation of the state):
Sys.Application._appLoadHandler = null;
Sys.Application._beginRequestHandler = null;
Sys.Application._clientId = null;
Sys.Application._currentEntry = '';
Sys.Application._endRequestHandler = null;
Sys.Application._history = null;
Sys.Application._enableHistory = false;
#if DEBUG
Sys.Application._historyEnabledInScriptManager = false;
#endif
Sys.Application._historyFrame = null;
Sys.Application._historyInitialized = false;
Sys.Application._historyPointIsNew = false;
Sys.Application._ignoreTimer = false;
Sys.Application._initialState = null;
Sys.Application._state = {};
Sys.Application._timerCookie = 0;
Sys.Application._timerHandler = null;
Sys.Application._uniqueId = null;
// Add methods to support history
Sys._Application.prototype.get_stateString = function() {
///
var hash = null;
// Need to do an additional check because of a bug in Firefox
// (https://bugzilla.mozilla.org/show_bug.cgi?id=378962).
// Firefox incorrectly calls decodeURIComponent on window.hash
// before returning it.
if (Sys.Browser.agent === Sys.Browser.Firefox) {
var href = window.location.href;
var hashIndex = href.indexOf('#');
if (hashIndex !== -1) {
hash = href.substring(hashIndex + 1);
}
else {
hash = "";
}
return hash;
}
else {
hash = window.location.hash;
}
if ((hash.length > 0) && (hash.charAt(0) === '#')) {
hash = hash.substring(1);
}
return hash;
};
Sys._Application.prototype.get_enableHistory = function() {
///
return this._enableHistory;
};
Sys._Application.prototype.set_enableHistory = function(value) {
#if DEBUG
if (this._initialized && !this._initializing) {
throw Error.invalidOperation(Sys.Res.historyCannotEnableHistory);
}
else if (this._historyEnabledInScriptManager && !value) {
throw Error.invalidOperation(Sys.Res.invalidHistorySettingCombination);
}
#endif
this._enableHistory = value;
};
Sys._Application.prototype.add_navigate = function(handler) {
///
///
this.get_events().addHandler("navigate", handler);
};
Sys._Application.prototype.remove_navigate = function(handler) {
///
this.get_events().removeHandler("navigate", handler);
};
Sys._Application.prototype.addHistoryPoint = function(state, title) {
///
///
/// A dictionary of state bits that will be added to the main state
/// to form the global state of the new history point.
/// The state must be a string dictionary. The application is responsible
/// for converting the state bits from and into the relevant types.
///
/// The title for the new history point.
#if DEBUG
if (!this._enableHistory) throw Error.invalidOperation(Sys.Res.historyCannotAddHistoryPointWithHistoryDisabled);
for (var n in state) {
var v = state[n];
var t = typeof(v);
if ((v !== null) && ((t === 'object') || (t === 'function') || (t === 'undefined'))) {
throw Error.argument('state', Sys.Res.stateMustBeStringDictionary);
}
}
#endif
this._ensureHistory();
var initialState = this._state;
for (var key in state) {
var value = state[key];
if (value === null) {
if (typeof(initialState[key]) !== 'undefined') {
delete initialState[key];
}
}
else {
initialState[key] = value;
}
}
var entry = this._serializeState(initialState);
this._historyPointIsNew = true;
this._setState(entry, title);
// Raising navigate even when creating a history point so that independant
// observers such as PermaLink can get all state changes.
this._raiseNavigate();
};
Sys._Application.prototype.setServerId = function(clientId, uniqueId) {
///
///
///
this._clientId = clientId;
this._uniqueId = uniqueId;
};
Sys._Application.prototype.setServerState = function(value) {
///
///
this._ensureHistory();
this._state.__s = value;
this._updateHiddenField(value);
};
Sys._Application.prototype._deserializeState = function(entry) {
// Deserializes a querystring to a string dictionary.
// The serialized dictionary.
// The deserialized string dictionary.
var result = {};
entry = entry || '';
// Extract the server part (everything after &&)
var serverSeparator = entry.indexOf('&&');
if ((serverSeparator !== -1) && (serverSeparator + 2 < entry.length)) {
result.__s = entry.substr(serverSeparator + 2);
entry = entry.substr(0, serverSeparator);
}
// Deserialize the client part
var tokens = entry.split('&');
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
var equal = token.indexOf('=');
if ((equal !== -1) && (equal + 1 < token.length)) {
var name = token.substr(0, equal);
var value = token.substr(equal + 1);
result[name] = decodeURIComponent(value);
}
}
return result;
};
Sys._Application.prototype._enableHistoryInScriptManager = function() {
this._enableHistory = true;
#if DEBUG
this._historyEnabledInScriptManager = true;
#endif
};
Sys._Application.prototype._ensureHistory = function() {
if (!this._historyInitialized && this._enableHistory) {
if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) &&
((!document.documentMode) || document.documentMode < 8)) {
this._historyFrame = document.getElementById('__historyFrame');
##DEBUG if (!this._historyFrame) throw Error.invalidOperation(Sys.Res.historyMissingFrame);
this._ignoreIFrame = true;
}
this._timerHandler = Function.createDelegate(this, this._onIdle);
this._timerCookie = window.setTimeout(this._timerHandler, 100);
try {
this._initialState = this._deserializeState(this.get_stateString());
} catch(e) {}
this._historyInitialized = true;
}
};
Sys._Application.prototype._navigate = function(entry) {
this._ensureHistory();
var state = this._deserializeState(entry);
if (this._uniqueId) {
var oldServerEntry = this._state.__s || '';
var newServerEntry = state.__s || '';
if (newServerEntry !== oldServerEntry) {
this._updateHiddenField(newServerEntry);
__doPostBack(this._uniqueId, newServerEntry);
this._state = state;
return;
}
}
this._setState(entry);
this._state = state;
this._raiseNavigate();
};
Sys._Application.prototype._onIdle = function() {
delete this._timerCookie;
var entry = this.get_stateString();
if (entry !== this._currentEntry) {
if (!this._ignoreTimer) {
this._historyPointIsNew = false;
this._navigate(entry);
}
}
else {
this._ignoreTimer = false;
}
this._timerCookie = window.setTimeout(this._timerHandler, 100);
};
// devdiv 617545: Ajax history iframe rendered in IE8+ after .NET 4.5 installed
// the fix of DevDiv 13920 enables iframe rendering on server side for IE8+
// as the latter could be in a lower document mode. On client side,
// in case documentMode >= 8, make sure that the _onIframeLoad handler does nothing
// Note document.documentMode for IE7 is undefined
Sys._Application.prototype._onIFrameLoad = function(entry) {
if ((!document.documentMode) || document.documentMode < 8 ) {
this._ensureHistory();
if (!this._ignoreIFrame) {
this._historyPointIsNew = false;
this._navigate(entry);
}
this._ignoreIFrame = false;
}
};
Sys._Application.prototype._onPageRequestManagerBeginRequest = function(sender, args) {
this._ignoreTimer = true;
this._originalTitle = document.title;
};
Sys._Application.prototype._onPageRequestManagerEndRequest = function(sender, args) {
var dataItem = args.get_dataItems()[this._clientId];
var originalTitle = this._originalTitle;
this._originalTitle = null;
// Reset event target if set by history
var eventTarget = document.getElementById("__EVENTTARGET");
if (eventTarget && eventTarget.value === this._uniqueId) {
eventTarget.value = '';
}
if (typeof(dataItem) !== 'undefined') {
this.setServerState(dataItem);
this._historyPointIsNew = true;
}
else {
this._ignoreTimer = false;
}
var entry = this._serializeState(this._state);
if (entry !== this._currentEntry) {
this._ignoreTimer = true;
if (typeof(originalTitle) === "string") {
// At this point the PRM has already updated the document title.
// We want the title of the target entry will be the new page title
// but since the title has been set, it has been applied to the current
// entry. Temporarily restoring the old title while setting the hash.
// However, this has the opposite effect in IE7.
if (Sys.Browser.agent !== Sys.Browser.InternetExplorer || Sys.Browser.version > 7) {
var newTitle = document.title;
document.title = originalTitle;
this._setState(entry);
document.title = newTitle;
}
else {
this._setState(entry);
}
this._raiseNavigate();
}
else {
this._setState(entry);
this._raiseNavigate();
}
}
};
Sys._Application.prototype._raiseNavigate = function() {
var isNew = this._historyPointIsNew;
var h = this.get_events().getHandler("navigate");
var stateClone = {};
for (var key in this._state) {
if (key !== '__s') {
stateClone[key] = this._state[key];
}
}
var args = new Sys.HistoryEventArgs(stateClone);
if (h) {
h(this, args);
}
// Dev10 475308
// History: Firefox 3.0: Entries on History dropdown button are shifted after navigation
// The entry in the history dropdown ends up with the title of the page you came from rather than
// the title of the page you navigated to. This is just how it works. In order for the title
// of the entry in the dropdown to be correct, the title must be set before the hash is changed,
// which obviously is not possible since we only set the title in response to the hash changing.
// So, we re-do another hash change, to force FF to re-think the title of the entry. Since the value
// is the same, it doesn't actually cause a navigation.
// The only problem is that doing this causes a page refresh if there isn't a hash at all.
// There is no other known workaround for this problem in that scenario, so this bug remains
// when you navigate from a certain state to one with no hash. Setting a blank hash like "#"
// makes things worse as it is considered a new state and resets the history.
// See Mozilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=442060
// Dev10 622219: don't use the go() workaround if the document is an iframe and the top window
// does not have a hash, because that will cause the page to refresh.
// If the top document is in a different domain we can't detect that due to the same origin policy,
// so the try/catch prevents an error. In that scenario it is the best we can do.
if (!isNew) {
// !isNew means we raised the navigate event because the user navigated with the browser,
// not because a history point was added. In this scenario, the history point is navigated
// to first, then the title is changed in reaction. In FF that causes the history point navigated
// to to get the title of the page we were on before. Applying this hack fixes it. Unfortunately
// the hack required varies in 3.0 and 3.5.
var err;
try {
if ((Sys.Browser.agent === Sys.Browser.Firefox) && window.location.hash &&
(!window.frameElement || window.top.location.hash)) {
(Sys.Browser.version < 3.5) ?
window.history.go(0) :
location.hash = this.get_stateString();
}
}
catch(err) {
}
}
};
Sys._Application.prototype._serializeState = function(state) {
// Serializes a string dictionary to a querystring form.
// The dictionary to serialize.
// The serialized dictionary.
var serialized = [];
for (var key in state) {
var value = state[key];
if (key === '__s') {
var serverState = value;
}
else {
##DEBUG if (key.indexOf('=') !== -1) throw Error.argument('state', Sys.Res.stateFieldNameInvalid);
serialized[serialized.length] = key + '=' + encodeURIComponent(value);
}
}
return serialized.join('&') + (serverState ? '&&' + serverState : '');
};
Sys._Application.prototype._setState = function(entry, title) {
if (this._enableHistory) {
entry = entry || '';
if (entry !== this._currentEntry) {
// Replace the hash on the current ASP.NET form if it exists
if (window.theForm) {
var action = window.theForm.action;
var hashIndex = action.indexOf('#');
window.theForm.action = ((hashIndex !== -1) ? action.substring(0, hashIndex) : action) + '#' + entry;
}
if (this._historyFrame && this._historyPointIsNew) {
// need to HTML-encode the document title
var newDiv = document.createElement("div");
newDiv.appendChild(document.createTextNode(title || document.title));
var htmlEncodedTitle = newDiv.innerHTML;
this._ignoreIFrame = true;
var frameDoc = this._historyFrame.contentWindow.document;
frameDoc.open("javascript:''");
frameDoc.write("" + htmlEncodedTitle +
"parent.Sys.Application._onIFrameLoad(" +
Sys.Serialization.JavaScriptSerializer.serialize(entry) +
");");
frameDoc.close();
}
this._ignoreTimer = false;
this._currentEntry = entry;
if (this._historyFrame || this._historyPointIsNew) {
// Dev 10 Bug: 594690
// dont set the hash unless this is a new history point, since that's pointless, and it
// causes an occassional loss of forward state in IE8.
// unless the iframe is in use, we need to set it to keep the hash in sync with it.
var currentHash = this.get_stateString();
if (entry !== currentHash) {
// Check length before setting it
#if DEBUG
var loc = document.location;
if (loc.href.length - loc.hash.length + entry.length > 2048) {
throw Error.invalidOperation(String.format(Sys.Res.urlTooLong, 2048));
}
#endif
window.location.hash = entry;
// reset currentEntry to the statestring in case the browser has
// encoded any characters in it
this._currentEntry = this.get_stateString();
if ((typeof(title) !== 'undefined') && (title !== null)) {
document.title = title;
}
}
}
this._historyPointIsNew = false;
}
}
};
Sys._Application.prototype._updateHiddenField = function(value) {
if (this._clientId) {
var serverStateField = document.getElementById(this._clientId);
if (serverStateField) {
serverStateField.value = value;
}
}
};