diff options
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/history/history.js')
-rw-r--r-- | contexts/data/lib/closure-library/closure/goog/history/history.js | 999 |
1 files changed, 0 insertions, 999 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/history/history.js b/contexts/data/lib/closure-library/closure/goog/history/history.js deleted file mode 100644 index 6b4d650..0000000 --- a/contexts/data/lib/closure-library/closure/goog/history/history.js +++ /dev/null @@ -1,999 +0,0 @@ -// Copyright 2007 The Closure Library Authors. All Rights Reserved. -// -// Licensed 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. - -/** - * @fileoverview Browser history stack management class. - * - * The goog.History object allows a page to create history state without leaving - * the current document. This allows users to, for example, hit the browser's - * back button without leaving the current page. - * - * The history object can be instantiated in one of two modes. In user visible - * mode, the current history state is shown in the browser address bar as a - * document location fragment (the portion of the URL after the '#'). These - * addresses can be bookmarked, copied and pasted into another browser, and - * modified directly by the user like any other URL. - * - * If the history object is created in invisible mode, the user can still - * affect the state using the browser forward and back buttons, but the current - * state is not displayed in the browser address bar. These states are not - * bookmarkable or editable. - * - * It is possible to use both types of history object on the same page, but not - * currently recommended due to browser deficiencies. - * - * Tested to work in: - * <ul> - * <li>Firefox 1.0-4.0 - * <li>Internet Explorer 5.5-9.0 - * <li>Opera 9+ - * <li>Safari 4+ - * </ul> - * - * @author brenneman@google.com (Shawn Brenneman) - * @see ../demos/history1.html - * @see ../demos/history2.html - */ - -/* Some browser specific implementation notes: - * - * Firefox (through version 2.0.0.1): - * - * Ideally, navigating inside the hidden iframe could be done using - * about:blank#state instead of a real page on the server. Setting the hash on - * about:blank creates history entries, but the hash is not recorded and is lost - * when the user hits the back button. This is true in Opera as well. A blank - * HTML page must be provided for invisible states to be recorded in the iframe - * hash. - * - * After leaving the page with the History object and returning to it (by - * hitting the back button from another site), the last state of the iframe is - * overwritten. The most recent state is saved in a hidden input field so the - * previous state can be restored. - * - * Firefox does not store the previous value of dynamically generated input - * elements. To save the state, the hidden element must be in the HTML document, - * either in the original source or added with document.write. If a reference - * to the input element is not provided as a constructor argument, then the - * history object creates one using document.write, in which case the history - * object must be created from a script in the body element of the page. - * - * Manually editing the address field to a different hash link prevents further - * updates to the address bar. The page continues to work as normal, but the - * address shown will be incorrect until the page is reloaded. - * - * NOTE(user): It should be noted that Firefox will URL encode any non-regular - * ascii character, along with |space|, ", <, and >, when added to the fragment. - * If you expect these characters in your tokens you should consider that - * setToken('<b>') would result in the history fragment "%3Cb%3E", and - * "espére" would show "esp%E8re". (IE allows unicode characters in the - * fragment) - * - * TODO(user): Should we encapsulate this escaping into the API for visible - * history and encode all characters that aren't supported by Firefox? It also - * needs to be optional so apps can elect to handle the escaping themselves. - * - * - * Internet Explorer (through version 7.0): - * - * IE does not modify the history stack when the document fragment is changed. - * We create history entries instead by using document.open and document.write - * into a hidden iframe. - * - * IE destroys the history stack when navigating from /foo.html#someFragment to - * /foo.html. The workaround is to always append the # to the URL. This is - * somewhat unfortunate when loading the page without any # specified, because - * a second "click" sound will play on load as the fragment is automatically - * appended. If the hash is always present, this can be avoided. - * - * Manually editing the hash in the address bar in IE6 and then hitting the back - * button can replace the page with a blank page. This is a Bad User Experience, - * but probably not preventable. - * - * IE also has a bug when the page is loaded via a server redirect, setting - * a new hash value on the window location will force a page reload. This will - * happen the first time setToken is called with a new token. The only known - * workaround is to force a client reload early, for example by setting - * window.location.hash = window.location.hash, which will otherwise be a no-op. - * - * Internet Explorer 8.0, Webkit 532.1 and Gecko 1.9.2: - * - * IE8 has introduced the support to the HTML5 onhashchange event, which means - * we don't have to do any polling to detect fragment changes. Chrome and - * Firefox have added it on their newer builds, wekbit 532.1 and gecko 1.9.2. - * http://www.w3.org/TR/html5/history.html - * NOTE(goto): it is important to note that the document needs to have the - * <!DOCTYPE html> tag to enable the IE8 HTML5 mode. If the tag is not present, - * IE8 will enter IE7 compatibility mode (which can also be enabled manually). - * - * Opera (through version 9.02): - * - * Navigating through pages at a rate faster than some threshhold causes Opera - * to cancel all outstanding timeouts and intervals, including the location - * polling loop. Since this condition cannot be detected, common input events - * are captured to cause the loop to restart. - * - * location.replace is adding a history entry inside setHash_, despite - * documentation that suggests it should not. - * - * - * Safari (through version 2.0.4): - * - * After hitting the back button, the location.hash property is no longer - * readable from JavaScript. This is fixed in later WebKit builds, but not in - * currently shipping Safari. For now, the only recourse is to disable history - * states in Safari. Pages are still navigable via the History object, but the - * back button cannot restore previous states. - * - * Safari sets history states on navigation to a hashlink, but doesn't allow - * polling of the hash, so following actual anchor links in the page will create - * useless history entries. Using location.replace does not seem to prevent - * this. Not a terribly good user experience, but fixed in later Webkits. - * - * - * WebKit (nightly version 420+): - * - * This almost works. Returning to a page with an invisible history object does - * not restore the old state, however, and there is no pageshow event that fires - * in this browser. Holding off on finding a solution for now. - * - * - * HTML5 capable browsers (Firefox 4, Chrome, Safari 5) - * - * No known issues. The goog.history.Html5History class provides a simpler - * implementation more suitable for recent browsers. These implementations - * should be merged so the history class automatically invokes the correct - * implementation. - */ - - -goog.provide('goog.History'); -goog.provide('goog.History.Event'); -goog.provide('goog.History.EventType'); - -goog.require('goog.Timer'); -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.events.BrowserEvent'); -goog.require('goog.events.Event'); -goog.require('goog.events.EventHandler'); -goog.require('goog.events.EventTarget'); -goog.require('goog.events.EventType'); -goog.require('goog.history.Event'); -goog.require('goog.history.EventType'); -goog.require('goog.string'); -goog.require('goog.userAgent'); - - - -/** - * A history management object. Can be instantiated in user-visible mode (uses - * the address fragment to manage state) or in hidden mode. This object should - * be created from a script in the document body before the document has - * finished loading. - * - * To store the hidden states in browsers other than IE, a hidden iframe is - * used. It must point to a valid html page on the same domain (which can and - * probably should be blank.) - * - * Sample instantiation and usage: - * - * <pre> - * // Instantiate history to use the address bar for state. - * var h = new goog.History(); - * goog.events.listen(h, goog.history.EventType.NAVIGATE, navCallback); - * h.setEnabled(true); - * - * // Any changes to the location hash will call the following function. - * function navCallback(e) { - * alert('Navigated to state "' + e.token + '"'); - * } - * - * // The history token can also be set from code directly. - * h.setToken('foo'); - * </pre> - * - * @param {boolean=} opt_invisible True to use hidden history states instead of - * the user-visible location hash. - * @param {string=} opt_blankPageUrl A URL to a blank page on the same server. - * Required if opt_invisible is true. This URL is also used as the src - * for the iframe used to track history state in IE (if not specified the - * iframe is not given a src attribute). Access is Denied error may - * occur in IE7 if the window's URL's scheme is https, and this URL is - * not specified. - * @param {HTMLInputElement=} opt_input The hidden input element to be used to - * store the history token. If not provided, a hidden input element will - * be created using document.write. - * @param {HTMLIFrameElement=} opt_iframe The hidden iframe that will be used by - * IE for pushing history state changes, or by all browsers if opt_invisible - * is true. If not provided, a hidden iframe element will be created using - * document.write. - * @constructor - * @extends {goog.events.EventTarget} - */ -goog.History = function(opt_invisible, opt_blankPageUrl, opt_input, - opt_iframe) { - goog.events.EventTarget.call(this); - - if (opt_invisible && !opt_blankPageUrl) { - throw Error('Can\'t use invisible history without providing a blank page.'); - } - - var input; - if (opt_input) { - input = opt_input; - } else { - var inputId = 'history_state' + goog.History.historyCount_; - document.write(goog.string.subs(goog.History.INPUT_TEMPLATE_, - inputId, inputId)); - input = goog.dom.getElement(inputId); - } - - /** - * An input element that stores the current iframe state. Used to restore - * the state when returning to the page on non-IE browsers. - * @type {HTMLInputElement} - * @private - */ - this.hiddenInput_ = (/** @type {HTMLInputElement} */ input); - - /** - * The window whose location contains the history token fragment. This is - * the window that contains the hidden input. It's typically the top window. - * It is not necessarily the same window that the js code is loaded in. - * @type {Window} - * @private - */ - this.window_ = opt_input ? - goog.dom.getWindow(goog.dom.getOwnerDocument(opt_input)) : window; - - /** - * The initial page location with an empty hash component. If the page uses - * a BASE element, setting location.hash directly will navigate away from the - * current document. To prevent this, the full path is always specified. - * @type {string} - * @private - */ - this.baseUrl_ = this.window_.location.href.split('#')[0]; - - /** - * The base URL for the hidden iframe. Must refer to a document in the - * same domain as the main page. - * @type {string|undefined} - * @private - */ - this.iframeSrc_ = opt_blankPageUrl; - - if (goog.userAgent.IE && !opt_blankPageUrl) { - this.iframeSrc_ = window.location.protocol == 'https' ? 'https:///' : - 'javascript:""'; - } - - /** - * A timer for polling the current history state for changes. - * @type {goog.Timer} - * @private - */ - this.timer_ = new goog.Timer(goog.History.PollingType.NORMAL); - - /** - * True if the state tokens are displayed in the address bar, false for hidden - * history states. - * @type {boolean} - * @private - */ - this.userVisible_ = !opt_invisible; - - /** - * An object to keep track of the history event listeners. - * @type {goog.events.EventHandler} - * @private - */ - this.eventHandler_ = new goog.events.EventHandler(this); - - if (opt_invisible || goog.History.LEGACY_IE) { - var iframe; - if (opt_iframe) { - iframe = opt_iframe; - } else { - var iframeId = 'history_iframe' + goog.History.historyCount_; - var srcAttribute = this.iframeSrc_ ? - 'src="' + goog.string.htmlEscape(this.iframeSrc_) + '"' : - ''; - document.write(goog.string.subs(goog.History.IFRAME_TEMPLATE_, - iframeId, - srcAttribute)); - iframe = goog.dom.getElement(iframeId); - } - - /** - * Internet Explorer uses a hidden iframe for all history changes. Other - * browsers use the iframe only for pushing invisible states. - * @type {HTMLIFrameElement} - * @private - */ - this.iframe_ = (/** @type {HTMLIFrameElement} */ iframe); - - /** - * Whether the hidden iframe has had a document written to it yet in this - * session. - * @type {boolean} - * @private - */ - this.unsetIframe_ = true; - } - - if (goog.History.LEGACY_IE) { - // IE relies on the hidden input to restore the history state from previous - // sessions, but input values are only restored after window.onload. Set up - // a callback to poll the value after the onload event. - this.eventHandler_.listen(this.window_, - goog.events.EventType.LOAD, - this.onDocumentLoaded); - - /** - * IE-only variable for determining if the document has loaded. - * @type {boolean} - * @protected - */ - this.documentLoaded = false; - - /** - * IE-only variable for storing whether the history object should be enabled - * once the document finishes loading. - * @type {boolean} - * @private - */ - this.shouldEnable_ = false; - } - - // Set the initial history state. - if (this.userVisible_) { - this.setHash_(this.getToken(), true); - } else { - this.setIframeToken_(this.hiddenInput_.value); - } - - goog.History.historyCount_++; -}; -goog.inherits(goog.History, goog.events.EventTarget); - - -/** - * Status of when the object is active and dispatching events. - * @type {boolean} - * @private - */ -goog.History.prototype.enabled_ = false; - - -/** - * Whether the object is performing polling with longer intervals. This can - * occur for instance when setting the location of the iframe when in invisible - * mode and the server that is hosting the blank html page is down. In FF, this - * will cause the location of the iframe to no longer be accessible, with - * permision denied exceptions being thrown on every access of the history - * token. When this occurs, the polling interval is elongated. This causes - * exceptions to be thrown at a lesser rate while allowing for the history - * object to resurrect itself when the html page becomes accessible. - * @type {boolean} - * @private - */ -goog.History.prototype.longerPolling_ = false; - - -/** - * The last token set by the history object, used to poll for changes. - * @type {?string} - * @private - */ -goog.History.prototype.lastToken_ = null; - - -/** - * Whether the browser supports HTML5 history management. - * {@link http://www.w3.org/TR/html5/history.html}. - * @type {boolean} - */ -goog.History.HAS_ONHASHCHANGE = - goog.userAgent.IE && goog.userAgent.isDocumentMode(8) || - goog.userAgent.GECKO && goog.userAgent.isVersion('1.9.2') || - goog.userAgent.WEBKIT && goog.userAgent.isVersion('532.1'); - - -/** - * Whether the current browser is Internet Explorer prior to version 8. Many IE - * specific workarounds developed before version 8 are unnecessary in more - * current versions. - * @type {boolean} - */ -goog.History.LEGACY_IE = goog.userAgent.IE && !goog.userAgent.isDocumentMode(8); - - -/** - * Whether the browser always requires the hash to be present. Internet Explorer - * before version 8 will reload the HTML page if the hash is omitted. - * @type {boolean} - */ -goog.History.HASH_ALWAYS_REQUIRED = goog.History.LEGACY_IE; - - -/** - * If not null, polling in the user invisible mode will be disabled until this - * token is seen. This is used to prevent a race condition where the iframe - * hangs temporarily while the location is changed. - * @type {?string} - * @private - */ -goog.History.prototype.lockedToken_ = null; - - -/** @override */ -goog.History.prototype.disposeInternal = function() { - goog.History.superClass_.disposeInternal.call(this); - this.eventHandler_.dispose(); - this.setEnabled(false); -}; - - -/** - * Starts or stops the History polling loop. When enabled, the History object - * will immediately fire an event for the current location. The caller can set - * up event listeners between the call to the constructor and the call to - * setEnabled. - * - * On IE, actual startup may be delayed until the iframe and hidden input - * element have been loaded and can be polled. This behavior is transparent to - * the caller. - * - * @param {boolean} enable Whether to enable the history polling loop. - */ -goog.History.prototype.setEnabled = function(enable) { - - if (enable == this.enabled_) { - return; - } - - if (goog.History.LEGACY_IE && !this.documentLoaded) { - // Wait until the document has actually loaded before enabling the - // object or any saved state from a previous session will be lost. - this.shouldEnable_ = enable; - return; - } - - if (enable) { - if (goog.userAgent.OPERA) { - // Capture events for common user input so we can restart the timer in - // Opera if it fails. Yes, this is distasteful. See operaDefibrillator_. - this.eventHandler_.listen(this.window_.document, - goog.History.INPUT_EVENTS_, - this.operaDefibrillator_); - } else if (goog.userAgent.GECKO) { - // Firefox will not restore the correct state after navigating away from - // and then back to the page with the history object. This can be fixed - // by restarting the history object on the pageshow event. - this.eventHandler_.listen(this.window_, 'pageshow', this.onShow_); - } - - // TODO(user): make HTML5 and invisible history work by listening to the - // iframe # changes instead of the window. - if (goog.History.HAS_ONHASHCHANGE && this.userVisible_) { - this.eventHandler_.listen( - this.window_, goog.events.EventType.HASHCHANGE, this.onHashChange_); - this.enabled_ = true; - this.dispatchEvent(new goog.history.Event(this.getToken(), false)); - } else if (!goog.userAgent.IE || this.documentLoaded) { - // Start dispatching history events if all necessary loading has - // completed (always true for browsers other than IE.) - this.eventHandler_.listen(this.timer_, goog.Timer.TICK, - goog.bind(this.check_, this, true)); - - this.enabled_ = true; - - // Initialize last token at startup except on IE < 8, where the last token - // must only be set in conjunction with IFRAME updates, or the IFRAME will - // start out of sync and remove any pre-existing URI fragment. - if (!goog.History.LEGACY_IE) { - this.lastToken_ = this.getToken(); - this.dispatchEvent(new goog.history.Event(this.getToken(), false)); - } - - this.timer_.start(); - } - - } else { - this.enabled_ = false; - this.eventHandler_.removeAll(); - this.timer_.stop(); - } -}; - - -/** - * Callback for the window onload event in IE. This is necessary to read the - * value of the hidden input after restoring a history session. The value of - * input elements is not viewable until after window onload for some reason (the - * iframe state is similarly unavailable during the loading phase.) If - * setEnabled is called before the iframe has completed loading, the history - * object will actually be enabled at this point. - * @protected - */ -goog.History.prototype.onDocumentLoaded = function() { - this.documentLoaded = true; - - if (this.hiddenInput_.value) { - // Any saved value in the hidden input can only be read after the document - // has been loaded due to an IE limitation. Restore the previous state if - // it has been set. - this.setIframeToken_(this.hiddenInput_.value, true); - } - - this.setEnabled(this.shouldEnable_); -}; - - -/** - * Handler for the Gecko pageshow event. Restarts the history object so that the - * correct state can be restored in the hash or iframe. - * @param {goog.events.BrowserEvent} e The browser event. - * @private - */ -goog.History.prototype.onShow_ = function(e) { - // NOTE(user): persisted is a property passed in the pageshow event that - // indicates whether the page is being persisted from the cache or is being - // loaded for the first time. - if (e.getBrowserEvent()['persisted']) { - this.setEnabled(false); - this.setEnabled(true); - } -}; - - -/** - * Handles HTML5 onhashchange events on browsers where it is supported. - * This is very similar to {@link #check_}, except that it is not executed - * continuously. It is only used when {@code goog.History.HAS_ONHASHCHANGE} is - * true. - * @param {goog.events.BrowserEvent} e The browser event. - * @private - */ -goog.History.prototype.onHashChange_ = function(e) { - var hash = this.getLocationFragment_(this.window_); - if (hash != this.lastToken_) { - this.update_(hash, true); - } -}; - - -/** - * @return {string} The current token. - */ -goog.History.prototype.getToken = function() { - if (this.lockedToken_ != null) { - return this.lockedToken_; - } else if (this.userVisible_) { - return this.getLocationFragment_(this.window_); - } else { - return this.getIframeToken_() || ''; - } -}; - - -/** - * Sets the history state. When user visible states are used, the URL fragment - * will be set to the provided token. Sometimes it is necessary to set the - * history token before the document title has changed, in this case IE's - * history drop down can be out of sync with the token. To get around this - * problem, the app can pass in a title to use with the hidden iframe. - * @param {string} token The history state identifier. - * @param {string=} opt_title Optional title used when setting the hidden iframe - * title in IE. - */ -goog.History.prototype.setToken = function(token, opt_title) { - this.setHistoryState_(token, false, opt_title); -}; - - -/** - * Replaces the current history state without affecting the rest of the history - * stack. - * @param {string} token The history state identifier. - * @param {string=} opt_title Optional title used when setting the hidden iframe - * title in IE. - */ -goog.History.prototype.replaceToken = function(token, opt_title) { - this.setHistoryState_(token, true, opt_title); -}; - - -/** - * Gets the location fragment for the current URL. We don't use location.hash - * directly as the browser helpfully urlDecodes the string for us which can - * corrupt the tokens. For example, if we want to store: label/%2Froot it would - * be returned as label//root. - * @param {Window} win The window object to use. - * @return {string} The fragment. - * @private - */ -goog.History.prototype.getLocationFragment_ = function(win) { - var href = win.location.href; - var index = href.indexOf('#'); - return index < 0 ? '' : href.substring(index + 1); -}; - - -/** - * Sets the history state. When user visible states are used, the URL fragment - * will be set to the provided token. Setting opt_replace to true will cause the - * navigation to occur, but will replace the current history entry without - * affecting the length of the stack. - * - * @param {string} token The history state identifier. - * @param {boolean} replace Set to replace the current history entry instead of - * appending a new history state. - * @param {string=} opt_title Optional title used when setting the hidden iframe - * title in IE. - * @private - */ -goog.History.prototype.setHistoryState_ = function(token, replace, opt_title) { - if (this.getToken() != token) { - if (this.userVisible_) { - this.setHash_(token, replace); - - if (!goog.History.HAS_ONHASHCHANGE) { - if (goog.userAgent.IE) { - // IE must save state using the iframe. - this.setIframeToken_(token, replace, opt_title); - } - } - - // This condition needs to be called even if - // goog.History.HAS_ONHASHCHANGE is true so the NAVIGATE event fires - // sychronously. - if (this.enabled_) { - this.check_(false); - } - } else { - // Fire the event immediately so that setting history is synchronous, but - // set a suspendToken so that polling doesn't trigger a 'back'. - this.setIframeToken_(token, replace); - this.lockedToken_ = this.lastToken_ = this.hiddenInput_.value = token; - this.dispatchEvent(new goog.history.Event(token, false)); - } - } -}; - - -/** - * Sets or replaces the URL fragment. The token does not need to be URL encoded - * according to the URL specification, though certain characters (like newline) - * are automatically stripped. - * - * If opt_replace is not set, non-IE browsers will append a new entry to the - * history list. Setting the hash does not affect the history stack in IE - * (unless there is a pre-existing named anchor for that hash.) - * - * Older versions of Webkit cannot query the location hash, but it still can be - * set. If we detect one of these versions, always replace instead of creating - * new history entries. - * - * window.location.replace replaces the current state from the history stack. - * http://www.whatwg.org/specs/web-apps/current-work/#dom-location-replace - * http://www.whatwg.org/specs/web-apps/current-work/#replacement-enabled - * - * @param {string} token The new string to set. - * @param {boolean=} opt_replace Set to true to replace the current token - * without appending a history entry. - * @private - */ -goog.History.prototype.setHash_ = function(token, opt_replace) { - var loc = this.window_.location; - var url = this.baseUrl_; - - // If a hash has already been set, then removing it programmatically will - // reload the page. Once there is a hash, we won't remove it. - var hasHash = goog.string.contains(loc.href, '#'); - - if (goog.History.HASH_ALWAYS_REQUIRED || hasHash || token) { - url += '#' + token; - } - - if (url != loc.href) { - if (opt_replace) { - loc.replace(url); - } else { - loc.href = url; - } - } -}; - - -/** - * Sets the hidden iframe state. On IE, this is accomplished by writing a new - * document into the iframe. In Firefox, the iframe's URL fragment stores the - * state instead. - * - * Older versions of webkit cannot set the iframe, so ignore those browsers. - * - * @param {string} token The new string to set. - * @param {boolean=} opt_replace Set to true to replace the current iframe state - * without appending a new history entry. - * @param {string=} opt_title Optional title used when setting the hidden iframe - * title in IE. - * @private - */ -goog.History.prototype.setIframeToken_ = function(token, - opt_replace, - opt_title) { - if (this.unsetIframe_ || token != this.getIframeToken_()) { - - this.unsetIframe_ = false; - token = goog.string.urlEncode(token); - - if (goog.userAgent.IE) { - // Caching the iframe document results in document permission errors after - // leaving the page and returning. Access it anew each time instead. - var doc = goog.dom.getFrameContentDocument(this.iframe_); - - doc.open('text/html', opt_replace ? 'replace' : undefined); - doc.write(goog.string.subs( - goog.History.IFRAME_SOURCE_TEMPLATE_, - goog.string.htmlEscape( - /** @type {string} */ (opt_title || this.window_.document.title)), - token)); - doc.close(); - } else { - var url = this.iframeSrc_ + '#' + token; - - // In Safari, it is possible for the contentWindow of the iframe to not - // be present when the page is loading after a reload. - var contentWindow = this.iframe_.contentWindow; - if (contentWindow) { - if (opt_replace) { - contentWindow.location.replace(url); - } else { - contentWindow.location.href = url; - } - } - } - } -}; - - -/** - * Return the current state string from the hidden iframe. On internet explorer, - * this is stored as a string in the document body. Other browsers use the - * location hash of the hidden iframe. - * - * Older versions of webkit cannot access the iframe location, so always return - * null in that case. - * - * @return {?string} The state token saved in the iframe (possibly null if the - * iframe has never loaded.). - * @private - */ -goog.History.prototype.getIframeToken_ = function() { - if (goog.userAgent.IE) { - var doc = goog.dom.getFrameContentDocument(this.iframe_); - return doc.body ? goog.string.urlDecode(doc.body.innerHTML) : null; - } else { - // In Safari, it is possible for the contentWindow of the iframe to not - // be present when the page is loading after a reload. - var contentWindow = this.iframe_.contentWindow; - if (contentWindow) { - var hash; - /** @preserveTry */ - try { - // Iframe tokens are urlEncoded - hash = goog.string.urlDecode(this.getLocationFragment_(contentWindow)); - } catch (e) { - // An exception will be thrown if the location of the iframe can not be - // accessed (permission denied). This can occur in FF if the the server - // that is hosting the blank html page goes down and then a new history - // token is set. The iframe will navigate to an error page, and the - // location of the iframe can no longer be accessed. Due to the polling, - // this will cause constant exceptions to be thrown. In this case, - // we enable longer polling. We do not have to attempt to reset the - // iframe token because (a) we already fired the NAVIGATE event when - // setting the token, (b) we can rely on the locked token for current - // state, and (c) the token is still in the history and - // accesible on forward/back. - if (!this.longerPolling_) { - this.setLongerPolling_(true); - } - - return null; - } - - // There was no exception when getting the hash so turn off longer polling - // if it is on. - if (this.longerPolling_) { - this.setLongerPolling_(false); - } - - return hash || null; - } else { - return null; - } - } -}; - - -/** - * Checks the state of the document fragment and the iframe title to detect - * navigation changes. If {@code goog.History.HAS_ONHASHCHANGE} is - * {@code false}, then this runs approximately twenty times per second. - * @param {boolean} isNavigation True if the event was initiated by a browser - * action, false if it was caused by a setToken call. See - * {@link goog.history.Event}. - * @private - */ -goog.History.prototype.check_ = function(isNavigation) { - if (this.userVisible_) { - var hash = this.getLocationFragment_(this.window_); - if (hash != this.lastToken_) { - this.update_(hash, isNavigation); - } - } - - // Old IE uses the iframe for both visible and non-visible versions. - if (!this.userVisible_ || goog.History.LEGACY_IE) { - var token = this.getIframeToken_() || ''; - if (this.lockedToken_ == null || token == this.lockedToken_) { - this.lockedToken_ = null; - if (token != this.lastToken_) { - this.update_(token, isNavigation); - } - } - } -}; - - -/** - * Updates the current history state with a given token. Called after a change - * to the location or the iframe state is detected by poll_. - * - * @param {string} token The new history state. - * @param {boolean} isNavigation True if the event was initiated by a browser - * action, false if it was caused by a setToken call. See - * {@link goog.history.Event}. - * @private - */ -goog.History.prototype.update_ = function(token, isNavigation) { - this.lastToken_ = this.hiddenInput_.value = token; - - if (this.userVisible_) { - if (goog.History.LEGACY_IE) { - this.setIframeToken_(token); - } - - this.setHash_(token); - } else { - this.setIframeToken_(token); - } - - this.dispatchEvent(new goog.history.Event(this.getToken(), isNavigation)); -}; - - -/** - * Sets if the history oject should use longer intervals when polling. - * - * @param {boolean} longerPolling Whether to enable longer polling. - * @private - */ -goog.History.prototype.setLongerPolling_ = function(longerPolling) { - if (this.longerPolling_ != longerPolling) { - this.timer_.setInterval(longerPolling ? - goog.History.PollingType.LONG : goog.History.PollingType.NORMAL); - } - this.longerPolling_ = longerPolling; -}; - - -/** - * Opera cancels all outstanding timeouts and intervals after any rapid - * succession of navigation events, including the interval used to detect - * navigation events. This function restarts the interval so that navigation can - * continue. Ideally, only events which would be likely to cause a navigation - * change (mousedown and keydown) would be bound to this function. Since Opera - * seems to ignore keydown events while the alt key is pressed (such as - * alt-left or right arrow), this function is also bound to the much more - * frequent mousemove event. This way, when the update loop freezes, it will - * unstick itself as the user wiggles the mouse in frustration. - * @private - */ -goog.History.prototype.operaDefibrillator_ = function() { - this.timer_.stop(); - this.timer_.start(); -}; - - -/** - * List of user input event types registered in Opera to restart the history - * timer (@see goog.History#operaDefibrillator_). - * @type {Array.<string>} - * @private - */ -goog.History.INPUT_EVENTS_ = [ - goog.events.EventType.MOUSEDOWN, - goog.events.EventType.KEYDOWN, - goog.events.EventType.MOUSEMOVE -]; - - -/** - * Minimal HTML page used to populate the iframe in Internet Explorer. The title - * is visible in the history dropdown menu, the iframe state is stored as the - * body innerHTML. - * @type {string} - * @private - */ -goog.History.IFRAME_SOURCE_TEMPLATE_ = '<title>%s</title><body>%s</body>'; - - -/** - * HTML template for an invisible iframe. - * @type {string} - * @private - */ -goog.History.IFRAME_TEMPLATE_ = - '<iframe id="%s" style="display:none" %s></iframe>'; - - -/** - * HTML template for an invisible named input element. - * @type {string} - * @private - */ -goog.History.INPUT_TEMPLATE_ = - '<input type="text" name="%s" id="%s" style="display:none">'; - - -/** - * Counter for the number of goog.History objects that have been instantiated. - * Used to create unique IDs. - * @type {number} - * @private - */ -goog.History.historyCount_ = 0; - - -/** - * Types of polling. The values are in ms of the polling interval. - * @enum {number} - */ -goog.History.PollingType = { - NORMAL: 150, - LONG: 10000 -}; - - -/** - * Constant for the history change event type. - * @enum {string} - * @deprecated Use goog.history.EventType. - */ -goog.History.EventType = goog.history.EventType; - - - -/** - * Constant for the history change event type. - * @param {string} token The string identifying the new history state. - * @extends {goog.events.Event} - * @constructor - * @deprecated Use goog.history.Event. - */ -goog.History.Event = goog.history.Event; |