blob: 02ecaef6761106fc7e799857c343ede0767368bf [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* 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.google.gwt.user.client.ui;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.WhiteSpace;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.BidiUtils;
import com.google.gwt.i18n.client.HasDirection;
import com.google.gwt.i18n.shared.DirectionEstimator;
import com.google.gwt.i18n.shared.HasDirectionEstimator;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.safehtml.shared.annotations.IsSafeHtml;
import com.google.gwt.safehtml.shared.annotations.IsSafeUri;
import com.google.gwt.safehtml.shared.annotations.SuppressIsSafeHtmlCastCheck;
/**
* A widget that represents a simple <a> element.
*
* <p>
* If you want use this anchor only for changing history states, use
* {@link Hyperlink} instead.
* </p>
*
* <p>
* <h3>Built-in Bidi Text Support</h3>
* This widget is capable of automatically adjusting its direction according to
* its content. This feature is controlled by {@link #setDirectionEstimator} or
* passing a DirectionEstimator parameter to the constructor, and is off by
* default.
* </p>
*
* <h3>CSS Style Rules</h3>
* <ul class='css'>
* <li>.gwt-Anchor { }</li>
* </ul>
*
* @see Hyperlink
*/
public class Anchor extends FocusWidget implements HasHorizontalAlignment,
HasName, HasHTML, HasWordWrap, HasDirection,
HasDirectionEstimator, HasDirectionalSafeHtml {
public static final DirectionEstimator DEFAULT_DIRECTION_ESTIMATOR =
DirectionalTextHelper.DEFAULT_DIRECTION_ESTIMATOR;
/**
* The default HREF is a no-op javascript statement. We need an href to ensure
* that the browser renders the anchor with native styles, such as underline
* and font color.
*/
private static final String DEFAULT_HREF = "javascript:;";
/**
* Creates an Anchor widget that wraps an existing &lt;a&gt; element.
*
* This element must already be attached to the document. If the element is
* removed from the document, you must call
* {@link RootPanel#detachNow(Widget)}.
*
* @param element the element to be wrapped
*/
public static Anchor wrap(Element element) {
// Assert that the element is attached.
assert Document.get().getBody().isOrHasChild(element);
Anchor anchor = new Anchor(element);
// Mark it attached and remember it for cleanup.
anchor.onAttach();
RootPanel.detachOnWindowClose(anchor);
return anchor;
}
private final DirectionalTextHelper directionalTextHelper;
private HorizontalAlignmentConstant horzAlign;
/**
* Creates an empty anchor.
*
* <p>
* The anchor's href is <em>not</em> set, which means that the widget will not
* not be styled with the browser's native link styles (such as underline and
* font color). Use {@link #Anchor(boolean)} to add a default no-op href that
* does not open a link but ensures the native link styles are applied.
* </p>
*
* @see #Anchor(boolean)
*/
public Anchor() {
this(false);
}
/**
* Creates an anchor.
*
* The anchor's href is optionally set to <code>javascript:;</code>, based on
* the expectation that listeners will be added to the anchor.
*
* @param useDefaultHref true to set the default href to
* <code>javascript:;</code>, false to leave it blank
*/
public Anchor(boolean useDefaultHref) {
setElement(Document.get().createAnchorElement());
setStyleName("gwt-Anchor");
directionalTextHelper = new DirectionalTextHelper(getAnchorElement(),
/* is inline */true);
if (useDefaultHref) {
setHref(DEFAULT_HREF);
}
// The following click handler is used to support users of CSP (Content
// Security Policy). When a CSP policy is in place, clicking on a link with
// the DEFAULT_HREF as the href can trigger a CSP violation. As a
// work-around, we prevent execution using preventDefault() when the href is
// set to DEFAULT_HREF.
if ("true".equals(System.getProperty("gwt.cspCompatModeEnabled"))) {
addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (DEFAULT_HREF.equals(getAnchorElement().getHref())) {
event.preventDefault();
}
}
});
}
}
/**
* Creates an anchor for scripting.
*
* @param html the anchor's html
*/
public Anchor(SafeHtml html) {
this(html.asString(), true);
}
/**
* Creates an anchor for scripting.
*
* The anchor's href is set to <code>javascript : ;</code>, based on the
* expectation that listeners will be added to the anchor.
*
* @param html the anchor's html
* @param dir the html's direction
*/
public Anchor(SafeHtml html, Direction dir) {
this(html.asString(), true, dir, DEFAULT_HREF);
}
/**
* Creates an anchor for scripting.
*
* The anchor's href is set to <code>javascript : ;</code>, based on the
* expectation that listeners will be added to the anchor.
*
* @param html the anchor's html
* @param directionEstimator A DirectionEstimator object used for automatic
* direction adjustment. For convenience,
* {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
*/
public Anchor(SafeHtml html, DirectionEstimator directionEstimator) {
this(html.asString(), true, directionEstimator, DEFAULT_HREF);
}
/**
* Creates an anchor for scripting.
*
* The anchor's href is set to <code>javascript:;</code>, based on the
* expectation that listeners will be added to the anchor.
*
* @param text the anchor's text
*/
public Anchor(String text) {
this(text, DEFAULT_HREF);
}
/**
* Creates an anchor for scripting.
*
* The anchor's href is set to <code>javascript : ;</code>, based on the
* expectation that listeners will be added to the anchor.
*
* @param text the anchor's text
* @param dir the text's direction
*/
public Anchor(String text, Direction dir) {
this(text, dir, DEFAULT_HREF);
}
/**
* Creates an anchor for scripting.
*
* The anchor's href is set to <code>javascript : ;</code>, based on the
* expectation that listeners will be added to the anchor.
*
* @param text the anchor's text
* @param directionEstimator A DirectionEstimator object used for automatic
* direction adjustment. For convenience,
* {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
*/
public Anchor(String text, DirectionEstimator directionEstimator) {
this(text, directionEstimator, DEFAULT_HREF);
}
/**
* Creates an anchor for scripting.
*
* The anchor's href is set to <code>javascript:;</code>, based on the
* expectation that listeners will be added to the anchor.
*
* @param text the anchor's text
* @param asHtml <code>true</code> to treat the specified text as html
*/
public Anchor(@IsSafeHtml String text, boolean asHtml) {
this(text, asHtml, DEFAULT_HREF);
}
/**
* Creates an anchor with its html and href (target URL) specified.
*
* @param html the anchor's html
* @param href the url to which it will link
*/
public Anchor(SafeHtml html, @IsSafeUri String href) {
this(html.asString(), true, href);
}
/**
* Creates an anchor with its html and href (target URL) specified.
*
* @param html the anchor's html
* @param href the url to which it will link
*/
public Anchor(SafeHtml html, SafeUri href) {
this(html.asString(), true, href.asString());
}
/**
* Creates an anchor with its html and href (target URL) specified.
*
* @param html the anchor's html
* @param dir the html's direction
* @param href the url to which it will link
*/
public Anchor(SafeHtml html, Direction dir, @IsSafeUri String href) {
this(html.asString(), true, dir, href);
}
/**
* Creates an anchor with its html and href (target URL) specified.
*
* @param html the anchor's html
* @param dir the html's direction
* @param href the url to which it will link
*/
public Anchor(SafeHtml html, Direction dir, SafeUri href) {
this(html.asString(), true, dir, href.asString());
}
/**
* Creates an anchor with its html and href (target URL) specified.
*
* @param html the anchor's html
* @param directionEstimator A DirectionEstimator object used for automatic
* direction adjustment. For convenience,
* {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
* @param href the url to which it will link
*/
public Anchor(SafeHtml html, DirectionEstimator directionEstimator, @IsSafeUri String href) {
this(html.asString(), true, directionEstimator, href);
}
/**
* Creates an anchor with its html and href (target URL) specified.
*
* @param html the anchor's html
* @param directionEstimator A DirectionEstimator object used for automatic
* direction adjustment. For convenience,
* {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
* @param href the url to which it will link
*/
public Anchor(SafeHtml html, DirectionEstimator directionEstimator,
SafeUri href) {
this(html.asString(), true, directionEstimator, href.asString());
}
/**
* Creates an anchor with its text and href (target URL) specified.
*
* @param text the anchor's text
* @param href the url to which it will link
*/
@SuppressIsSafeHtmlCastCheck
public Anchor(String text, @IsSafeUri String href) {
this(text, false, href);
}
/**
* Creates an anchor with its text and href (target URL) specified.
*
* @param text the anchor's text
* @param dir the text's direction
* @param href the url to which it will link
*/
@SuppressIsSafeHtmlCastCheck
public Anchor(String text, Direction dir, @IsSafeUri String href) {
this(text, false, dir, href);
}
/**
* Creates an anchor with its text and href (target URL) specified.
*
* @param text the anchor's text
* @param directionEstimator A DirectionEstimator object used for automatic
* direction adjustment. For convenience,
* {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
* @param href the url to which it will link
*/
@SuppressIsSafeHtmlCastCheck
public Anchor(String text, DirectionEstimator directionEstimator, @IsSafeUri String href) {
this(text, false, directionEstimator, href);
}
/**
* Creates an anchor with its text and href (target URL) specified.
*
* @param text the anchor's text
* @param asHTML <code>true</code> to treat the specified text as html
* @param href the url to which it will link
*/
public Anchor(@IsSafeHtml String text, boolean asHTML, @IsSafeUri String href) {
this();
directionalTextHelper.setTextOrHtml(text, asHTML);
setHref(href);
}
/**
* Creates a source anchor (link to URI).
*
* That is, an anchor with an href attribute specifying the destination URI.
*
* @param html the anchor's html
* @param href the url to which it will link
* @param target the target frame (e.g. "_blank" to open the link in a new
* window)
*/
public Anchor(SafeHtml html, @IsSafeUri String href, String target) {
this(html.asString(), true, href, target);
}
/**
* Creates a source anchor (link to URI).
*
* That is, an anchor with an href attribute specifying the destination URI.
*
* @param html the anchor's html
* @param href the url to which it will link
* @param target the target frame (e.g. "_blank" to open the link in a new
* window)
*/
public Anchor(SafeHtml html, SafeUri href, String target) {
this(html.asString(), true, href.asString(), target);
}
/**
* Creates a source anchor with a frame target.
*
* @param text the anchor's text
* @param href the url to which it will link
* @param target the target frame (e.g. "_blank" to open the link in a new
* window)
*/
@SuppressIsSafeHtmlCastCheck
public Anchor(String text, @IsSafeUri String href, String target) {
this(text, false, href, target);
}
/**
* Creates a source anchor (link to URI).
*
* That is, an anchor with an href attribute specifying the destination URI.
*
* @param text the anchor's text
* @param asHtml asHTML <code>true</code> to treat the specified text as html
* @param href the url to which it will link
* @param target the target frame (e.g. "_blank" to open the link in a new
* window)
*/
public Anchor(@IsSafeHtml String text, boolean asHtml, @IsSafeUri String href, String target) {
this(text, asHtml, href);
setTarget(target);
}
/**
* This constructor may be used by subclasses to explicitly use an existing
* element. This element must be an &lt;a&gt; element.
*
* @param element the element to be used
*/
protected Anchor(Element element) {
AnchorElement.as(element);
setElement(element);
directionalTextHelper = new DirectionalTextHelper(getAnchorElement(),
/* is inline */ true);
}
/**
* Creates an anchor with its text, direction and href (target URL) specified.
*
* @param text the anchor's text
* @param asHTML <code>true</code> to treat the specified text as html
* @param dir the text's direction
* @param href the url to which it will link
*/
private Anchor(@IsSafeHtml String text, boolean asHTML, Direction dir, @IsSafeUri String href) {
this();
directionalTextHelper.setTextOrHtml(text, dir, asHTML);
setHref(href);
}
/**
* Creates an anchor with its text, direction and href (target URL) specified.
*
* @param text the anchor's text
* @param asHTML <code>true</code> to treat the specified text as html
* @param directionEstimator A DirectionEstimator object used for automatic
* direction adjustment. For convenience,
* {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used.
* @param href the url to which it will link
*/
private Anchor(
@IsSafeHtml String text,
boolean asHTML,
DirectionEstimator directionEstimator,
@IsSafeUri String href) {
this();
directionalTextHelper.setDirectionEstimator(directionEstimator);
directionalTextHelper.setTextOrHtml(text, asHTML);
setHref(href);
}
@Override
public Direction getDirection() {
return BidiUtils.getDirectionOnElement(getElement());
}
@Override
public DirectionEstimator getDirectionEstimator() {
return directionalTextHelper.getDirectionEstimator();
}
@Override
public HorizontalAlignmentConstant getHorizontalAlignment() {
return horzAlign;
}
/**
* Gets the anchor's href (the url to which it links).
*
* @return the anchor's href
*/
public String getHref() {
return getAnchorElement().getHref();
}
@Override
public String getHTML() {
return getElement().getInnerHTML();
}
@Override
public String getName() {
return getAnchorElement().getName();
}
@Override
public int getTabIndex() {
return getAnchorElement().getTabIndex();
}
/**
* Gets the anchor's target frame (the frame in which navigation will occur
* when the link is selected).
*
* @return the target frame
*/
public String getTarget() {
return getAnchorElement().getTarget();
}
@Override
public String getText() {
return directionalTextHelper.getText();
}
@Override
public Direction getTextDirection() {
return directionalTextHelper.getTextDirection();
}
@Override
public boolean getWordWrap() {
return !WhiteSpace.NOWRAP.getCssName().equals(getElement().getStyle().getWhiteSpace());
}
@Override
public void setAccessKey(char key) {
getAnchorElement().setAccessKey(Character.toString(key));
}
/**
* @deprecated Use {@link #setDirectionEstimator} and / or pass explicit
* direction to {@link #setText}, {@link #setHTML} instead
*/
@Override
@Deprecated
public void setDirection(Direction direction) {
directionalTextHelper.setDirection(direction);
}
/**
* {@inheritDoc}
* <p>
* See note at {@link #setDirectionEstimator(DirectionEstimator)}.
*/
@Override
public void setDirectionEstimator(boolean enabled) {
directionalTextHelper.setDirectionEstimator(enabled);
}
/**
* {@inheritDoc}
* <p>
* Note: DirectionEstimator should be set before the widget has any content;
* it's highly recommended to set it using a constructor. Reason: if the
* widget already has non-empty content, this will update its direction
* according to the new estimator's result. This may cause flicker, and thus
* should be avoided.
*/
@Override
public void setDirectionEstimator(DirectionEstimator directionEstimator) {
directionalTextHelper.setDirectionEstimator(directionEstimator);
}
@Override
public void setFocus(boolean focused) {
if (focused) {
getAnchorElement().focus();
} else {
getAnchorElement().blur();
}
}
@Override
public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
horzAlign = align;
getElement().getStyle().setProperty("textAlign", align.getTextAlignString());
}
/**
* Sets the anchor's href (the url to which it links).
*
* @param href the anchor's href
*/
public void setHref(SafeUri href) {
getAnchorElement().setHref(href);
}
/**
* Sets the anchor's href (the url to which it links).
*
* @param href the anchor's href
*/
public void setHref(@IsSafeUri String href) {
getAnchorElement().setHref(href);
}
@Override
public void setHTML(SafeHtml html) {
directionalTextHelper.setHtml(html);
}
@Override
public void setHTML(@IsSafeHtml String html) {
directionalTextHelper.setHtml(html);
}
@Override
public void setHTML(SafeHtml html, Direction dir) {
directionalTextHelper.setHtml(html, dir);
}
@Override
public void setName(String name) {
getAnchorElement().setName(name);
}
@Override
public void setTabIndex(int index) {
getAnchorElement().setTabIndex(index);
}
/**
* Sets the anchor's target frame (the frame in which navigation will occur
* when the link is selected).
*
* @param target the target frame
*/
public void setTarget(String target) {
getAnchorElement().setTarget(target);
}
@Override
public void setText(String text) {
directionalTextHelper.setText(text);
}
@Override
public void setText(String text, Direction dir) {
directionalTextHelper.setText(text, dir);
}
@Override
public void setWordWrap(boolean wrap) {
getElement().getStyle().setWhiteSpace(wrap ? WhiteSpace.NORMAL : WhiteSpace.NOWRAP);
}
private AnchorElement getAnchorElement() {
return AnchorElement.as(getElement());
}
}