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}