com.vaadin.client.ui.VCustomLayout Maven / Gradle / Ivy
Show all versions of vaadin-client Show documentation
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* 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 com.vaadin.client.ui;
import java.util.HashMap;
import java.util.Iterator;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.Util;
import com.vaadin.client.VCaption;
import com.vaadin.client.VCaptionWrapper;
import com.vaadin.client.WidgetUtil;
/**
* Custom Layout implements complex layout defined with HTML template.
*
* @author Vaadin Ltd
*
*/
public class VCustomLayout extends ComplexPanel {
public static final String CLASSNAME = "v-customlayout";
/** Location-name to containing element in DOM map */
private final HashMap locationToElement = new HashMap();
/** Location-name to contained widget map */
final HashMap locationToWidget = new HashMap();
/** Widget to captionwrapper map */
private final HashMap childWidgetToCaptionWrapper = new HashMap();
/**
* Unexecuted scripts loaded from the template.
*
* For internal use only. May be removed or replaced in the future.
*/
public String scripts = "";
/**
* Paintable ID of this paintable.
*
* For internal use only. May be removed or replaced in the future.
*/
public String pid;
/** For internal use only. May be removed or replaced in the future. */
public ApplicationConnection client;
private boolean htmlInitialized = false;
private Element elementWithNativeResizeFunction;
private String height = "";
private String width = "";
public VCustomLayout() {
setElement(DOM.createDiv());
// Clear any unwanted styling
Style style = getElement().getStyle();
style.setBorderStyle(BorderStyle.NONE);
style.setMargin(0, Unit.PX);
style.setPadding(0, Unit.PX);
if (BrowserInfo.get().isIE()) {
style.setPosition(Position.RELATIVE);
}
setStyleName(CLASSNAME);
}
@Override
public void setStyleName(String style) {
super.setStyleName(style);
addStyleName(StyleConstants.UI_LAYOUT);
}
/**
* Sets widget to given location.
*
* If location already contains a widget it will be removed.
*
* @param widget
* Widget to be set into location.
* @param location
* location name where widget will be added
*
* @throws IllegalArgumentException
* if no such location is found in the layout.
*/
public void setWidget(Widget widget, String location) {
if (widget == null) {
return;
}
// If no given location is found in the layout, and exception is throws
Element elem = locationToElement.get(location);
if (elem == null && hasTemplate()) {
throw new IllegalArgumentException("No location " + location
+ " found");
}
// Get previous widget
final Widget previous = locationToWidget.get(location);
// NOP if given widget already exists in this location
if (previous == widget) {
return;
}
if (previous != null) {
remove(previous);
}
// if template is missing add element in order
if (!hasTemplate()) {
elem = getElement();
}
// Add widget to location
super.add(widget, elem);
locationToWidget.put(location, widget);
}
/** Initialize HTML-layout. */
public void initializeHTML(String template, String themeUri) {
// Connect body of the template to DOM
template = extractBodyAndScriptsFromTemplate(template);
// TODO prefix img src:s here with a regeps, cannot work further with IE
String relImgPrefix = WidgetUtil
.escapeAttribute(themeUri + "/layouts/");
// prefix all relative image elements to point to theme dir with a
// regexp search
template = template.replaceAll(
"<((?:img)|(?:IMG))\\s([^>]*)src=\"((?![a-z]+:)[^/][^\"]+)\"",
"<$1 $2src=\"" + relImgPrefix + "$3\"");
// also support src attributes without quotes
template = template
.replaceAll(
"<((?:img)|(?:IMG))\\s([^>]*)src=[^\"]((?![a-z]+:)[^/][^ />]+)[ />]",
"<$1 $2src=\"" + relImgPrefix + "$3\"");
// also prefix relative style="...url(...)..."
template = template
.replaceAll(
"(<[^>]+style=\"[^\"]*url\\()((?![a-z]+:)[^/][^\"]+)(\\)[^>]*>)",
"$1 " + relImgPrefix + "$2 $3");
getElement().setInnerHTML(template);
// Remap locations to elements
locationToElement.clear();
scanForLocations(getElement());
initImgElements();
elementWithNativeResizeFunction = DOM.getFirstChild(getElement());
if (elementWithNativeResizeFunction == null) {
elementWithNativeResizeFunction = getElement();
}
publishResizedFunction(elementWithNativeResizeFunction);
htmlInitialized = true;
}
private native boolean uriEndsWithSlash()
/*-{
var path = $wnd.location.pathname;
if(path.charAt(path.length - 1) == "/")
return true;
return false;
}-*/;
/** For internal use only. May be removed or replaced in the future. */
public boolean hasTemplate() {
return htmlInitialized;
}
/** Collect locations from template */
private void scanForLocations(Element elem) {
if (elem.hasAttribute("location")) {
final String location = elem.getAttribute("location");
locationToElement.put(location, elem);
elem.setInnerHTML("");
} else {
final int len = DOM.getChildCount(elem);
for (int i = 0; i < len; i++) {
scanForLocations(DOM.getChild(elem, i));
}
}
}
/**
* Evaluate given script in browser document.
*
* For internal use only. May be removed or replaced in the future.
*/
public static native void eval(String script)
/*-{
try {
if (script != null)
eval("{ var document = $doc; var window = $wnd; "+ script + "}");
} catch (e) {
}
}-*/;
/**
* Img elements needs some special handling in custom layout. Img elements
* will get their onload events sunk. This way custom layout can notify
* parent about possible size change.
*/
private void initImgElements() {
NodeList nodeList = getElement().getElementsByTagName("IMG");
for (int i = 0; i < nodeList.getLength(); i++) {
ImageElement img = ImageElement.as(nodeList.getItem(i));
DOM.sinkEvents(img, Event.ONLOAD);
}
}
/**
* Extract body part and script tags from raw html-template.
*
* Saves contents of all script-tags to private property: scripts. Returns
* contents of the body part for the html without script-tags. Also replaces
* all _UID_ tags with an unique id-string.
*
* @param html
* Original HTML-template received from server
* @return html that is used to create the HTMLPanel.
*/
private String extractBodyAndScriptsFromTemplate(String html) {
// Replace UID:s
html = html.replaceAll("_UID_", pid + "__");
// Exctract script-tags
scripts = "";
int endOfPrevScript = 0;
int nextPosToCheck = 0;
String lc = html.toLowerCase();
String res = "";
int scriptStart = lc.indexOf("", scriptStart);
scripts += html.substring(scriptStart + 1, j) + ";";
nextPosToCheck = endOfPrevScript = j + "".length();
scriptStart = lc.indexOf("