001package org.gwtbootstrap3.client.ui;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 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 org.gwtbootstrap3.client.ui.base.HasId;
024
025import com.google.gwt.dom.client.Document;
026import com.google.gwt.dom.client.Element;
027import com.google.gwt.user.client.ui.UIObject;
028
029/**
030 * A ScrollSpy handles scrolling events (typically on {@code <body>}) and
031 * updates "active" states of a {@link Nav} accordingly.
032 * <h3>Note</h3> The target element <strong>must</strong> be a parent element of a
033 * {@code <ul class="nav">} or {@link Nav}.
034 * <p/>
035 * Also the ScrollSpy must be initialized when the target element has been added
036 * to the DOM, for example in {@link com.google.gwt.user.client.ui.Widget#onAttach()}.
037 * <pre>{@code
038 * &#64;Override
039 * protected void onAttach() {
040 *     super.onAttach();
041 *     ScrollSpy.scrollSpy(this);
042 * }}</pre>
043 * <p/>
044 * See Bootstrap's <a
045 * href="http://getbootstrap.com/javascript/#scrollspy">documentation</a>.
046 *
047 * @author Sven Jacobs
048 */
049public class ScrollSpy {
050
051    private final Element spyOn;
052    private final String target;
053
054    /**
055     * Attaches ScrollSpy to document {@code <body>} and with the specified
056     * target CSS selector.
057     *
058     * @param selector CSS selector for target element
059     * @return ScrollSpy
060     */
061    public static ScrollSpy scrollSpy(final String selector) {
062        return new ScrollSpy((Element) Document.get().getBody().cast(), selector);
063    }
064
065    /**
066     * Attaches ScrollSpy to document {@code <body>} and with the specified
067     * target element that <strong>must</strong> have an ID.
068     *
069     * @param target Target element having an ID
070     * @return ScrollSpy
071     */
072    public static ScrollSpy scrollSpy(final HasId target) {
073        return new ScrollSpy((Element) Document.get().getBody().cast(), target);
074    }
075
076    /**
077     * Attaches ScrollSpy to specified object with specified target selector.
078     *
079     * @param spyOn    Spy on this object
080     * @param selector CSS selector of target element
081     * @return ScrollSpy
082     */
083    public static ScrollSpy scrollSpy(final UIObject spyOn, final String selector) {
084        return new ScrollSpy(spyOn.getElement(), selector);
085    }
086
087    /**
088     * Attaches ScrollSpy to specified object with specified target element.
089     *
090     * @param spyOn  Spy on this object
091     * @param target Target element having an ID
092     * @return ScrollSpy
093     */
094    public static ScrollSpy scrollSpy(final UIObject spyOn, final HasId target) {
095        return new ScrollSpy(spyOn.getElement(), target);
096    }
097
098    /**
099     * Attaches ScrollSpy to specified element with specified target selector.
100     *
101     * @param spyOn    Spy on this element
102     * @param selector CSS selector of target element
103     * @return ScrollSpy
104     */
105    public static ScrollSpy scrollSpy(final Element spyOn, final String selector) {
106        return new ScrollSpy(spyOn, selector);
107    }
108
109    private ScrollSpy(final Element spyOn, final String selector) {
110
111        this.spyOn = spyOn;
112        this.target = selector;
113
114        init(this.spyOn, this.target);
115    }
116
117    private ScrollSpy(final Element spyOn, final HasId target) {
118
119        final String id = target.getId();
120
121        if (id == null || id.isEmpty()) {
122            throw new IllegalArgumentException("ScrollSpy target element must have id");
123        }
124
125        this.spyOn = spyOn;
126        this.target = "#" + id;
127
128        init(this.spyOn, this.target);
129    }
130
131    /**
132     * Refresh ScrollSpy after elements have been added to or removed from the
133     * DOM.
134     */
135    public void refresh() {
136        refresh(spyOn);
137    }
138
139    private native void init(final Element e, final String target) /*-{
140        var $e = $wnd.jQuery(e);
141
142        $e.scrollspy({
143            target: target
144        });
145    }-*/;
146
147    private native void refresh(final Element e) /*-{
148        $wnd.jQuery(e).scrollspy('refresh');
149    }-*/;
150}