opal-browser 0.2.0.beta1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (218) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/build.yml +95 -0
  3. data/.gitignore +3 -0
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +17 -3
  6. data/LICENSE +2 -1
  7. data/README.md +183 -52
  8. data/Rakefile +29 -1
  9. data/config.ru +20 -3
  10. data/docs/polyfills.md +24 -0
  11. data/examples/2048/Gemfile +6 -0
  12. data/examples/2048/README.md +13 -0
  13. data/examples/2048/app/application.rb +169 -0
  14. data/examples/2048/config.ru +9 -0
  15. data/examples/canvas/Gemfile +6 -0
  16. data/examples/canvas/README.md +9 -0
  17. data/examples/canvas/app/application.rb +55 -0
  18. data/examples/canvas/config.ru +9 -0
  19. data/examples/component/Gemfile +6 -0
  20. data/examples/component/README.md +10 -0
  21. data/examples/component/app/application.rb +66 -0
  22. data/examples/component/config.ru +9 -0
  23. data/examples/integrations/README.md +24 -0
  24. data/examples/integrations/dynamic-rack-opal-sprockets-server/Gemfile +6 -0
  25. data/examples/integrations/dynamic-rack-opal-sprockets-server/README.md +16 -0
  26. data/examples/integrations/dynamic-rack-opal-sprockets-server/app/application.rb +6 -0
  27. data/examples/integrations/dynamic-rack-opal-sprockets-server/config.ru +9 -0
  28. data/examples/integrations/dynamic-roda-roda-sprockets/.gitignore +1 -0
  29. data/examples/integrations/dynamic-roda-roda-sprockets/Gemfile +7 -0
  30. data/examples/integrations/dynamic-roda-roda-sprockets/README.md +22 -0
  31. data/examples/integrations/dynamic-roda-roda-sprockets/Rakefile +4 -0
  32. data/examples/integrations/dynamic-roda-roda-sprockets/app/application.rb +6 -0
  33. data/examples/integrations/dynamic-roda-roda-sprockets/app.rb +32 -0
  34. data/examples/integrations/dynamic-roda-roda-sprockets/config.ru +3 -0
  35. data/examples/integrations/dynamic-roda-tilt/.gitignore +1 -0
  36. data/examples/integrations/dynamic-roda-tilt/Gemfile +8 -0
  37. data/examples/integrations/dynamic-roda-tilt/README.md +17 -0
  38. data/examples/integrations/dynamic-roda-tilt/Rakefile +6 -0
  39. data/examples/integrations/dynamic-roda-tilt/app/application.rb +6 -0
  40. data/examples/integrations/dynamic-roda-tilt/app.rb +50 -0
  41. data/examples/integrations/dynamic-roda-tilt/config.ru +3 -0
  42. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/Gemfile +7 -0
  43. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/README.md +16 -0
  44. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/app/application.rb +6 -0
  45. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/config.ru +29 -0
  46. data/examples/integrations/static-bash/.gitignore +2 -0
  47. data/examples/integrations/static-bash/Gemfile +3 -0
  48. data/examples/integrations/static-bash/README.md +8 -0
  49. data/examples/integrations/static-bash/app/application.rb +6 -0
  50. data/examples/integrations/static-bash/build.sh +4 -0
  51. data/examples/integrations/static-bash/index.html +10 -0
  52. data/examples/integrations/static-bash-opal-parser/.gitignore +3 -0
  53. data/examples/integrations/static-bash-opal-parser/Gemfile +3 -0
  54. data/examples/integrations/static-bash-opal-parser/README.md +10 -0
  55. data/examples/integrations/static-bash-opal-parser/build.sh +4 -0
  56. data/examples/integrations/static-bash-opal-parser/index.html +19 -0
  57. data/examples/integrations/static-rake/.gitignore +1 -0
  58. data/examples/integrations/static-rake/Gemfile +4 -0
  59. data/examples/integrations/static-rake/README.md +7 -0
  60. data/examples/integrations/static-rake/Rakefile +10 -0
  61. data/examples/integrations/static-rake/app/application.rb +6 -0
  62. data/examples/integrations/static-rake/index.html +9 -0
  63. data/examples/integrations/static-rake-guard/.gitignore +1 -0
  64. data/examples/integrations/static-rake-guard/Gemfile +6 -0
  65. data/examples/integrations/static-rake-guard/Guardfile +3 -0
  66. data/examples/integrations/static-rake-guard/README.md +10 -0
  67. data/examples/integrations/static-rake-guard/Rakefile +10 -0
  68. data/examples/integrations/static-rake-guard/app/application.rb +6 -0
  69. data/examples/integrations/static-rake-guard/index.html +9 -0
  70. data/examples/svg/.gitignore +1 -0
  71. data/examples/svg/Gemfile +4 -0
  72. data/examples/svg/README.md +7 -0
  73. data/examples/svg/Rakefile +10 -0
  74. data/examples/svg/app/application.rb +11 -0
  75. data/examples/svg/index.html +17 -0
  76. data/examples/svg/index.svg +6 -0
  77. data/index.html.erb +8 -6
  78. data/lib/opal-browser.rb +1 -0
  79. data/opal/browser/animation_frame.rb +26 -1
  80. data/opal/browser/audio/node.rb +121 -0
  81. data/opal/browser/audio/param_schedule.rb +43 -0
  82. data/opal/browser/audio.rb +66 -0
  83. data/opal/browser/blob.rb +94 -0
  84. data/opal/browser/canvas/data.rb +1 -11
  85. data/opal/browser/canvas/gradient.rb +1 -11
  86. data/opal/browser/canvas/style.rb +3 -11
  87. data/opal/browser/canvas/text.rb +1 -11
  88. data/opal/browser/canvas.rb +17 -13
  89. data/opal/browser/console.rb +3 -1
  90. data/opal/browser/cookies.rb +78 -42
  91. data/opal/browser/crypto.rb +79 -0
  92. data/opal/browser/css/declaration.rb +1 -1
  93. data/opal/browser/css/rule.rb +1 -1
  94. data/opal/browser/css/style_sheet.rb +2 -2
  95. data/opal/browser/css.rb +23 -7
  96. data/opal/browser/database/sql.rb +193 -0
  97. data/opal/browser/delay.rb +41 -7
  98. data/opal/browser/dom/attribute.rb +13 -12
  99. data/opal/browser/dom/builder.rb +31 -17
  100. data/opal/browser/dom/document.rb +174 -42
  101. data/opal/browser/dom/document_fragment.rb +18 -0
  102. data/opal/browser/dom/document_or_shadow_root.rb +19 -0
  103. data/opal/browser/dom/element/attributes.rb +111 -0
  104. data/opal/browser/dom/element/button.rb +31 -0
  105. data/opal/browser/dom/element/custom.rb +177 -0
  106. data/opal/browser/dom/element/data.rb +82 -0
  107. data/opal/browser/dom/element/editable.rb +47 -0
  108. data/opal/browser/dom/element/form.rb +38 -0
  109. data/opal/browser/dom/element/iframe.rb +37 -0
  110. data/opal/browser/dom/element/image.rb +2 -0
  111. data/opal/browser/dom/element/input.rb +48 -1
  112. data/opal/browser/dom/element/media.rb +17 -0
  113. data/opal/browser/dom/element/offset.rb +5 -0
  114. data/opal/browser/dom/element/position.rb +11 -2
  115. data/opal/browser/dom/element/scroll.rb +123 -24
  116. data/opal/browser/dom/element/select.rb +42 -0
  117. data/opal/browser/dom/element/size.rb +17 -0
  118. data/opal/browser/dom/element/template.rb +11 -0
  119. data/opal/browser/dom/element/textarea.rb +26 -0
  120. data/opal/browser/dom/element.rb +468 -238
  121. data/opal/browser/dom/mutation_observer.rb +4 -4
  122. data/opal/browser/dom/node.rb +142 -60
  123. data/opal/browser/dom/node_set.rb +73 -44
  124. data/opal/browser/dom/shadow_root.rb +12 -0
  125. data/opal/browser/dom/text.rb +2 -2
  126. data/opal/browser/dom.rb +40 -16
  127. data/opal/browser/effects.rb +180 -3
  128. data/opal/browser/event/all.rb +26 -0
  129. data/opal/browser/{dom/event → event}/animation.rb +4 -2
  130. data/opal/browser/{dom/event → event}/audio_processing.rb +4 -2
  131. data/opal/browser/{dom/event → event}/base.rb +98 -9
  132. data/opal/browser/{dom/event → event}/before_unload.rb +4 -2
  133. data/opal/browser/{dom/event → event}/clipboard.rb +11 -2
  134. data/opal/browser/{dom/event → event}/close.rb +4 -2
  135. data/opal/browser/{dom/event → event}/composition.rb +4 -2
  136. data/opal/browser/{dom/event → event}/custom.rb +3 -3
  137. data/opal/browser/event/data_transfer.rb +95 -0
  138. data/opal/browser/{dom/event → event}/device_light.rb +4 -2
  139. data/opal/browser/{dom/event → event}/device_motion.rb +4 -2
  140. data/opal/browser/{dom/event → event}/device_orientation.rb +4 -2
  141. data/opal/browser/{dom/event → event}/device_proximity.rb +4 -2
  142. data/opal/browser/{dom/event → event}/drag.rb +11 -7
  143. data/opal/browser/{dom/event → event}/focus.rb +4 -2
  144. data/opal/browser/{dom/event → event}/gamepad.rb +5 -3
  145. data/opal/browser/{dom/event → event}/hash_change.rb +4 -2
  146. data/opal/browser/{dom/event → event}/keyboard.rb +16 -3
  147. data/opal/browser/{dom/event → event}/message.rb +4 -2
  148. data/opal/browser/{dom/event → event}/mouse.rb +12 -8
  149. data/opal/browser/{dom/event → event}/page_transition.rb +4 -2
  150. data/opal/browser/{dom/event → event}/pop_state.rb +4 -2
  151. data/opal/browser/{dom/event → event}/progress.rb +4 -2
  152. data/opal/browser/{dom/event → event}/sensor.rb +4 -2
  153. data/opal/browser/{dom/event → event}/storage.rb +4 -2
  154. data/opal/browser/{dom/event → event}/touch.rb +4 -2
  155. data/opal/browser/{dom/event → event}/ui.rb +2 -2
  156. data/opal/browser/{dom/event → event}/wheel.rb +4 -2
  157. data/opal/browser/event.rb +163 -0
  158. data/opal/browser/event_source.rb +2 -2
  159. data/opal/browser/form_data.rb +225 -0
  160. data/opal/browser/history.rb +4 -8
  161. data/opal/browser/http/binary.rb +1 -0
  162. data/opal/browser/http/headers.rb +16 -2
  163. data/opal/browser/http/request.rb +46 -48
  164. data/opal/browser/http/response.rb +5 -1
  165. data/opal/browser/http.rb +25 -2
  166. data/opal/browser/immediate.rb +9 -5
  167. data/opal/browser/interval.rb +34 -11
  168. data/opal/browser/location.rb +7 -1
  169. data/opal/browser/navigator.rb +127 -7
  170. data/opal/browser/polyfill/visual_viewport.rb +216 -0
  171. data/opal/browser/screen.rb +3 -3
  172. data/opal/browser/setup/base.rb +6 -0
  173. data/opal/browser/setup/full.rb +13 -0
  174. data/opal/browser/setup/large.rb +17 -0
  175. data/opal/browser/setup/mini.rb +8 -0
  176. data/opal/browser/setup/traditional.rb +10 -0
  177. data/opal/browser/socket.rb +8 -4
  178. data/opal/browser/storage.rb +53 -35
  179. data/opal/browser/support.rb +72 -5
  180. data/opal/browser/utils.rb +94 -14
  181. data/opal/browser/version.rb +1 -1
  182. data/opal/browser/visual_viewport.rb +39 -0
  183. data/opal/browser/window/size.rb +31 -3
  184. data/opal/browser/window/view.rb +15 -0
  185. data/opal/browser/window.rb +46 -25
  186. data/opal/browser.rb +1 -10
  187. data/opal/opal-browser.rb +1 -0
  188. data/opal-browser.gemspec +3 -3
  189. data/spec/database/sql_spec.rb +139 -0
  190. data/spec/delay_spec.rb +41 -0
  191. data/spec/dom/attribute_spec.rb +49 -0
  192. data/spec/dom/builder_spec.rb +25 -8
  193. data/spec/dom/document_spec.rb +22 -0
  194. data/spec/dom/element/attributes_spec.rb +52 -0
  195. data/spec/dom/element/custom_spec.rb +106 -0
  196. data/spec/dom/element/subclass_spec.rb +144 -0
  197. data/spec/dom/element_spec.rb +181 -4
  198. data/spec/dom/mutation_observer_spec.rb +12 -8
  199. data/spec/dom/node_set_spec.rb +44 -0
  200. data/spec/dom/node_spec.rb +48 -0
  201. data/spec/dom_spec.rb +8 -0
  202. data/spec/event_source_spec.rb +15 -12
  203. data/spec/{dom/event_spec.rb → event_spec.rb} +44 -15
  204. data/spec/history_spec.rb +23 -19
  205. data/spec/http_spec.rb +19 -31
  206. data/spec/immediate_spec.rb +5 -4
  207. data/spec/interval_spec.rb +59 -0
  208. data/spec/native_cached_wrapper_spec.rb +46 -0
  209. data/spec/runner.rb +62 -69
  210. data/spec/socket_spec.rb +16 -12
  211. data/spec/spec_helper.rb +2 -5
  212. data/spec/spec_helper_promise.rb.erb +25 -0
  213. data/spec/storage_spec.rb +1 -1
  214. metadata +172 -50
  215. data/.travis.yml +0 -60
  216. data/opal/browser/dom/event.rb +0 -253
  217. data/opal/browser/http/parameters.rb +0 -8
  218. data/opal/browser/window/scroll.rb +0 -59
