opal-browser 0.2.0.beta1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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