304 lines
9.0 KiB
JavaScript
304 lines
9.0 KiB
JavaScript
// Copyright 2010 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 HTML5 based history implementation, compatible with
|
|
* goog.History.
|
|
*
|
|
* TODO(user): There should really be a history interface and multiple
|
|
* implementations.
|
|
*
|
|
*/
|
|
|
|
|
|
goog.provide('goog.history.Html5History');
|
|
goog.provide('goog.history.Html5History.TokenTransformer');
|
|
|
|
goog.require('goog.asserts');
|
|
goog.require('goog.events');
|
|
goog.require('goog.events.EventTarget');
|
|
goog.require('goog.events.EventType');
|
|
goog.require('goog.history.Event');
|
|
goog.require('goog.history.EventType');
|
|
|
|
|
|
|
|
/**
|
|
* An implementation compatible with goog.History that uses the HTML5
|
|
* history APIs.
|
|
*
|
|
* @param {Window=} opt_win The window to listen/dispatch history events on.
|
|
* @param {goog.history.Html5History.TokenTransformer=} opt_transformer
|
|
* The token transformer that is used to create URL from the token
|
|
* when storing token without using hash fragment.
|
|
* @constructor
|
|
* @extends {goog.events.EventTarget}
|
|
*/
|
|
goog.history.Html5History = function(opt_win, opt_transformer) {
|
|
goog.events.EventTarget.call(this);
|
|
goog.asserts.assert(goog.history.Html5History.isSupported(opt_win),
|
|
'HTML5 history is not supported.');
|
|
|
|
/**
|
|
* The window object to use for history tokens. Typically the top window.
|
|
* @type {Window}
|
|
* @private
|
|
*/
|
|
this.window_ = opt_win || window;
|
|
|
|
/**
|
|
* The token transformer that is used to create URL from the token
|
|
* when storing token without using hash fragment.
|
|
* @type {goog.history.Html5History.TokenTransformer}
|
|
* @private
|
|
*/
|
|
this.transformer_ = opt_transformer || null;
|
|
|
|
goog.events.listen(this.window_, goog.events.EventType.POPSTATE,
|
|
this.onHistoryEvent_, false, this);
|
|
goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
|
|
this.onHistoryEvent_, false, this);
|
|
};
|
|
goog.inherits(goog.history.Html5History, goog.events.EventTarget);
|
|
|
|
|
|
/**
|
|
* Returns whether Html5History is supported.
|
|
* @param {Window=} opt_win Optional window to check.
|
|
* @return {boolean} Whether html5 history is supported.
|
|
*/
|
|
goog.history.Html5History.isSupported = function(opt_win) {
|
|
var win = opt_win || window;
|
|
return !!(win.history && win.history.pushState);
|
|
};
|
|
|
|
|
|
/**
|
|
* Status of when the object is active and dispatching events.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.history.Html5History.prototype.enabled_ = false;
|
|
|
|
|
|
/**
|
|
* Whether to use the fragment to store the token, defaults to true.
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
goog.history.Html5History.prototype.useFragment_ = true;
|
|
|
|
|
|
/**
|
|
* If useFragment is false the path will be used, the path prefix will be
|
|
* prepended to all tokens. Defaults to '/'.
|
|
* @type {string}
|
|
* @private
|
|
*/
|
|
goog.history.Html5History.prototype.pathPrefix_ = '/';
|
|
|
|
|
|
/**
|
|
* Starts or stops the History. 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.
|
|
*
|
|
* @param {boolean} enable Whether to enable history.
|
|
*/
|
|
goog.history.Html5History.prototype.setEnabled = function(enable) {
|
|
if (enable == this.enabled_) {
|
|
return;
|
|
}
|
|
|
|
this.enabled_ = enable;
|
|
|
|
if (enable) {
|
|
this.dispatchEvent(new goog.history.Event(this.getToken(), false));
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Returns the current token.
|
|
* @return {string} The current token.
|
|
*/
|
|
goog.history.Html5History.prototype.getToken = function() {
|
|
if (this.useFragment_) {
|
|
var loc = this.window_.location.href;
|
|
var index = loc.indexOf('#');
|
|
return index < 0 ? '' : loc.substring(index + 1);
|
|
} else {
|
|
return this.transformer_ ?
|
|
this.transformer_.retrieveToken(
|
|
this.pathPrefix_, this.window_.location) :
|
|
this.window_.location.pathname.substr(this.pathPrefix_.length);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the history state.
|
|
* @param {string} token The history state identifier.
|
|
* @param {string=} opt_title Optional title to associate with history entry.
|
|
*/
|
|
goog.history.Html5History.prototype.setToken = function(token, opt_title) {
|
|
if (token == this.getToken()) {
|
|
return;
|
|
}
|
|
|
|
// Per externs/gecko_dom.js document.title can be null.
|
|
this.window_.history.pushState(null,
|
|
opt_title || this.window_.document.title || '', this.getUrl_(token));
|
|
this.dispatchEvent(new goog.history.Event(token, false));
|
|
};
|
|
|
|
|
|
/**
|
|
* 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 to associate with history entry.
|
|
*/
|
|
goog.history.Html5History.prototype.replaceToken = function(token, opt_title) {
|
|
// Per externs/gecko_dom.js document.title can be null.
|
|
this.window_.history.replaceState(null,
|
|
opt_title || this.window_.document.title || '', this.getUrl_(token));
|
|
this.dispatchEvent(new goog.history.Event(token, false));
|
|
};
|
|
|
|
|
|
/** @override */
|
|
goog.history.Html5History.prototype.disposeInternal = function() {
|
|
goog.events.unlisten(this.window_, goog.events.EventType.POPSTATE,
|
|
this.onHistoryEvent_, false, this);
|
|
if (this.useFragment_) {
|
|
goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
|
|
this.onHistoryEvent_, false, this);
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets whether to use the fragment to store tokens.
|
|
* @param {boolean} useFragment Whether to use the fragment.
|
|
*/
|
|
goog.history.Html5History.prototype.setUseFragment = function(useFragment) {
|
|
if (this.useFragment_ != useFragment) {
|
|
if (useFragment) {
|
|
goog.events.listen(this.window_, goog.events.EventType.HASHCHANGE,
|
|
this.onHistoryEvent_, false, this);
|
|
} else {
|
|
goog.events.unlisten(this.window_, goog.events.EventType.HASHCHANGE,
|
|
this.onHistoryEvent_, false, this);
|
|
}
|
|
this.useFragment_ = useFragment;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Sets the path prefix to use if storing tokens in the path. The path
|
|
* prefix should start and end with slash.
|
|
* @param {string} pathPrefix Sets the path prefix.
|
|
*/
|
|
goog.history.Html5History.prototype.setPathPrefix = function(pathPrefix) {
|
|
this.pathPrefix_ = pathPrefix;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the path prefix.
|
|
* @return {string} The path prefix.
|
|
*/
|
|
goog.history.Html5History.prototype.getPathPrefix = function() {
|
|
return this.pathPrefix_;
|
|
};
|
|
|
|
|
|
/**
|
|
* Gets the URL to set when calling history.pushState
|
|
* @param {string} token The history token.
|
|
* @return {string} The URL.
|
|
* @private
|
|
*/
|
|
goog.history.Html5History.prototype.getUrl_ = function(token) {
|
|
if (this.useFragment_) {
|
|
return '#' + token;
|
|
} else {
|
|
return this.transformer_ ?
|
|
this.transformer_.createUrl(
|
|
token, this.pathPrefix_, this.window_.location) :
|
|
this.pathPrefix_ + token + this.window_.location.search;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Handles history events dispatched by the browser.
|
|
* @param {goog.events.BrowserEvent} e The browser event object.
|
|
* @private
|
|
*/
|
|
goog.history.Html5History.prototype.onHistoryEvent_ = function(e) {
|
|
if (this.enabled_) {
|
|
this.dispatchEvent(new goog.history.Event(this.getToken(), true));
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* A token transformer that can create a URL from a history
|
|
* token. This is used by {@code goog.history.Html5History} to create
|
|
* URL when storing token without the hash fragment.
|
|
*
|
|
* Given a {@code window.location} object containing the location
|
|
* created by {@code createUrl}, the token transformer allows
|
|
* retrieval of the token back via {@code retrieveToken}.
|
|
*
|
|
* @interface
|
|
*/
|
|
goog.history.Html5History.TokenTransformer = function() {};
|
|
|
|
|
|
/**
|
|
* Retrieves a history token given the path prefix and
|
|
* {@code window.location} object.
|
|
*
|
|
* @param {string} pathPrefix The path prefix to use when storing token
|
|
* in a path; always begin with a slash.
|
|
* @param {Location} location The {@code window.location} object.
|
|
* Treat this object as read-only.
|
|
* @return {string} token The history token.
|
|
*/
|
|
goog.history.Html5History.TokenTransformer.prototype.retrieveToken = function(
|
|
pathPrefix, location) {};
|
|
|
|
|
|
/**
|
|
* Creates a URL to be pushed into HTML5 history stack when storing
|
|
* token without using hash fragment.
|
|
*
|
|
* @param {string} token The history token.
|
|
* @param {string} pathPrefix The path prefix to use when storing token
|
|
* in a path; always begin with a slash.
|
|
* @param {Location} location The {@code window.location} object.
|
|
* Treat this object as read-only.
|
|
* @return {string} url The complete URL string from path onwards
|
|
* (without {@code protocol://host:port} part); must begin with a
|
|
* slash.
|
|
*/
|
|
goog.history.Html5History.TokenTransformer.prototype.createUrl = function(
|
|
token, pathPrefix, location) {};
|