@@ -19,14 +19,11 @@ class Interval
19
19
  @block = block
20
20
 
21
21
  @aborted = false
22
- @stopped = true
23
-
24
- start
25
22
  end
26
23
 
27
24
  # Check if the interval has been stopped.
28
25
  def stopped?
29
- @stopped
26
+ @id.nil?
30
27
  end
31
28
 
32
29
  # Check if the interval has been aborted.
@@ -44,6 +41,8 @@ class Interval
44
41
 
45
42
  # Stop the interval, it will be possible to start it again.
46
43
  def stop
44
+ return if stopped?
45
+
47
46
  `#@window.clearInterval(#@id)`
48
47
 
49
48
  @stopped = true
@@ -53,10 +52,14 @@ class Interval
53
52
  # Start the interval if it has been stopped.
54
53
  def start
55
54
  raise "the interval has been aborted" if aborted?
56
-
57
55
  return unless stopped?
58
56
 
59
- @id = `#@window.setInterval(#{@block.to_n}, #@every * 1000)`
57
+ @id = `#@window.setInterval(#@block, #@every * 1000)`
58
+ end
59
+
60
+ # Call the [Interval] block.
61
+ def call
62
+ @block.call
60
63
  end
61
64
  end
62
65
 
@@ -67,22 +70,42 @@ class Window
67
70
  #
