001package org.gwtbootstrap3.client.ui;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2016 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import com.google.gwt.dom.client.AnchorElement;
024import com.google.gwt.dom.client.Document;
025import com.google.gwt.event.dom.client.ClickEvent;
026import com.google.gwt.event.dom.client.ClickHandler;
027import com.google.gwt.event.dom.client.DoubleClickEvent;
028import com.google.gwt.event.dom.client.DoubleClickHandler;
029import com.google.gwt.event.dom.client.HasClickHandlers;
030import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
031import com.google.gwt.event.shared.HandlerRegistration;
032import com.google.gwt.user.client.DOM;
033import com.google.gwt.user.client.Event;
034import com.google.gwt.user.client.History;
035import com.google.gwt.user.client.ui.Focusable;
036import com.google.gwt.user.client.ui.HasEnabled;
037import com.google.gwt.user.client.ui.HasHTML;
038import com.google.gwt.user.client.ui.Widget;
039import org.gwtbootstrap3.client.ui.base.ComplexWidget;
040import org.gwtbootstrap3.client.ui.base.HasBadge;
041import org.gwtbootstrap3.client.ui.base.HasDataParent;
042import org.gwtbootstrap3.client.ui.base.HasDataTarget;
043import org.gwtbootstrap3.client.ui.base.HasDataToggle;
044import org.gwtbootstrap3.client.ui.base.HasHref;
045import org.gwtbootstrap3.client.ui.base.HasIcon;
046import org.gwtbootstrap3.client.ui.base.HasIconPosition;
047import org.gwtbootstrap3.client.ui.base.HasPull;
048import org.gwtbootstrap3.client.ui.base.HasTarget;
049import org.gwtbootstrap3.client.ui.base.HasTargetHistoryToken;
050import org.gwtbootstrap3.client.ui.base.mixin.AttributeMixin;
051import org.gwtbootstrap3.client.ui.base.mixin.DataParentMixin;
052import org.gwtbootstrap3.client.ui.base.mixin.DataTargetMixin;
053import org.gwtbootstrap3.client.ui.base.mixin.DataToggleMixin;
054import org.gwtbootstrap3.client.ui.base.mixin.EnabledMixin;
055import org.gwtbootstrap3.client.ui.base.mixin.FocusableMixin;
056import org.gwtbootstrap3.client.ui.base.mixin.IconTextMixin;
057import org.gwtbootstrap3.client.ui.base.mixin.PullMixin;
058import org.gwtbootstrap3.client.ui.constants.Attributes;
059import org.gwtbootstrap3.client.ui.constants.BadgePosition;
060import org.gwtbootstrap3.client.ui.constants.IconFlip;
061import org.gwtbootstrap3.client.ui.constants.IconPosition;
062import org.gwtbootstrap3.client.ui.constants.IconRotate;
063import org.gwtbootstrap3.client.ui.constants.IconSize;
064import org.gwtbootstrap3.client.ui.constants.IconType;
065import org.gwtbootstrap3.client.ui.constants.Pull;
066import org.gwtbootstrap3.client.ui.constants.Styles;
067import org.gwtbootstrap3.client.ui.constants.Toggle;
068
069import java.util.List;
070
071/**
072 * Anchor {@code <a>} element with text and optional icon.
073 *
074 * @author Sven Jacobs
075 * @author Joshua Godi
076 * @author Grant Slender
077 * @author Drew Spencer
078 */
079public class Anchor extends ComplexWidget implements HasEnabled, HasClickHandlers, HasDoubleClickHandlers, HasHref, HasDataToggle, HasDataParent,
080        HasTargetHistoryToken, HasHTML, HasIcon, HasIconPosition, Focusable, HasDataTarget, HasTarget, HasPull, HasBadge {
081
082    private final PullMixin<Anchor> pullMixin = new PullMixin<Anchor>(this);
083    private final DataToggleMixin<Anchor> toggleMixin = new DataToggleMixin<Anchor>(this);
084    private final DataParentMixin<Anchor> parentMixin = new DataParentMixin<Anchor>(this);
085    private final IconTextMixin<Anchor> iconTextMixin = new IconTextMixin<Anchor>(this);
086    private final DataTargetMixin<Anchor> targetMixin = new DataTargetMixin<Anchor>(this);
087    private final AttributeMixin<Anchor> attributeMixin = new AttributeMixin<Anchor>(this);
088    private final FocusableMixin<Anchor> focusableMixin = new FocusableMixin<Anchor>(this);
089    private final EnabledMixin<Anchor> enabledMixin = new EnabledMixin<Anchor>(this);
090    private String targetHistoryToken;
091
092    /**
093     * Creates an anchor widget with the desired HREF
094     *
095     * @param href href for the anchor
096     */
097    public Anchor(final String href) {
098        setElement(Document.get().createAnchorElement());
099        setHref(href);
100        iconTextMixin.addTextWidgetToParent();
101    }
102
103    /**
104     * Creates an achor widget with the desired HREF and text
105     *
106     * @param text text for the anchor
107     * @param href href for the anchor
108     */
109    public Anchor(final String text, final String href) {
110        this(href);
111        setText(text);
112    }
113
114    /**
115     * Creates a default anchor with a default href
116     */
117    public Anchor() {
118        this(EMPTY_HREF);
119    }
120
121    /**
122     * {@inheritDoc}
123     */
124    @Override
125    public HandlerRegistration addClickHandler(final ClickHandler handler) {
126        return addDomHandler(handler, ClickEvent.getType());
127    }
128
129    /**
130     * {@inheritDoc}
131     */
132    @Override
133    public HandlerRegistration addDoubleClickHandler(final DoubleClickHandler handler) {
134        return addDomHandler(handler, DoubleClickEvent.getType());
135    }
136
137    /**
138     * {@inheritDoc}
139     */
140    @Override
141    public void setText(final String text) {
142        iconTextMixin.setText(text);
143    }
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public String getText() {
150        return iconTextMixin.getText();
151    }
152
153    /**
154     * {@inheritDoc}
155     */
156    @Override
157    public void setIcon(final IconType iconType) {
158        iconTextMixin.setIcon(iconType);
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    public IconType getIcon() {
166        return iconTextMixin.getIcon();
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public void setIconPosition(final IconPosition iconPosition) {
174        iconTextMixin.setIconPosition(iconPosition);
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    @Override
181    public IconPosition getIconPosition() {
182        return iconTextMixin.getIconPosition();
183    }
184
185    /**
186     * {@inheritDoc}
187     */
188    @Override
189    public void setIconSize(final IconSize iconSize) {
190        iconTextMixin.setIconSize(iconSize);
191    }
192
193    /**
194     * {@inheritDoc}
195     */
196    @Override
197    public IconSize getIconSize() {
198        return iconTextMixin.getIconSize();
199    }
200
201    /**
202     * {@inheritDoc}
203     */
204    @Override
205    public void setIconFlip(final IconFlip iconFlip) {
206        iconTextMixin.setIconFlip(iconFlip);
207    }
208
209    /**
210     * {@inheritDoc}
211     */
212    @Override
213    public IconFlip getIconFlip() {
214        return iconTextMixin.getIconFlip();
215    }
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public void setIconRotate(final IconRotate iconRotate) {
222        iconTextMixin.setIconRotate(iconRotate);
223    }
224
225    /**
226     * {@inheritDoc}
227     */
228    @Override
229    public IconRotate getIconRotate() {
230        return iconTextMixin.getIconRotate();
231    }
232
233    /**
234     * {@inheritDoc}
235     */
236    @Override
237    public void setIconBordered(final boolean iconBordered) {
238        iconTextMixin.setIconBordered(iconBordered);
239    }
240
241    /**
242     * {@inheritDoc}
243     */
244    @Override
245    public boolean isIconBordered() {
246        return iconTextMixin.isIconBordered();
247    }
248
249    /**
250     * {@inheritDoc}
251     */
252    @Override
253    public void setIconInverse(final boolean iconInverse) {
254        iconTextMixin.setIconInverse(iconInverse);
255    }
256
257    /**
258     * {@inheritDoc}
259     */
260    @Override
261    public boolean isIconInverse() {
262        return iconTextMixin.isIconInverse();
263    }
264
265    /**
266     * {@inheritDoc}
267     */
268    @Override
269    public void setIconSpin(final boolean iconSpin) {
270        iconTextMixin.setIconSpin(iconSpin);
271    }
272
273    /**
274     * {@inheritDoc}
275     */
276    @Override
277    public boolean isIconSpin() {
278        return iconTextMixin.isIconSpin();
279    }
280
281    /**
282     * {@inheritDoc}
283     */
284    @Override
285    public void setIconPulse(boolean iconPulse) {
286        iconTextMixin.setIconPulse(iconPulse);
287    }
288
289    /**
290     * {@inheritDoc}
291     */
292    @Override
293    public boolean isIconPulse() {
294        return iconTextMixin.isIconPulse();
295    }
296
297    /**
298     * {@inheritDoc}
299     */
300    @Override
301    public void setIconFixedWidth(final boolean iconFixedWidth) {
302        iconTextMixin.setIconFixedWidth(iconFixedWidth);
303    }
304
305    /**
306     * {@inheritDoc}
307     */
308    @Override
309    public boolean isIconFixedWidth() {
310        return iconTextMixin.isIconFixedWidth();
311    }
312
313    /**
314     * {@inheritDoc}
315     */
316    @Override
317    public void setHref(final String href) {
318        AnchorElement.as(getElement()).setHref(href);
319    }
320
321    /**
322     * {@inheritDoc}
323     */
324    @Override
325    public String getHref() {
326        return AnchorElement.as(getElement()).getHref();
327    }
328
329    /**
330     * {@inheritDoc}
331     */
332    @Override
333    public void setTargetHistoryToken(final String targetHistoryToken) {
334        this.targetHistoryToken = targetHistoryToken;
335        final String hash = History.encodeHistoryToken(targetHistoryToken);
336        setHref("#" + hash);
337    }
338
339    /**
340     * {@inheritDoc}
341     */
342    @Override
343    public String getTargetHistoryToken() {
344        return targetHistoryToken;
345    }
346
347    /**
348     * {@inheritDoc}
349     */
350    @Override
351    public void setDataParent(final String dataParent) {
352        parentMixin.setDataParent(dataParent);
353    }
354
355    /**
356     * {@inheritDoc}
357     */
358    @Override
359    public String getDataParent() {
360        return parentMixin.getDataParent();
361    }
362
363    /**
364     * {@inheritDoc}
365     */
366    @Override
367    public void setDataToggle(final Toggle toggle) {
368        toggleMixin.setDataToggle(toggle);
369    }
370
371    /**
372     * {@inheritDoc}
373     */
374    @Override
375    public Toggle getDataToggle() {
376        return toggleMixin.getDataToggle();
377    }
378
379    /**
380     * {@inheritDoc}
381     */
382    @Override
383    public int getTabIndex() {
384        return focusableMixin.getTabIndex();
385    }
386
387    /**
388     * {@inheritDoc}
389     */
390    @Override
391    public void setTabIndex(final int index) {
392        focusableMixin.setTabIndex(index);
393    }
394
395    /**
396     * {@inheritDoc}
397     */
398    @Override
399    public void setAccessKey(final char key) {
400        focusableMixin.setAccessKey(key);
401    }
402
403    /**
404     * {@inheritDoc}
405     */
406    @Override
407    public void setFocus(final boolean focused) {
408        focusableMixin.setFocus(focused);
409    }
410
411    /**
412     * {@inheritDoc}
413     */
414    @Override
415    public String getHTML() {
416        return getElement().getInnerHTML();
417    }
418
419    /**
420     * {@inheritDoc}
421     */
422    @Override
423    public void setHTML(final String html) {
424        getElement().setInnerHTML(html);
425    }
426
427    @Override
428    public void setDataTargetWidgets(final List<Widget> widgets) {
429        targetMixin.setDataTargetWidgets(widgets);
430    }
431
432    @Override
433    public void setDataTargetWidget(final Widget widget) {
434        targetMixin.setDataTargetWidget(widget);
435    }
436
437    /**
438     * {@inheritDoc}
439     */
440    @Override
441    public void setDataTarget(final String dataTarget) {
442        targetMixin.setDataTarget(dataTarget);
443    }
444
445    /**
446     * {@inheritDoc}
447     */
448    @Override
449    public String getDataTarget() {
450        return targetMixin.getDataTarget();
451    }
452
453    /**
454     * {@inheritDoc}
455     */
456    @Override
457    public void setTarget(final String target) {
458        attributeMixin.setAttribute(Attributes.TARGET, target);
459    }
460
461    /**
462     * {@inheritDoc}
463     */
464    @Override
465    public String getTarget() {
466        return attributeMixin.getAttribute(Attributes.TARGET);
467    }
468
469    /**
470     * {@inheritDoc}
471     */
472    @Override
473    public void setPull(final Pull pull) {
474        pullMixin.setPull(pull);
475    }
476
477    /**
478     * {@inheritDoc}
479     */
480    @Override
481    public Pull getPull() {
482        return pullMixin.getPull();
483    }
484
485    /**
486     * {@inheritDoc}
487     */
488    @Override
489    public boolean isEnabled() {
490        return enabledMixin.isEnabled();
491    }
492
493    /**
494     * {@inheritDoc}
495     */
496    @Override
497    public void setEnabled(final boolean enabled) {
498        enabledMixin.setEnabled(enabled);
499    }
500
501    @Override
502    public void setBadgeText(String badgeText) {
503        iconTextMixin.setBadgeText(badgeText);
504    }
505
506    @Override
507    public String getBadgeText() {
508        return iconTextMixin.getBadgeText();
509    }
510
511    @Override
512    public void setBadgePosition(BadgePosition badgePosition) {
513        iconTextMixin.setBadgePosition(badgePosition);
514    }
515
516    @Override
517    public BadgePosition getBadgePosition() {
518        return iconTextMixin.getBadgePosition();
519    }
520
521    /**
522     * {@inheritDoc}
523     */
524    @Override
525    protected void onAttach() {
526        super.onAttach();
527
528        // Adding styles to the heading depending on the parent
529        if (getParent() != null) {
530            if (getParent() instanceof Alert) {
531                addStyleName(Styles.ALERT_LINK);
532            }
533        }
534    }
535
536    /**
537     * We override this because the <a></a> tag doesn't support the disabled property. So on clicks and focus, if disabled then ignore
538     *
539     * @param event dom event
540     */
541    @Override
542    public void onBrowserEvent(final Event event) {
543        switch (DOM.eventGetType(event)) {
544            case Event.ONDBLCLICK:
545            case Event.ONFOCUS:
546            case Event.ONCLICK:
547                if (!isEnabled()) {
548                    return;
549                }
550                break;
551        }
552        super.onBrowserEvent(event);
553    }
554
555}