/* * Copyright 2012 Johannes Barop * * 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. */ package de.barop.gwt.client; import java.util.logging.Logger; import com.google.gwt.logging.client.LogConfiguration; import com.google.gwt.user.client.History; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.impl.HistoryImpl; /** * Extends GWT's {@link HistoryImpl} and adds HTML5 pushState support. * * <p> * The complete path is treated as history token. * </p> * * <p> * The leading '/' is hidden from GWTs History API, so that the path '/' is returned as an empty * history token (''). * </p> * * @author <a href="mailto:jb@barop.de">Johannes Barop</a> * */ public class HistoryImplPushState extends HistoryImpl { private static final Logger LOG = Logger.getLogger(HistoryImplPushState.class.getName()); @Override public boolean init() { // initialize HistoryImpl with the current path updateHistoryToken(Window.Location.getPath() + Window.Location.getQueryString()); // initialize the empty state with the current history token nativeUpdate(getToken()); // initialize the popState handler initPopStateHandler(); return true; } @Override protected void nativeUpdate(final String historyToken) { String newPushStateToken = CodeServerParameterHelper.append(encodeFragment(historyToken)); if (!newPushStateToken.startsWith("/")) { newPushStateToken = "/" + newPushStateToken; } pushState(newPushStateToken); if (LogConfiguration.loggingIsEnabled()) { LOG.fine("Pushed '" + newPushStateToken + "' (" + historyToken + ")"); } } /** * Set the current path as GWT History token which can later retrieved with * {@link History#getToken()}. */ private void updateHistoryToken(String path) { String[] split = path.split("\\?"); String token = split[0]; token = (token.length() > 0) ? decodeFragment(token) : ""; token = (token.startsWith("/")) ? token.substring(1) : token; String queryString = (split.length == 2) ? split[1] : ""; queryString = CodeServerParameterHelper.remove(queryString); if (queryString != null && !queryString.trim().isEmpty()) { token += "?" + queryString; } if (LogConfiguration.loggingIsEnabled()) { LOG.fine("Set token to '" + token + "'"); } setToken(token); } /** * Initialize an event handler that gets executed when the token changes. */ private native void initPopStateHandler() /*-{ var that = this; var oldHandler = $wnd.onpopstate; $wnd.onpopstate = $entry(function(e) { if (e.state && e.state.historyToken) { that.@de.barop.gwt.client.HistoryImplPushState::onPopState(Ljava/lang/String;)(e.state.historyToken); } if (oldHandler) { oldHandler(e); } }); }-*/; /** * Called from native JavaScript when an old history state was popped. */ private void onPopState(final String historyToken) { if (LogConfiguration.loggingIsEnabled()) { LOG.fine("Popped '" + historyToken + "'"); } updateHistoryToken(historyToken); fireHistoryChangedImpl(getToken()); } /** * Add the given token to the history using pushState. */ private static native void pushState(final String token) /*-{ var state = { historyToken : token }; $wnd.history.pushState(state, $doc.title, token); }-*/; }