68
71
  # @return [Interval] the object representing the interval
69
72
  def every(time, &block)
73
+ Interval.new(@native, time, &block).tap(&:start)
74
+ end
75
+
76
+ # Execute the block every given seconds, you have to call [#start] on it
77
+ # yourself.
78
+ #
79
+ # @param time [Float] the seconds between every call
80
+ #
81
+ # @return [Interval] the object representing the interval
82
+ def every!(time, &block)
70
83
  Interval.new(@native, time, &block)
71
84
  end
72
85
  end
73
86
 
74
87
  end
75
88
 
89
+ module Kernel
90
+ # (see Browser::Window#every)
91
+ def every(time, &block)
92
+ $window.every(time, &block)
93
+ end
94
+
95
+ # (see Browser::Window#every!)
96
+ def every!(time, &block)
97
+ $window.every!(time, &block)
98
+ end
99
+ end
100
+
76
101
  class Proc
77
102
  # (see Browser::Window#every)
78
103
  def every(time)
79
104
  $window.every(time, &self)
80
105
  end
81
- end
82
106
 
83
- module Kernel
84
- # (see Browser::Window#every)
85
- def every(time, &block)
86
- $window.every(time, &block)
107
+ # (see Browser::Window#every!)
108
+ def every!(time)
109
+ $window.every!(time, &self)
87
110
  end
