123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- if (!dojo._hasResource["dojo.back"]) { // _hasResource checks added by build.
- // Do not use _hasResource directly in
- // your code.
- dojo._hasResource["dojo.back"] = true;
- dojo.provide("dojo.back");
- (function() {
- var back = dojo.back;
- // everyone deals with encoding the hash slightly differently
- function getHash() {
- var h = window.location.hash;
- if (h.charAt(0) == "#") {
- h = h.substring(1);
- }
- return dojo.isMozilla ? h : decodeURIComponent(h);
- }
- function setHash(h) {
- if (!h) {
- h = ""
- };
- window.location.hash = encodeURIComponent(h);
- historyCounter = history.length;
- }
- // if we're in the test for these methods, expose them on dojo.back.
- // ok'd with alex.
- if (dojo.exists("tests.back-hash")) {
- back.getHash = getHash;
- back.setHash = setHash;
- }
- var initialHref = (typeof(window) !== "undefined")
- ? window.location.href
- : "";
- var initialHash = (typeof(window) !== "undefined") ? getHash() : "";
- var initialState = null;
- var locationTimer = null;
- var bookmarkAnchor = null;
- var historyIframe = null;
- var forwardStack = [];
- var historyStack = [];
- var moveForward = false;
- var changingUrl = false;
- var historyCounter;
- function handleBackButton() {
- // summary: private method. Do not call this directly.
- // The "current" page is always at the top of the history stack.
- // console.debug("handlingBackButton");
- var current = historyStack.pop();
- if (!current) {
- return;
- }
- var last = historyStack[historyStack.length - 1];
- if (!last && historyStack.length == 0) {
- last = initialState;
- }
- if (last) {
- if (last.kwArgs["back"]) {
- last.kwArgs["back"]();
- } else if (last.kwArgs["backButton"]) {
- last.kwArgs["backButton"]();
- } else if (last.kwArgs["handle"]) {
- last.kwArgs.handle("back");
- }
- }
- forwardStack.push(current);
- // console.debug("done handling back");
- }
- back.goBack = handleBackButton;
- function handleForwardButton() {
- // summary: private method. Do not call this directly.
- // console.debug("handling forward");
- var last = forwardStack.pop();
- if (!last) {
- return;
- }
- if (last.kwArgs["forward"]) {
- last.kwArgs.forward();
- } else if (last.kwArgs["forwardButton"]) {
- last.kwArgs.forwardButton();
- } else if (last.kwArgs["handle"]) {
- last.kwArgs.handle("forward");
- }
- historyStack.push(last);
- // console.debug("done handling forward");
- }
- back.goForward = handleForwardButton;
- function createState(url, args, hash) {
- // summary: private method. Do not call this directly.
- return {
- "url" : url,
- "kwArgs" : args,
- "urlHash" : hash
- }; // Object
- }
- function getUrlQuery(url) {
- // summary: private method. Do not call this directly.
- var segments = url.split("?");
- if (segments.length < 2) {
- return null; // null
- } else {
- return segments[1]; // String
- }
- }
- function loadIframeHistory() {
- // summary: private method. Do not call this directly.
- var url = (djConfig["dojoIframeHistoryUrl"] || dojo.moduleUrl(
- "dojo", "resources/iframe_history.html"))
- + "?" + (allGetServerTime()).getTime();
- moveForward = true;
- if (historyIframe) {
- (dojo.isSafari)
- ? historyIframe.location = url
- : window.frames[historyIframe.name].location = url;
- } else {
- // console.warn("dojo.back: Not initialised. You need to call
- // dojo.back.init() from a <script> block that lives inside the
- // <body> tag.");
- }
- return url; // String
- }
- function checkLocation() {
- // console.debug("checking url");
- if (!changingUrl) {
- var hsl = historyStack.length;
- var hash = getHash();
- if ((hash === initialHash || window.location.href == initialHref)
- && (hsl == 1)) {
- // 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();
- return;
- }
- // 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].urlHash === hash) {
- handleForwardButton();
- return;
- }
- }
- // ok, that didn't work, try someplace back in the history stack
- if ((hsl >= 2) && (historyStack[hsl - 2])) {
- if (historyStack[hsl - 2].urlHash === hash) {
- handleBackButton();
- return;
- }
- }
- if (dojo.isSafari && dojo.isSafari < 3) {
- var hisLen = history.length;
- if (hisLen > historyCounter)
- handleForwardButton();
- else if (hisLen < historyCounter)
- handleBackButton();
- historyCounter = hisLen;
- }
- }
- // console.debug("done checking");
- };
- back.init = function() {
- // summary: Initializes the undo stack. This must be called from a
- // <script>
- // block that lives inside the <body> tag to prevent bugs on IE.
- if (dojo.byId("dj_history")) {
- return;
- } // prevent reinit
- var src = djConfig["dojoIframeHistoryUrl"]
- || dojo.moduleUrl("dojo", "resources/iframe_history.html");
- document
- .write('<iframe style="border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;" name="dj_history" id="dj_history" src="'
- + src + '"></iframe>');
- };
- back.setInitialState = function(/* Object */args) {
- // summary:
- // Sets the state object and back callback for the very first page
- // that is loaded.
- // description:
- // It is recommended that you call this method as part of an event
- // listener that is registered via dojo.addOnLoad().
- // args: Object
- // See the addToHistory() function for the list of valid args
- // properties.
- initialState = createState(initialHref, args, initialHash);
- };
- // FIXME: Make these doc comments not be awful. At least they're not
- // wrong.
- // FIXME: Would like to support arbitrary back/forward jumps. Have to
- // rework iframeLoaded among other things.
- // FIXME: is there a slight race condition in moz using change URL with
- // the timer check and when
- // the hash gets set? I think I have seen a back/forward call in quick
- // succession, but not consistent.
- /*
- * ===== dojo.__backArgs = function(kwArgs){ // back: Function? // A
- * function to be called when this state is reached via the user //
- * clicking the back button. // forward: Function? // Upon return to
- * this state from the "back, forward" combination // of navigation
- * steps, this function will be called. Somewhat // analgous to the
- * semantic of an "onRedo" event handler. // changeUrl: Boolean?|String? //
- * Boolean indicating whether or not to create a unique hash for // this
- * state. If a string is passed instead, it is used as the // hash. }
- * =====
- */
- back.addToHistory = function(/* dojo.__backArgs */args) {
- // summary:
- // adds a state object (args) to the history list.
- // description:
- // To support getting back button notifications, the object
- // argument should implement a function called either "back",
- // "backButton", or "handle". The string "back" will be passed as
- // the first and only argument to this callback.
- //
- // To support getting forward button notifications, the object
- // argument should implement a function called either "forward",
- // "forwardButton", or "handle". The string "forward" will be
- // passed as the first and only argument to this callback.
- //
- // If you want the browser location string to change, define
- // "changeUrl" on the object. If the
- // value of "changeUrl" is true, then a unique number will be
- // appended to the URL as a fragment
- // identifier (http://some.domain.com/path#uniquenumber). If it is
- // any other value that does
- // not evaluate to false, that value will be used as the fragment
- // identifier. For example,
- // if changeUrl: 'page1', then the URL will look like:
- // http://some.domain.com/path#page1
- //
- // example:
- // | dojo.back.addToHistory({
- // | back: function(){ console.debug('back pressed'); },
- // | forward: function(){ console.debug('forward pressed'); },
- // | changeUrl: true
- // | });
- // BROWSER NOTES:
- // Safari 1.2:
- // back button "works" fine, however it's not possible to actually
- // DETECT that you've moved backwards by inspecting window.location.
- // Unless there is some other means of locating.
- // FIXME: perhaps we can poll on history.length?
- // Safari 2.0.3+ (and probably 1.3.2+):
- // works fine, except when changeUrl is used. When changeUrl is
- // used,
- // Safari jumps all the way back to whatever page was shown before
- // the page that uses dojo.undo.browser support.
- // IE 5.5 SP2:
- // back button behavior is macro. It does not move back to the
- // previous hash value, but to the last full page load. This
- // suggests
- // that the iframe is the correct way to capture the back button in
- // these cases.
- // Don't test this page using local disk for MSIE. MSIE will not
- // create
- // a history list for iframe_history.html if served from a file:
- // URL.
- // The XML served back from the XHR tests will also not be properly
- // created if served from local disk. Serve the test pages from a
- // web
- // server to test in that browser.
- // IE 6.0:
- // same behavior as IE 5.5 SP2
- // Firefox 1.0+:
- // the back button will return us to the previous hash on the same
- // page, thereby not requiring an iframe hack, although we do then
- // need to run a timer to detect inter-page movement.
- // If addToHistory is called, then that means we prune the
- // forward stack -- the user went back, then wanted to
- // start a new forward path.
- forwardStack = [];
- var hash = null;
- var url = null;
- if (!historyIframe) {
- if (djConfig["useXDomain"] && !djConfig["dojoIframeHistoryUrl"]) {
- console
- .debug("dojo.back: When using cross-domain Dojo builds,"
- + " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
- + " to the path on your domain to iframe_history.html");
- }
- historyIframe = window.frames["dj_history"];
- }
- if (!bookmarkAnchor) {
- bookmarkAnchor = document.createElement("a");
- dojo.body().appendChild(bookmarkAnchor);
- bookmarkAnchor.style.display = "none";
- }
- if (args["changeUrl"]) {
- hash = ""
- + ((args["changeUrl"] !== true)
- ? args["changeUrl"]
- : (allGetServerTime()).getTime());
- // If the current hash matches the new one, just replace the
- // history object with
- // this new one. It doesn't make sense to track different state
- // objects for the same
- // logical URL. This matches the browser behavior of only
- // putting in one history
- // item no matter how many times you click on the same #hash
- // link, at least in Firefox
- // and Safari, and there is no reliable way in those browsers to
- // know if a #hash link
- // has been clicked on multiple times. So making this the
- // standard behavior in all browsers
- // so that dojo.back's behavior is the same in all browsers.
- if (historyStack.length == 0 && initialState.urlHash == hash) {
- initialState = createState(url, args, hash);
- return;
- } else if (historyStack.length > 0
- && historyStack[historyStack.length - 1].urlHash == hash) {
- historyStack[historyStack.length - 1] = createState(url,
- args, hash);
- return;
- }
- changingUrl = true;
- setTimeout(function() {
- setHash(hash);
- changingUrl = false;
- }, 1);
- bookmarkAnchor.href = hash;
- if (dojo.isIE) {
- url = loadIframeHistory();
- var oldCB = args["back"] || args["backButton"]
- || args["handle"];
- // The function takes handleName as a parameter, in case the
- // callback we are overriding was "handle". In that case,
- // we will need to pass the handle name to handle.
- var tcb = function(handleName) {
- if (getHash() != "") {
- setTimeout(function() {
- setHash(hash);
- }, 1);
- }
- // Use apply to set "this" to args, and to try to avoid
- // memory leaks.
- oldCB.apply(this, [handleName]);
- };
- // Set interceptor function in the right place.
- if (args["back"]) {
- args.back = tcb;
- } else if (args["backButton"]) {
- args.backButton = tcb;
- } else if (args["handle"]) {
- args.handle = tcb;
- }
- var oldFW = args["forward"] || args["forwardButton"]
- || args["handle"];
- // The function takes handleName as a parameter, in case the
- // callback we are overriding was "handle". In that case,
- // we will need to pass the handle name to handle.
- var tfw = function(handleName) {
- if (getHash() != "") {
- setHash(hash);
- }
- if (oldFW) { // we might not actually have one
- // Use apply to set "this" to args, and to try to
- // avoid memory leaks.
- oldFW.apply(this, [handleName]);
- }
- };
- // Set interceptor function in the right place.
- if (args["forward"]) {
- args.forward = tfw;
- } else if (args["forwardButton"]) {
- args.forwardButton = tfw;
- } else if (args["handle"]) {
- args.handle = tfw;
- }
- } else if (!dojo.isIE) {
- // start the timer
- if (!locationTimer) {
- locationTimer = setInterval(checkLocation, 200);
- }
- }
- } else {
- url = loadIframeHistory();
- }
- historyStack.push(createState(url, args, hash));
- };
- back._iframeLoaded = function(evt, ifrLoc) {
- // summary:
- // private method. Do not call this directly.
- var query = getUrlQuery(ifrLoc.href);
- if (query == null) {
- // alert("iframeLoaded");
- // we hit the end of the history, so we should go back
- if (historyStack.length == 1) {
- handleBackButton();
- }
- return;
- }
- if (moveForward) {
- // we were expecting it, so it's not either a forward or
- // backward movement
- moveForward = false;
- return;
- }
- // Check the back stack first, since it is more likely.
- // Note that only one step back or forward is supported.
- if (historyStack.length >= 2
- && query == getUrlQuery(historyStack[historyStack.length
- - 2].url)) {
- handleBackButton();
- } else if (forwardStack.length > 0
- && query == getUrlQuery(forwardStack[forwardStack.length
- - 1].url)) {
- handleForwardButton();
- }
- };
- })();
- }
|