/*
* Copyright 2014 cruxframework.org.
*
* 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 org.cruxframework.crux.core.client.screen.views;
import org.cruxframework.crux.core.client.screen.Screen;
import org.cruxframework.crux.core.client.screen.history.History;
import org.cruxframework.crux.core.client.utils.StringUtils;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
/**
* A view container that handle history changes using the hashbang approach
* {@link https://developers.google.com/webmasters/ajax-crawling/docs/getting-started}
* @author Thiago da Rosa de Bustamante
*
*/
public abstract class CrawlableViewContainer extends ViewContainer
{
private boolean historyControlEnabled = true;
private String historyControlPrefix = null;
private HandlerRegistration historyControlHandler;
private boolean createNewHistoryToken = true;
private int historyStackSize = 0;
/**
* Constructor
* @param mainWidget main widget on this container
*/
public CrawlableViewContainer(Widget mainWidget)
{
this(mainWidget, true);
}
/**
* Constructor
* @param mainWidget Main widget on this container
* @param clearPanelsForDeactivatedViews If true, makes the container clear the container panel for a view, when the view is deactivated.
*/
public CrawlableViewContainer(Widget mainWidget, boolean clearPanelsForDeactivatedViews)
{
super(mainWidget, clearPanelsForDeactivatedViews);
historyControlPrefix = getDefaultHistoryPrefix();
}
/**
* Retrieve the historyControlEnabled property value. When this property is enabled, the container control
* automatically the browser history to forward and back between container's views changes
* @return true if enabled
*/
public boolean isHistoryControlEnabled()
{
return historyControlEnabled;
}
/**
* Sets the historyControlEnabled property value. When this property is enabled, the container control
* automatically the browser history to forward and back between container's views changes
* @param historyControlEnabled true to enable history control
*/
public void setHistoryControlEnabled(boolean historyControlEnabled)
{
this.historyControlEnabled = historyControlEnabled;
}
/**
* Retrieve the historyControlPrefix property value. When historyControlEnabled property is enabled, the
* container control automatically the browser history to forward and back between container's views changes.
* This property inform the prefix that will be used on historyTokens created by this container
*
* @return history prefix
*/
public String getHistoryControlPrefix()
{
return historyControlPrefix;
}
/**
* Sets the historyControlPrefix property value. When historyControlEnabled property is enabled, the
* container control automatically the browser history to forward and back between container's views changes.
* This property inform the prefix that will be used on historyTokens created by this container
*
* @param historyControlPrefix history prefix to set
*/
public void setHistoryControlPrefix(String historyControlPrefix)
{
if (!StringUtils.isEmpty(historyControlPrefix))
{
if (!historyControlPrefix.startsWith("!"))
{
historyControlPrefix = "!"+historyControlPrefix;
}
if (!historyControlPrefix.endsWith("="))
{
historyControlPrefix = historyControlPrefix+"=";
}
}
else
{
historyControlPrefix = getDefaultHistoryPrefix();
}
this.historyControlPrefix = historyControlPrefix;
}
/**
* Return the current history token.
* @return
*/
public String getCurrentHistoryItem()
{
return Screen.getCurrentHistoryItem();
}
/**
*
* @return
*/
public boolean isHistoryTarget()
{
String token = getCurrentHistoryItem();
if (token != null && token.startsWith(historyControlPrefix))
{
return true;
}
return false;
}
/**
* Return the default historyPrefix
* @return the default historyPrefix
*/
protected String getDefaultHistoryPrefix()
{
return "!view=";
}
@Override
protected void bindToDOM()
{
super.bindToDOM();
attachHistoryControlHandler();
synchronizeHistoryState();
}
@Override
protected void unbindToDOM()
{
dettachHistoryControlHandler();
super.unbindToDOM();
}
@Override
protected boolean activate(View view, Panel containerPanel, Object parameter)
{
boolean activated = super.activate(view, containerPanel, parameter);
if (isHistoryControlEnabled() && activated)
{
if (createNewHistoryToken)
{
Screen.addToHistory(this.historyControlPrefix+view.getId());
}
else
{
createNewHistoryToken = true;
}
}
return activated;
}
protected void attachHistoryControlHandler()
{
dettachHistoryControlHandler();
historyControlHandler = Screen.addHistoryChangedHandler(new ValueChangeHandler<String>(){
@Override
public void onValueChange(ValueChangeEvent<String> event)
{
String token = event.getValue();
updateViewToken(token);
}
});
}
protected void dettachHistoryControlHandler()
{
if (historyControlHandler != null)
{
historyControlHandler.removeHandler();
historyControlHandler = null;
}
}
protected void synchronizeHistoryState()
{
Scheduler.get().scheduleDeferred(new ScheduledCommand()
{
@Override
public void execute()
{
String historyItem = Screen.getCurrentHistoryItem();
updateViewToken(historyItem);
}
});
}
protected void updateViewToken(String token)
{
if (isHistoryControlEnabled())
{
int historyLength = History.length();
boolean backButtonPressed = (historyLength < historyStackSize);
historyStackSize = historyLength;
if (token != null && token.startsWith(historyControlPrefix))
{
String viewId = token.substring(historyControlPrefix.length());
if (!isViewDisplayed(viewId))
{
createNewHistoryToken = false;
showView(viewId, backButtonPressed);
}
}
}
}
/**
* Show the given view, considering that the last navigation was derived from an user back command.
* @param viewName view name
* @param backButtonPressed true if it is possible to detect that the last navigation was derived from an user back command
*/
protected void showView(String viewName, boolean backButtonPressed)
{
showView(viewName);
}
protected abstract boolean isViewDisplayed(String viewId);
}