88
111
  end
@@ -4,7 +4,7 @@ module Browser
4
4
  #
5
5
  # @see https://developer.mozilla.org/en-US/docs/Web/API/Location
6
6
  class Location
7
- include Native
7
+ include Browser::NativeCachedWrapper
8
8
 
9
9
  # Change the location.
10
10
  #
@@ -66,6 +66,12 @@ class Location
66
66
  # @return [String] the query part of the location URI
67
67
  alias_native :query, :search
68
68
  alias_native :query=, :search=
69
+
70
+ # Returns the full path of the location URI, including
71
+ # the query string and fragment, eg. /site?a=b#c
72
+ def full_path
73
+ path + query + fragment
74
+ end
69
75
  end
70
76
 
71
77
  class Window
@@ -4,7 +4,7 @@ module Browser
4
4
  #
5
5
  # @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator
6
6
  class Navigator
7
- include Native
7
+ include Browser::NativeCachedWrapper
8
8
 
9
9
  Version = Struct.new(:major, :minor, :build)
10
10
  Product = Struct.new(:name, :version)
@@ -12,7 +12,7 @@ class Navigator
12
12
 
13
13
  # Representation of a MIME type.
14
14
  class MimeType
15
- include Native
15
+ include Browser::NativeCachedWrapper
16
16
 
17
17
  # @!attribute [r] plugin
18
18
  # @return [Plugin] the plugin for the MIME type
@@ -62,6 +62,27 @@ class Navigator
62
62
  alias_native :version
63
63
  end
64
64
 
65
+ # Representation for the array of plugins.
66
+ #
67
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/NavigatorPlugins
68
+ class Plugins < Native::Array
69
+ def initialize(plugins)
70
+ super plugins do |p|
71
+ Plugin.new(p)
72
+ end
73
+ end
74
+
75
+ # Reload all browser plugins.
76
+ def refresh
77
+ `#@native.refresh(false)`
78
+ end
79
+
80
+ # Reload all browser plugins reloading pages that contain `<embed>`s.
81
+ def refresh!
82
+ `#@native.refresh(true)`
83
+ end
84
+ end
85
+
65
86
  # @!attribute [r] code
66
87
  # @return [String] the browser code name
67
88
  alias_native :code, :appCodeName
