/* | |
* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
BrowserHistoryUtils = { | |
addEvent: function(elm, evType, fn, useCapture) { | |
useCapture = useCapture || false; | |
if (elm.addEventListener) { | |
elm.addEventListener(evType, fn, useCapture); | |
return true; | |
} else if (elm.attachEvent) { | |
var r = elm.attachEvent('on' + evType, fn); | |
return r; | |
} else { | |
elm['on' + evType] = fn; | |
} | |
} | |
} | |
BrowserHistory = (function() { | |
// type of browser | |
var browser = { | |
ie: false, | |
ie8: false, | |
firefox: false, | |
safari: false, | |
opera: false, | |
version: -1 | |
}; | |
// Default app state URL to use when no fragment ID present | |
var defaultHash = ''; | |
// Last-known app state URL | |
var currentHref = document.location.href; | |
// Initial URL (used only by IE) | |
var initialHref = document.location.href; | |
// Initial URL (used only by IE) | |
var initialHash = document.location.hash; | |
// History frame source URL prefix (used only by IE) | |
var historyFrameSourcePrefix = 'history/historyFrame.html?'; | |
// History maintenance (used only by Safari) | |
var currentHistoryLength = -1; | |
// Flag to denote the existence of onhashchange | |
var browserHasHashChange = false; | |
var historyHash = []; | |
var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash); | |
var backStack = []; | |
var forwardStack = []; | |
var currentObjectId = null; | |
//UserAgent detection | |
var useragent = navigator.userAgent.toLowerCase(); | |
if (useragent.indexOf("opera") != -1) { | |
browser.opera = true; | |
} else if (useragent.indexOf("msie") != -1) { | |
browser.ie = true; | |
browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4)); | |
if (browser.version == 8) { | |
browser.ie = false; | |
browser.ie8 = true; | |
} | |
} else if (useragent.indexOf("safari") != -1) { | |
browser.safari = true; | |
browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7)); | |
} else if (useragent.indexOf("gecko") != -1) { | |
browser.firefox = true; | |
} | |
if (browser.ie == true && browser.version == 7) { | |
window["_ie_firstload"] = false; | |
} | |
function hashChangeHandler() { | |
currentHref = document.location.href; | |
var flexAppUrl = getHash(); | |
//ADR: to fix multiple | |
if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { | |
var pl = getPlayers(); | |
for (var i = 0; i < pl.length; i++) { | |
pl[i].browserURLChange(flexAppUrl); | |
} | |
} else { | |
getPlayer().browserURLChange(flexAppUrl); | |
} | |
} | |
// Accessor functions for obtaining specific elements of the page. | |
function getHistoryFrame() { | |
return document.getElementById('ie_historyFrame'); | |
} | |
function getFormElement() { | |
return document.getElementById('safari_formDiv'); | |
} | |
function getRememberElement() { | |
return document.getElementById("safari_remember_field"); | |
} | |
// Get the Flash player object for performing ExternalInterface callbacks. | |
// Updated for changes to SWFObject2. | |
function getPlayer(id) { | |
var i; | |
if (id && document.getElementById(id)) { | |
var r = document.getElementById(id); | |
if (typeof r.SetVariable != "undefined") { | |
return r; | |
} else { | |
var o = r.getElementsByTagName("object"); | |
var e = r.getElementsByTagName("embed"); | |
for (i = 0; i < o.length; i++) { | |
if (typeof o[i].browserURLChange != "undefined") | |
return o[i]; | |
} | |
for (i = 0; i < e.length; i++) { | |
if (typeof e[i].browserURLChange != "undefined") | |
return e[i]; | |
} | |
} | |
} else { | |
var o = document.getElementsByTagName("object"); | |
var e = document.getElementsByTagName("embed"); | |
for (i = 0; i < e.length; i++) { | |
if (typeof e[i].browserURLChange != "undefined") { | |
return e[i]; | |
} | |
} | |
for (i = 0; i < o.length; i++) { | |
if (typeof o[i].browserURLChange != "undefined") { | |
return o[i]; | |
} | |
} | |
} | |
return undefined; | |
} | |
function getPlayers() { | |
var i; | |
var players = []; | |
if (players.length == 0) { | |
var tmp = document.getElementsByTagName('object'); | |
for (i = 0; i < tmp.length; i++) { | |
if (typeof tmp[i].browserURLChange != "undefined") | |
players.push(tmp[i]); | |
} | |
} | |
if (players.length == 0 || players[0].object == null) { | |
var tmp = document.getElementsByTagName('embed'); | |
for (i = 0; i < tmp.length; i++) { | |
if (typeof tmp[i].browserURLChange != "undefined") | |
players.push(tmp[i]); | |
} | |
} | |
return players; | |
} | |
function getIframeHash() { | |
var doc = getHistoryFrame().contentWindow.document; | |
var hash = String(doc.location.search); | |
if (hash.length == 1 && hash.charAt(0) == "?") { | |
hash = ""; | |
} else if (hash.length >= 2 && hash.charAt(0) == "?") { | |
hash = hash.substring(1); | |
} | |
return hash; | |
} | |
/* Get the current location hash excluding the '#' symbol. */ | |
function getHash() { | |
// It would be nice if we could use document.location.hash here, | |
// but it's faulty sometimes. | |
var idx = document.location.href.indexOf('#'); | |
return (idx >= 0) ? document.location.href.substr(idx + 1) : ''; | |
} | |
/* Get the current location hash excluding the '#' symbol. */ | |
function setHash(hash) { | |
// It would be nice if we could use document.location.hash here, | |
// but it's faulty sometimes. | |
if (hash == '') hash = '#' | |
document.location.hash = hash; | |
} | |
function createState(baseUrl, newUrl, flexAppUrl) { | |
return { | |
'baseUrl': baseUrl, | |
'newUrl': newUrl, | |
'flexAppUrl': flexAppUrl, | |
'title': null | |
}; | |
} | |
/* Add a history entry to the browser. | |
* baseUrl: the portion of the location prior to the '#' | |
* newUrl: the entire new URL, including '#' and following fragment | |
* flexAppUrl: the portion of the location following the '#' only | |
*/ | |
function addHistoryEntry(baseUrl, newUrl, flexAppUrl) { | |
//delete all the history entries | |
forwardStack = []; | |
if (browser.ie) { | |
//Check to see if we are being asked to do a navigate for the first | |
//history entry, and if so ignore, because it's coming from the creation | |
//of the history iframe | |
if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) { | |
currentHref = initialHref; | |
return; | |
} | |
if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) { | |
newUrl = baseUrl + '#' + defaultHash; | |
flexAppUrl = defaultHash; | |
} else { | |
// for IE, tell the history frame to go somewhere without a '#' | |
// in order to get this entry into the browser history. | |
getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl; | |
} | |
setHash(flexAppUrl); | |
} else { | |
//ADR | |
if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) { | |
initialState = createState(baseUrl, newUrl, flexAppUrl); | |
} else if (backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) { | |
backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl); | |
} | |
if (browser.safari && !browserHasHashChange) { | |
// for Safari, submit a form whose action points to the desired URL | |
if (browser.version <= 419.3) { | |
var file = window.location.pathname.toString(); | |
file = file.substring(file.lastIndexOf("/") + 1); | |
getFormElement().innerHTML = '<form name="historyForm" action="' + file + '#' + flexAppUrl + '" method="GET"></form>'; | |
//get the current elements and add them to the form | |
var qs = window.location.search.substring(1); | |
var qs_arr = qs.split("&"); | |
for (var i = 0; i < qs_arr.length; i++) { | |
var tmp = qs_arr[i].split("="); | |
var elem = document.createElement("input"); | |
elem.type = "hidden"; | |
elem.name = tmp[0]; | |
elem.value = tmp[1]; | |
document.forms.historyForm.appendChild(elem); | |
} | |
document.forms.historyForm.submit(); | |
} else { | |
top.location.hash = flexAppUrl; | |
} | |
// We also have to maintain the history by hand for Safari | |
historyHash[history.length] = flexAppUrl; | |
_storeStates(); | |
} else { | |
// Otherwise, just tell the browser to go there | |
setHash(flexAppUrl); | |
} | |
} | |
backStack.push(createState(baseUrl, newUrl, flexAppUrl)); | |
} | |
function _storeStates() { | |
if (browser.safari) { | |
getRememberElement().value = historyHash.join(","); | |
} | |
} | |
function handleBackButton() { | |
//The "current" page is always at the top of the history stack. | |
var current = backStack.pop(); | |
if (!current) { | |
return; | |
} | |
var last = backStack[backStack.length - 1]; | |
if (!last && backStack.length == 0) { | |
last = initialState; | |
} | |
forwardStack.push(current); | |
} | |
function handleForwardButton() { | |
//summary: private method. Do not call this directly. | |
var last = forwardStack.pop(); | |
if (!last) { | |
return; | |
} | |
backStack.push(last); | |
} | |
function handleArbitraryUrl() { | |
//delete all the history entries | |
forwardStack = []; | |
} | |
/* Called periodically to poll to see if we need to detect navigation that has occurred */ | |
function checkForUrlChange() { | |
if (browser.ie) { | |
if (currentHref != document.location.href && currentHref + '#' != document.location.href) { | |
//This occurs when the user has navigated to a specific URL | |
//within the app, and didn't use browser back/forward | |
//IE seems to have a bug where it stops updating the URL it | |
//shows the end-user at this point, but programatically it | |
//appears to be correct. Do a full app reload to get around | |
//this issue. | |
if (browser.version < 7) { | |
currentHref = document.location.href; | |
document.location.reload(); | |
} else { | |
if (getHash() != getIframeHash()) { | |
// this.iframe.src = this.blankURL + hash; | |
var sourceToSet = historyFrameSourcePrefix + getHash(); | |
getHistoryFrame().src = sourceToSet; | |
currentHref = document.location.href; | |
} | |
} | |
} | |
} | |
if (browser.safari && !browserHasHashChange) { | |
// For Safari, we have to check to see if history.length changed. | |
if (currentHistoryLength >= 0 && history.length != currentHistoryLength) { | |
//alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|")); | |
var flexAppUrl = getHash(); | |
if (browser.version < 528.16 /* Anything earlier than Safari 4.0 */ ) { | |
// If it did change and we're running Safari 3.x or earlier, | |
// then we have to look the old state up in our hand-maintained | |
// array since document.location.hash won't have changed, | |
// then call back into BrowserManager. | |
currentHistoryLength = history.length; | |
flexAppUrl = historyHash[currentHistoryLength]; | |
} | |
//ADR: to fix multiple | |
if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { | |
var pl = getPlayers(); | |
for (var i = 0; i < pl.length; i++) { | |
pl[i].browserURLChange(flexAppUrl); | |
} | |
} else { | |
getPlayer().browserURLChange(flexAppUrl); | |
} | |
_storeStates(); | |
} | |
} | |
if (browser.firefox && !browserHasHashChange) { | |
if (currentHref != document.location.href) { | |
var bsl = backStack.length; | |
var urlActions = { | |
back: false, | |
forward: false, | |
set: false | |
} | |
if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) { | |
urlActions.back = true; | |
// FIXME: could this ever be a forward button? | |
// we can't clear it because we still need to check for forwards. Ugg. | |
// clearInterval(this.locationTimer); | |
handleBackButton(); | |
} | |
// first check to see if we could have gone forward. We always halt on | |
// a no-hash item. | |
if (forwardStack.length > 0) { | |
if (forwardStack[forwardStack.length - 1].flexAppUrl == getHash()) { | |
urlActions.forward = true; | |
handleForwardButton(); | |
} | |
} | |
// ok, that didn't work, try someplace back in the history stack | |
if ((bsl >= 2) && (backStack[bsl - 2])) { | |
if (backStack[bsl - 2].flexAppUrl == getHash()) { | |
urlActions.back = true; | |
handleBackButton(); | |
} | |
} | |
if (!urlActions.back && !urlActions.forward) { | |
var foundInStacks = { | |
back: -1, | |
forward: -1 | |
} | |
for (var i = 0; i < backStack.length; i++) { | |
if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { | |
arbitraryUrl = true; | |
foundInStacks.back = i; | |
} | |
} | |
for (var i = 0; i < forwardStack.length; i++) { | |
if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { | |
arbitraryUrl = true; | |
foundInStacks.forward = i; | |
} | |
} | |
handleArbitraryUrl(); | |
} | |
// Firefox changed; do a callback into BrowserManager to tell it. | |
currentHref = document.location.href; | |
var flexAppUrl = getHash(); | |
//ADR: to fix multiple | |
if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { | |
var pl = getPlayers(); | |
for (var i = 0; i < pl.length; i++) { | |
pl[i].browserURLChange(flexAppUrl); | |
} | |
} else { | |
getPlayer().browserURLChange(flexAppUrl); | |
} | |
} | |
} | |
} | |
var _initialize = function() { | |
browserHasHashChange = ("onhashchange" in document.body); | |
if (browser.ie) { | |
var scripts = document.getElementsByTagName('script'); | |
for (var i = 0, s; s = scripts[i]; i++) { | |
if (s.src.indexOf("history.js") > -1) { | |
var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html"); | |
} | |
} | |
historyFrameSourcePrefix = iframe_location + "?"; | |
var src = historyFrameSourcePrefix; | |
var iframe = document.createElement("iframe"); | |
iframe.id = 'ie_historyFrame'; | |
iframe.name = 'ie_historyFrame'; | |
iframe.src = 'javascript:false;'; | |
try { | |
document.body.appendChild(iframe); | |
} catch (e) { | |
setTimeout(function() { | |
document.body.appendChild(iframe); | |
}, 0); | |
} | |
} | |
if (browser.safari && !browserHasHashChange) { | |
var rememberDiv = document.createElement("div"); | |
rememberDiv.id = 'safari_rememberDiv'; | |
document.body.appendChild(rememberDiv); | |
rememberDiv.innerHTML = '<input type="text" id="safari_remember_field" style="width: 500px;">'; | |
var formDiv = document.createElement("div"); | |
formDiv.id = 'safari_formDiv'; | |
document.body.appendChild(formDiv); | |
var reloader_content = document.createElement('div'); | |
reloader_content.id = 'safarireloader'; | |
var scripts = document.getElementsByTagName('script'); | |
for (var i = 0, s; s = scripts[i]; i++) { | |
if (s.src.indexOf("history.js") > -1) { | |
html = (new String(s.src)).replace(".js", ".html"); | |
} | |
} | |
reloader_content.innerHTML = '<iframe id="safarireloader-iframe" src="about:blank" frameborder="no" scrolling="no"></iframe>'; | |
document.body.appendChild(reloader_content); | |
reloader_content.style.position = 'absolute'; | |
reloader_content.style.left = reloader_content.style.top = '-9999px'; | |
iframe = reloader_content.getElementsByTagName('iframe')[0]; | |
if (document.getElementById("safari_remember_field").value != "") { | |
historyHash = document.getElementById("safari_remember_field").value.split(","); | |
} | |
} | |
if (browserHasHashChange) | |
document.body.onhashchange = hashChangeHandler; | |
} | |
return { | |
historyHash: historyHash, | |
backStack: function() { | |
return backStack; | |
}, | |
forwardStack: function() { | |
return forwardStack | |
}, | |
getPlayer: getPlayer, | |
initialize: function(src) { | |
_initialize(src); | |
}, | |
setURL: function(url) { | |
document.location.href = url; | |
}, | |
getURL: function() { | |
return document.location.href; | |
}, | |
getTitle: function() { | |
return document.title; | |
}, | |
setTitle: function(title) { | |
try { | |
backStack[backStack.length - 1].title = title; | |
} catch (e) {} | |
//if on safari, set the title to be the empty string. | |
if (browser.safari) { | |
if (title == "") { | |
try { | |
var tmp = window.location.href.toString(); | |
title = tmp.substring((tmp.lastIndexOf("/") + 1), tmp.lastIndexOf("#")); | |
} catch (e) { | |
title = ""; | |
} | |
} | |
} | |
document.title = title; | |
}, | |
setDefaultURL: function(def) { | |
defaultHash = def; | |
def = getHash(); | |
//trailing ? is important else an extra frame gets added to the history | |
//when navigating back to the first page. Alternatively could check | |
//in history frame navigation to compare # and ?. | |
if (browser.ie) { | |
window['_ie_firstload'] = true; | |
var sourceToSet = historyFrameSourcePrefix + def; | |
var func = function() { | |
getHistoryFrame().src = sourceToSet; | |
window.location.replace("#" + def); | |
setInterval(checkForUrlChange, 50); | |
} | |
try { | |
func(); | |
} catch (e) { | |
window.setTimeout(function() { | |
func(); | |
}, 0); | |
} | |
} | |
if (browser.safari) { | |
currentHistoryLength = history.length; | |
if (historyHash.length == 0) { | |
historyHash[currentHistoryLength] = def; | |
var newloc = "#" + def; | |
window.location.replace(newloc); | |
} else { | |
//alert(historyHash[historyHash.length-1]); | |
} | |
setInterval(checkForUrlChange, 50); | |
} | |
if (browser.firefox || browser.opera) { | |
var reg = new RegExp("#" + def + "$"); | |
if (window.location.toString().match(reg)) {} else { | |
var newloc = "#" + def; | |
window.location.replace(newloc); | |
} | |
setInterval(checkForUrlChange, 50); | |
} | |
}, | |
/* Set the current browser URL; called from inside BrowserManager to propagate | |
* the application state out to the container. | |
*/ | |
setBrowserURL: function(flexAppUrl, objectId) { | |
if (browser.ie && typeof objectId != "undefined") { | |
currentObjectId = objectId; | |
} | |
//fromIframe = fromIframe || false; | |
//fromFlex = fromFlex || false; | |
//alert("setBrowserURL: " + flexAppUrl); | |
//flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ; | |
var pos = document.location.href.indexOf('#'); | |
var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href; | |
var newUrl = baseUrl + '#' + flexAppUrl; | |
if (document.location.href != newUrl && document.location.href + '#' != newUrl) { | |
currentHref = newUrl; | |
addHistoryEntry(baseUrl, newUrl, flexAppUrl); | |
currentHistoryLength = history.length; | |
} | |
}, | |
browserURLChange: function(flexAppUrl) { | |
var objectId = null; | |
if (browser.ie && currentObjectId != null) { | |
objectId = currentObjectId; | |
} | |
if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { | |
var pl = getPlayers(); | |
for (var i = 0; i < pl.length; i++) { | |
try { | |
pl[i].browserURLChange(flexAppUrl); | |
} catch (e) {} | |
} | |
} else { | |
try { | |
getPlayer(objectId).browserURLChange(flexAppUrl); | |
} catch (e) {} | |
} | |
currentObjectId = null; | |
}, | |
getUserAgent: function() { | |
return navigator.userAgent; | |
}, | |
getPlatform: function() { | |
return navigator.platform; | |
} | |
} | |
})(); | |
// Initialization | |
// Automated unit testing and other diagnostics | |
function setURL(url) { | |
document.location.href = url; | |
} | |
function backButton() { | |
history.back(); | |
} | |
function forwardButton() { | |
history.forward(); | |
} | |
function goForwardOrBackInHistory(step) { | |
history.go(step); | |
} | |
//BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); }); | |
(function(i) { | |
var u = navigator.userAgent; | |
var e = /*@cc_on!@*/ false; | |
var st = setTimeout; | |
if (/webkit/i.test(u)) { | |
st(function() { | |
var dr = document.readyState; | |
if (dr == "loaded" || dr == "complete") { | |
i() | |
} else { | |
st(arguments.callee, 10); | |
} | |
}, 10); | |
} else if ((/mozilla/i.test(u) && !/(compati)/.test(u)) || (/opera/i.test(u))) { | |
document.addEventListener("DOMContentLoaded", i, false); | |
} else if (e) { | |
(function() { | |
var t = document.createElement('doc:rdy'); | |
try { | |
t.doScroll('left'); | |
i(); | |
t = null; | |
} catch (e) { | |
st(arguments.callee, 0); | |
} | |
})(); | |
} else { | |
window.onload = i; | |
} | |
})(function() { | |
BrowserHistory.initialize(); | |
}); |