001package arez.dom;
002
003import arez.Disposable;
004import java.util.Objects;
005import javax.annotation.Nonnull;
006import jsinterop.annotations.JsPackage;
007import jsinterop.annotations.JsProperty;
008import jsinterop.annotations.JsType;
009
010/**
011 * Exposes {@code document.visibilityState} as an observable property for specified documents.
012 *
013 * <p>A very simple example</p>
014 * <pre>{@code
015 * import arez.Arez;
016 * import arez.dom.DocumentVisibility;
017 * import com.google.gwt.core.client.EntryPoint;
018 *
019 * public class DocumentVisibilityExample
020 *   implements EntryPoint
021 * {
022 *   public void onModuleLoad()
023 *   {
024 *     final DocumentVisibility v = DocumentVisibility.create();
025 *     Arez.context().observer( () -> System.out.println( "Document Visibility: " + v.getVisibility() ) );
026 *   }
027 * }
028 * }</pre>
029 */
030public final class DocumentVisibility
031  implements Disposable
032{
033  /**
034   * Minimal facade for the browser document visibility API.
035   */
036  @JsType( isNative = true, name = "Document", namespace = JsPackage.GLOBAL )
037  public static class Document
038    extends EventDrivenValue.EventTarget
039  {
040    protected Document()
041    {
042    }
043
044    @JsProperty( name = "visibilityState" )
045    native String visibilityState();
046  }
047
048  /**
049   * The visibility state of the document.
050   */
051  public enum Visibility
052  {
053    /**
054     * The page content may be at least partially visible. In practice this means that the page is the foreground tab of a non-minimized window.
055     */
056    VISIBLE,
057    /**
058     * The page content is not visible to the user. In practice this means that the document is either a background tab or part of a minimized window, or the OS screen lock is active.
059     */
060    HIDDEN,
061    /**
062     * The page content is being prerendered and is not visible to the user (considered hidden for purposes of document.hidden). The document may start in this state, but will never transition to it from another value. Note: browser support is optional.
063     */
064    PRERENDER
065  }
066
067  /**
068   * The underlying component performing the monitoring.
069   */
070  private final EventDrivenValue<Document, String> _value;
071
072  /**
073   * Create component monitoring the default document.
074   *
075   * @return the new component.
076   */
077  @Nonnull
078  public static DocumentVisibility create()
079  {
080    return create( document() );
081  }
082
083  /**
084   * Create component monitoring specific document.
085   *
086   * @param document the document.
087   * @return the new component.
088   */
089  @Nonnull
090  public static DocumentVisibility create( @Nonnull final Document document )
091  {
092    return new DocumentVisibility( Objects.requireNonNull( document ) );
093  }
094
095  private DocumentVisibility( @Nonnull final Document document )
096  {
097    _value = EventDrivenValue.create( document, "visibilitychange", Document::visibilityState );
098  }
099
100  /**
101   * Return the document that monitoring visibility state.
102   *
103   * @return the document.
104   */
105  @Nonnull
106  public Document getDocument()
107  {
108    return _value.getSource();
109  }
110
111  /**
112   * Change the document that is having visibility state monitored.
113   *
114   * @param document the new document.
115   */
116  public void setDocument( @Nonnull final Document document )
117  {
118    _value.setSource( document );
119  }
120
121  /**
122   * Return the visibility state of the document as an enum.
123   *
124   * @return the visibility state as an enum.
125   */
126  @Nonnull
127  public Visibility getVisibility()
128  {
129    return asVisibility( getVisibilityState() );
130  }
131
132  /**
133   * Return the visibility state of the document as a string.
134   *
135   * @return the visibility state as a string.
136   */
137  @Nonnull
138  public String getVisibilityState()
139  {
140    return _value.getValue();
141  }
142
143  /**
144   * Return true if visibility state is "visible".
145   *
146   * @return true if visibility state is "visible".
147   */
148  public boolean isVisible()
149  {
150    return "visible".equals( getVisibilityState() );
151  }
152
153  /**
154   * Return true if visibility state is "hidden".
155   *
156   * @return true if visibility state is "hidden".
157   */
158  public boolean isHidden()
159  {
160    return "hidden".equals( getVisibilityState() );
161  }
162
163  @Override
164  public void dispose()
165  {
166    Disposable.dispose( _value );
167  }
168
169  @Override
170  public boolean isDisposed()
171  {
172    return Disposable.isDisposed( _value );
173  }
174
175  /**
176   * Convert the visibility state as an enum.
177   *
178   * @param state the state.
179   * @return the visibility enum.
180   */
181  @Nonnull
182  private Visibility asVisibility( @Nonnull final String state )
183  {
184    if ( "visible".equals( state ) )
185    {
186      return Visibility.VISIBLE;
187    }
188    else if ( "hidden".equals( state ) )
189    {
190      return Visibility.HIDDEN;
191    }
192    else
193    {
194      assert "prerender".equals( state );
195      return Visibility.PRERENDER;
196    }
197  }
198
199  @JsProperty( name = "document", namespace = JsPackage.GLOBAL )
200  private static native Document document();
201}