@@ -112,11 +133,9 @@ class Navigator
112
133
  alias_native :platform
113
134
 
114
135
  # @!attribute [r] plugins
115
- # @return [Native::Array<Plugin>] the enabled plugins
136
+ # @return [Plugins] the enabled plugins
116
137
  def plugins
117
- Native::Array.new `#@native.plugins` do |p|
118
- Plugin.new(p)
119
- end
138
+ Plugins.new(`#@native.plugins`)
120
139
  end
121
140
 
122
141
  # @!attribute [r] product
@@ -141,13 +160,114 @@ class Navigator
141
160
  rescue
142
161
  false
143
162
  end
163
+
164
+ # Representation of user location based on Geolocation API
165
+ #
166
+ # Example usage:
167
+ # ```
168
+ # $window.navigator.geolocate.then do |pos|
169
+ # puts "#{pos.coords.latitude}, #{pos.coords.longitude}, #{pos.coords.accuracy}"
170
+ # end
171
+ # ```
172
+ #
173
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Position
174
+ class Position
175
+ include Browser::NativeCachedWrapper
176
+
177
+ class Coords
178
+ include Native::Wrapper
179
+
180
+ # @!attribute [r] latitude
181
+ alias_native :latitude
182
+ # @!attribute [r] longitude
183
+ alias_native :longitude
184
+ # @!attribute [r] altitude
185
+ alias_native :altitude
186
+ # @!attribute [r] accuracy
187
+ alias_native :accuracy
188
+ # @!attribute [r] altitude_accuracy
189
+ alias_native :altitude_accuracy, :altitudeAccuracy
190
+ # @!attribute [r] heading
191
+ alias_native :heading
192
+ # @!attribute [r] speed
193
+ alias_native :speed
194
+ end
195
+
196
+ # @!attribute [r] timestamp
197
+ alias_native :timestamp
198
+
199
+ def coords
200
+ @coords ||= Coords.new(`#@native.coords`)
201
+ end
202
+ end
203
+
204
+ # Geolocates the user once
205
+ #
206
+ # @return [Promise] promise that resolves to the {Position} object
207
+ def geolocate(max_age: 0, timeout: Float::INFINITY, high_accuracy: false)
208
+ promise = Promise.new
209
+ succ = proc { |i| promise.resolve(Position.new(i)) }
210
+ fail = proc { |i| promise.reject(Native(i)) }
211
+ opts = {maxAge: max_age, timeout: timeout, enableHighAccuracy: high_accuracy}
212
+ `#@native.geolocation.getCurrentPosition(#{succ.to_n}, #{fail.to_n}, #{opts.to_n})`
213
+ promise
214
+ end
215
+
216
+ # Geolocates the user multiple times and calls a block with his location
217
+ # until #stop_tracking is called with a returned id. Calls a proc named error
218
+ # if error happens.
219
+ #
220
+ # @return [Integer] an ID that can be used as an argument to #stop_tracking
221
+ def track(max_age: 0, timeout: Float::INFINITY, high_accuracy: false, error: proc{|i|}, &block)
222
+ opts = {maxAge: max_age, timeout: timeout, enableHighAccuracy: high_accuracy}
223
+ succ = proc { |i| block.call(Position.new(i)) }
224
+ fail = proc { |i| error.call(Native(i)) }
225
+ `#@native.geolocation.watchPosition(#{succ.to_n}, #{fail.to_n}, #{opts.to_n})`
226
+ end
227
+
228
+ def stop_tracking(id)
229
+ `#@native.geolocation.clearWatch(#{id})`
230
+ end
231
+
232
+ # Triggers a vibration on a device. A pattern can be either a number of
233
+ # miliseconds for a vibration length, or an array of lengths (in
234
+ # miliseconds) which describes a vibration pattern - first element of said
235
+ # array describes how long the device should vibrate, second - how long to
236
+ # stop for and so on.
237
+ def vibrate(pattern)
238
+ `#@native.vibrate(#{pattern.to_n})`
239
+ end
240
+
241
+ # Check a battery status of user device. This API is deprecated in the browser
242
+ # context and usable mainly in privileged contexts.
243
+ #
244
+ # @return [Promise] a promise that resolves with a battery status
245
+ #
246
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery
247
+ def get_battery
248
+ promise = Promise.new
249
+ yes = proc { |r| promise.resolve(Native(r)) }
250
+ no = proc { |r| promise.reject(Native(r)) }
251
+ `#@native.getBattery().then(#{yes.to_n}).catch(#{no.to_n})`
252
+ promise
253
+ end
254
+
255
+ # Queue to send a small amount of data to a server.
256
+ #
257
+ # @param url [String] url to trigger
258
+ # @param payload [String, Blob, FormData, Hash] data to send
259
+ #
260
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
261
+ def send_beacon(url, payload=nil)
262
+ `#@native.sendBeacon(#{url}, #{payload.to_n})`
263
+ end
144
264
  end
145
265
 
146
266
  class Window
147
267
  # @!attribute [r] navigator
148
268
  # @return [Navigator] the navigator
149
269
  def navigator
150
- Navigator.new(`#@native.navigator`) if `#@native.navigator`
270
+ @navigator ||= Navigator.new(`#@native.navigator`) if `#@native.navigator`
151
271
  end
152
272
  end
153
273
 
@@ -0,0 +1,216 @@
1
+ # VisualViewport polyfill (mainly for Firefox browsers) taken from:
2
+ # https://github.com/WICG/visual-viewport/blob/gh-pages/polyfill/visualViewport.js
3
+ # Licensed under "W3C 3-clause BSD License".
4
+
5
+ # Let's make sure the functions aren't poluting the global context:
6
+ %x{
7
+ (function(){ "use strict";
8
+
9
+ // This is hacky but necessary in order to get the innerWidth/Height without
10
+ // page scale applied reliably.
11
+ function updateUnscaledDimensions() {
12
+ if (!window.viewPolyfill.iframeDummy) {
13
+ var iframe = document.createElement('iframe');
14
+ iframe.style.position="absolute";
15
+ iframe.style.width="100%";
16
+ iframe.style.height="100%";
17
+ iframe.style.left="0px";
18
+ iframe.style.top="0px";
19
+ iframe.style.border="0";
20
+ iframe.style.visibility="hidden";
21
+ iframe.style.zIndex="-1";
22
+ iframe.srcdoc = "<!DOCTYPE html><html><body style='margin:0px; padding:0px'></body></html>";
23
+
24
+ document.body.appendChild(iframe);
25
+ window.viewPolyfill.iframeDummy = iframe;
26
+ }
27
+
28
+ var iframe = window.viewPolyfill.iframeDummy;
29
+
30
+ var documentRect = document.documentElement.getBoundingClientRect();
31
+ var iframeBody = iframe.contentDocument.body;
32
+ iframeBody.style.width = documentRect.width + 'px';
33
+ iframeBody.style.height = documentRect.height + 'px';
34
+
35
+ // Hide overflow temporarily so that the iframe size isn't shrunk by
36
+ // scrollbars.
37
+ var prevDocumentOverflow = document.documentElement.style.overflow;
38
+ document.documentElement.style.overflow = "hidden";
39
+
40
+ var iframeWindow = window.viewPolyfill.iframeDummy.contentWindow;
41
+ window.viewPolyfill.unscaledInnerWidth = iframeWindow.innerWidth;
42
+ window.viewPolyfill.unscaledInnerHeight = iframeWindow.innerHeight;
43
+
44
+ document.documentElement.style.overflow = prevDocumentOverflow;
45
+ }
46
+
47
+ function fireScrollEvent() {
48
+ var listeners = window.viewPolyfill.scrollEventListeners;
49
+ for (var i = 0; i < listeners.length; i++)
50
+ listeners[i]();
51
+ }
52
+
53
+ function fireResizeEvent() {
54
+ var listeners = window.viewPolyfill.resizeEventListeners;
55
+ for (var i = 0; i < listeners.length; i++)
56
+ listeners[i]();
57
+ }
58
+
59
+ function updateViewportChanged() {
60
+ var scrollChanged =
61
+ window.viewPolyfill.offsetLeftSinceLastChange != window.visualViewport.offsetLeft ||
62
+ window.viewPolyfill.offsetTopSinceLastChange != window.visualViewport.offsetTop;
63
+
64
+ var sizeChanged =
65
+ window.viewPolyfill.widthSinceLastChange != window.visualViewport.width ||
66
+ window.viewPolyfill.heightSinceLastChange != window.visualViewport.height ||
67
+ window.viewPolyfill.scaleSinceLastChange != window.visualViewport.scale;
68
+
69
+ window.viewPolyfill.offsetLeftSinceLastChange = window.visualViewport.offsetLeft;
70
+ window.viewPolyfill.offsetTopSinceLastChange = window.visualViewport.offsetTop;
71
+ window.viewPolyfill.widthSinceLastChange = window.visualViewport.width;
72
+ window.viewPolyfill.heightSinceLastChange = window.visualViewport.height;
73
+ window.viewPolyfill.scaleSinceLastChange = window.visualViewport.scale;
74
+
75
+ if (scrollChanged)
76
+ fireScrollEvent();
77
+
78
+ if (sizeChanged)
79
+ fireResizeEvent();
80
+
81
+ setTimeout(updateViewportChanged, 500);
82
+ }
83
+
84
+ function registerChangeHandlers() {
85
+ window.addEventListener('scroll', updateViewportChanged, {'passive': true});
86
+ window.addEventListener('resize', updateViewportChanged, {'passive': true});
87
+ window.addEventListener('resize', updateUnscaledDimensions, {'passive': true});
88
+ }
89
+
90
+ var isChrome = navigator.userAgent.indexOf('Chrome') > -1;
91
+ var isSafari = navigator.userAgent.indexOf("Safari") > -1;
92
+ var isIEEdge = navigator.userAgent.indexOf('Edge') > -1;
93
+
94
+ if ((isChrome)&&(isSafari))
95
+ isSafari=false;
96
+
97
+ if (window.visualViewport) {
98
+ console.log('Using real visual viewport API');
99
+ } else {
100
+ console.log('Polyfilling Viewport API');
101
+ var layoutDummy = document.createElement('div');
102
+ layoutDummy.style.width = "100%";
103
+ layoutDummy.style.height = "100%";
104
+ if (isSafari) {
105
+ layoutDummy.style.position = "fixed";
106
+ } else {
107
+ layoutDummy.style.position = "absolute";
108
+ }
109
+ layoutDummy.style.left = "0px";
110
+ layoutDummy.style.top = "0px";
111
+ layoutDummy.style.visibility = "hidden";
112
+
113
+ window.viewPolyfill = {
114
+ "offsetLeftSinceLastChange": null,
115
+ "offsetTopSinceLastChange": null,
116
+ "widthSinceLastChange": null,
117
+ "heightSinceLastChange": null,
118
+ "scaleSinceLastChange": null,
119
+ "scrollEventListeners": [],
120
+ "resizeEventListeners": [],
121
+ "layoutDummy": layoutDummy,
122
+ "iframeDummy": null,
123
+ "unscaledInnerWidth": 0,
124
+ "unscaledInnerHeight": 0
125
+ }
126
+
127
+ registerChangeHandlers();
128
+
129
+ // TODO: Need to wait for <body> to be loaded but this is probably
130
+ // later than needed.
131
+ window.addEventListener('load', function() {
132
+ updateUnscaledDimensions();
133
+ document.body.appendChild(layoutDummy);
134
+
135
+ var viewport = {
136
+ get offsetLeft() {
137
+ if (isSafari) {
138
+ // Note: Safari's getBoundingClientRect left/top is wrong when pinch-zoomed requiring this "unscaling".
139
+ return window.scrollX - (layoutDummy.getBoundingClientRect().left * this.scale + window.scrollX * this.scale);
140
+ } else {
141
+ return window.scrollX + layoutDummy.getBoundingClientRect().left;
142
+ }
143
+ },
144
+ get offsetTop() {
145
+ if (isSafari) {
146
+ // Note: Safari's getBoundingClientRect left/top is wrong when pinch-zoomed requiring this "unscaling".
147
+ return window.scrollY - (layoutDummy.getBoundingClientRect().top * this.scale + window.scrollY * this.scale);
148
+ } else {
149
+ return window.scrollY + layoutDummy.getBoundingClientRect().top;
150
+ }
151
+ },
152
+ get width() {
153
+ var clientWidth = document.documentElement.clientWidth;
154
+ if (isIEEdge) {
155
+ // If there's no scrollbar before pinch-zooming, Edge will add
156
+ // a non-layout-affecting overlay scrollbar. This won't be
157
+ // reflected in documentElement.clientWidth so we need to
158
+ // manually subtract it out.
159
+ if (document.documentElement.clientWidth == window.viewPolyfill.unscaledInnerWidth
160
+ && this.scale > 1) {
161
+ var oldWidth = document.documentElement.clientWidth;
162
+ var prevHeight = layoutDummy.style.height;
163
+ // Lengthen the dummy to add a layout vertical scrollbar.
164
+ layoutDummy.style.height = "200%";
165
+ var scrollbarWidth = oldWidth - document.documentElement.clientWidth;
166
+ layoutDummy.style.width = prevHeight;
167
+ clientWidth -= scrollbarWidth;
168
+ }
169
+ }
170
+ return clientWidth / this.scale;
171
+ },
172
+ get height() {
173
+ var clientHeight = document.documentElement.clientHeight;
174
+ if (isIEEdge) {
175
+ // If there's no scrollbar before pinch-zooming, Edge will add
176
+ // a non-layout-affecting overlay scrollbar. This won't be
177
+ // reflected in documentElement.clientHeight so we need to
178
+ // manually subtract it out.
179
+ if (document.documentElement.clientHeight == window.viewPolyfill.unscaledInnerHeight
180
+ && this.scale > 1) {
181
+ var oldHeight = document.documentElement.clientHeight;
182
+ var prevWidth = layoutDummy.style.width;
183
+ // Widen the dummy to add a layout horizontal scrollbar.
184
+ layoutDummy.style.width = "200%";
185
+ var scrollbarHeight = oldHeight - document.documentElement.clientHeight;
186
+ layoutDummy.style.width = prevWidth;
187
+ clientHeight -= scrollbarHeight;
188
+ }
189
+ }
190
+ return clientHeight / this.scale;
191
+ },
192
+ get scale() {
193
+ return window.viewPolyfill.unscaledInnerWidth / window.innerWidth;
194
+ },
195
+ get pageLeft() {
196
+ return window.scrollX;
197
+ },
198
+ get pageTop() {
199
+ return window.scrollY;
200
+ },
201
+ "addEventListener": function(name, func) {
202
+ // TODO: Match event listener semantics. i.e. can't add the same callback twice.
203
+ if (name === 'scroll')
204
+ window.viewPolyfill.scrollEventListeners.push(func);
205
+ else if (name === 'resize')
206
+ window.viewPolyfill.resizeEventListeners.push(func);
207
+ }
208
+ };
209
+
210
+ window.visualViewport = viewport;
211
+ });
212
+ }
213
+
214
+ })();
215
+
216
+ }
@@ -4,8 +4,8 @@ module Browser
4
4
  #
5
5
  # @see https://developer.mozilla.org/en-US/docs/Web/API/Window.screen
6
6
  class Screen
7
- include Native
8
- include DOM::Event::Target
7
+ include Browser::NativeCachedWrapper
8
+ include Event::Target
9
9
 
10
10
  target {|value|
11
11
  Screen.new(value) if Native.is_a?(value, `window.Screen`)
@@ -59,7 +59,7 @@ class Window
59
59
  # @!attribute [r] screen
60
60
  # @return [Screen] the screen for the window
61
61
  def screen
62
- Screen.new(`#@native.screen`)
62
+ @screen ||= Screen.new(`#@native.screen`)
63
63
  end
64
64
  end
65
65
 
@@ -0,0 +1,6 @@
1
+ # browser/setup/base: Setup only the base requires
2
+
3
+ require 'browser/version'
4
+ require 'browser/utils'
5
+ require 'browser/form_data'
6
+ require 'browser/support'
@@ -0,0 +1,13 @@
1
+ # browser/setup/full - a full set of requires to provide all features at once
2
+
3
+ require 'paggio'
4
+
5
+ require 'browser/setup/large'
6
+
7
+ require 'browser/event/all'
8
+
9
+ require 'browser/dom/builder'
10
+ require 'browser/dom/mutation_observer'
11
+ require 'browser/dom/element/custom'
12
+
13
+ require 'browser/canvas'
@@ -0,0 +1,17 @@
1
+ # browser/setup/large - a larger set of requires for more complex applications
2
+ #
3
+ # Note - it doesn't include Paggio (or Paggio support) or many events
4
+
5
+ require 'browser/setup/mini'
6
+
7
+ require 'browser/effects'
8
+ require 'browser/http'
9
+ require 'browser/delay'
10
+ require 'browser/interval'
11
+ require 'browser/immediate'
12
+ require 'browser/storage'
13
+ require 'browser/blob'
14
+ require 'browser/animation_frame'
15
+ require 'browser/socket'
16
+ require 'browser/history'
17
+ require 'browser/navigator'
@@ -0,0 +1,8 @@
1
+ # browser/setup/mini - A smaller set of requires, designed for slim codebases
2
+
3
+ require 'browser/setup/base'
4
+
5
+ require 'browser/event'
6
+ require 'browser/window'
7
+ require 'browser/dom'
8
+ require 'browser/css'
@@ -0,0 +1,10 @@
1
+ # browser/setup/traditional, browser, opal-browser
2
+ # ------------------------------------------------
3
+ # A traditional set of requires.
4
+
5
+ require 'native'
6
+ require 'paggio'
7
+
8
+ require 'browser/setup/mini'
9
+ require 'browser/event/all'
10
+ require 'browser/dom/builder'
@@ -4,14 +4,14 @@ module Browser
4
4
  # connection.
5
5
  #
6
6
  # @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
7
- class Socket
7
+ class Socket < IO
8
8
  def self.supported?
9
9
  Browser.supports? :WebSocket
10
10
  end
11
11
 
12
- include Native
13
- include IO::Writable
14
- include DOM::Event::Target
12
+ include Native::Wrapper
13
+ include IO::Writable if defined? IO::Writable
14
+ include Event::Target
15
15
 
16
16
  target {|value|
17
17
  Socket.new(value) if Native.is_a?(value, `window.WebSocket`)
@@ -107,6 +107,10 @@ class Socket
107
107
  `#@native.send(#{data.to_n})`
108
108
  end
109
109
 
110
+ alias << write
111
+
112
+ alias send write
113
+
110
114
  # Close the socket.
111
115
  #
112
116
  # @param code [Integer, nil] the error code