opal-browser 0.1.0.beta1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (257) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/build.yml +95 -0
  3. data/.gitignore +3 -0
  4. data/.yardopts +1 -1
  5. data/Gemfile +22 -3
  6. data/LICENSE +20 -0
  7. data/README.md +200 -20
  8. data/Rakefile +29 -1
  9. data/config.ru +20 -2
  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 +24 -0
  78. data/lib/opal-browser.rb +1 -0
  79. data/opal/browser/animation_frame.rb +92 -10
  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 +2 -12
  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 +86 -28
  89. data/opal/browser/console.rb +6 -38
  90. data/opal/browser/cookies.rb +90 -27
  91. data/opal/browser/crypto.rb +79 -0
  92. data/opal/browser/css/declaration.rb +1 -6
  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 +94 -0
  98. data/opal/browser/dom/attribute.rb +16 -9
  99. data/opal/browser/dom/builder.rb +35 -25
  100. data/opal/browser/dom/character_data.rb +43 -7
  101. data/opal/browser/dom/document.rb +171 -37
  102. data/opal/browser/dom/document_fragment.rb +18 -0
  103. data/opal/browser/dom/document_or_shadow_root.rb +19 -0
  104. data/opal/browser/dom/element/attributes.rb +111 -0
  105. data/opal/browser/dom/element/button.rb +31 -0
  106. data/opal/browser/dom/element/custom.rb +177 -0
  107. data/opal/browser/dom/element/data.rb +82 -0
  108. data/opal/browser/dom/element/editable.rb +47 -0
  109. data/opal/browser/dom/element/form.rb +38 -0
  110. data/opal/browser/dom/element/iframe.rb +37 -0
  111. data/opal/browser/dom/element/image.rb +25 -0
  112. data/opal/browser/dom/element/input.rb +48 -1
  113. data/opal/browser/dom/element/media.rb +17 -0
  114. data/opal/browser/dom/element/offset.rb +32 -10
  115. data/opal/browser/dom/element/position.rb +11 -2
  116. data/opal/browser/dom/element/scroll.rb +139 -20
  117. data/opal/browser/dom/element/select.rb +42 -0
  118. data/opal/browser/dom/element/size.rb +46 -0
  119. data/opal/browser/dom/element/template.rb +11 -0
  120. data/opal/browser/dom/element/textarea.rb +26 -0
  121. data/opal/browser/dom/element.rb +496 -168
  122. data/opal/browser/dom/mutation_observer.rb +69 -9
  123. data/opal/browser/dom/node.rb +270 -83
  124. data/opal/browser/dom/node_set.rb +74 -41
  125. data/opal/browser/dom/shadow_root.rb +12 -0
  126. data/opal/browser/dom/text.rb +18 -3
  127. data/opal/browser/dom.rb +40 -18
  128. data/opal/browser/effects.rb +180 -3
  129. data/opal/browser/event/all.rb +26 -0
  130. data/opal/browser/event/animation.rb +40 -0
  131. data/opal/browser/{dom/event → event}/audio_processing.rb +10 -6
  132. data/opal/browser/event/base.rb +461 -0
  133. data/opal/browser/event/before_unload.rb +17 -0
  134. data/opal/browser/event/clipboard.rb +37 -0
  135. data/opal/browser/event/close.rb +49 -0
  136. data/opal/browser/event/composition.rb +52 -0
  137. data/opal/browser/event/custom.rb +65 -0
  138. data/opal/browser/event/data_transfer.rb +95 -0
  139. data/opal/browser/event/device_light.rb +25 -0
  140. data/opal/browser/{dom/event → event}/device_motion.rb +21 -6
  141. data/opal/browser/event/device_orientation.rb +50 -0
  142. data/opal/browser/{dom/event → event}/device_proximity.rb +10 -6
  143. data/opal/browser/event/drag.rb +123 -0
  144. data/opal/browser/event/focus.rb +41 -0
  145. data/opal/browser/event/gamepad.rb +62 -0
  146. data/opal/browser/{dom/event → event}/hash_change.rb +10 -6
  147. data/opal/browser/event/keyboard.rb +128 -0
  148. data/opal/browser/event/message.rb +72 -0
  149. data/opal/browser/{dom/event → event}/mouse.rb +37 -32
  150. data/opal/browser/event/page_transition.rb +25 -0
  151. data/opal/browser/event/pop_state.rb +35 -0
  152. data/opal/browser/event/progress.rb +45 -0
  153. data/opal/browser/event/sensor.rb +17 -0
  154. data/opal/browser/{dom/event → event}/storage.rb +10 -6
  155. data/opal/browser/{dom/event → event}/touch.rb +14 -21
  156. data/opal/browser/event/ui.rb +38 -0
  157. data/opal/browser/{dom/event → event}/wheel.rb +6 -4
  158. data/opal/browser/event.rb +163 -0
  159. data/opal/browser/event_source.rb +7 -4
  160. data/opal/browser/form_data.rb +225 -0
  161. data/opal/browser/history.rb +53 -21
  162. data/opal/browser/http/binary.rb +1 -0
  163. data/opal/browser/http/headers.rb +21 -2
  164. data/opal/browser/http/request.rb +83 -55
  165. data/opal/browser/http/response.rb +5 -1
  166. data/opal/browser/http.rb +47 -9
  167. data/opal/browser/immediate.rb +128 -10
  168. data/opal/browser/interval.rb +41 -23
  169. data/opal/browser/location.rb +20 -4
  170. data/opal/browser/navigator.rb +136 -13
  171. data/opal/browser/polyfill/visual_viewport.rb +216 -0
  172. data/opal/browser/screen.rb +34 -8
  173. data/opal/browser/setup/base.rb +6 -0
  174. data/opal/browser/setup/full.rb +13 -0
  175. data/opal/browser/setup/large.rb +17 -0
  176. data/opal/browser/setup/mini.rb +8 -0
  177. data/opal/browser/setup/traditional.rb +10 -0
  178. data/opal/browser/socket.rb +16 -8
  179. data/opal/browser/storage.rb +155 -52
  180. data/opal/browser/support.rb +299 -0
  181. data/opal/browser/utils.rb +116 -18
  182. data/opal/browser/version.rb +1 -1
  183. data/opal/browser/visual_viewport.rb +39 -0
  184. data/opal/browser/window/size.rb +47 -9
  185. data/opal/browser/window/view.rb +37 -4
  186. data/opal/browser/window.rb +46 -26
  187. data/opal/browser.rb +1 -10
  188. data/opal/opal-browser.rb +1 -0
  189. data/opal-browser.gemspec +10 -12
  190. data/spec/database/sql_spec.rb +139 -0
  191. data/spec/delay_spec.rb +41 -0
  192. data/spec/dom/attribute_spec.rb +49 -0
  193. data/spec/dom/builder_spec.rb +36 -19
  194. data/spec/dom/document_spec.rb +28 -6
  195. data/spec/dom/element/attributes_spec.rb +52 -0
  196. data/spec/dom/element/custom_spec.rb +106 -0
  197. data/spec/dom/element/subclass_spec.rb +144 -0
  198. data/spec/dom/element_spec.rb +184 -7
  199. data/spec/dom/mutation_observer_spec.rb +13 -9
  200. data/spec/dom/node_set_spec.rb +44 -0
  201. data/spec/dom/node_spec.rb +87 -27
  202. data/spec/dom_spec.rb +19 -9
  203. data/spec/event_source_spec.rb +18 -15
  204. data/spec/{dom/event_spec.rb → event_spec.rb} +55 -26
  205. data/spec/history_spec.rb +32 -19
  206. data/spec/http_spec.rb +25 -36
  207. data/spec/immediate_spec.rb +10 -7
  208. data/spec/interval_spec.rb +59 -0
  209. data/spec/native_cached_wrapper_spec.rb +46 -0
  210. data/spec/runner.rb +107 -0
  211. data/spec/socket_spec.rb +18 -14
  212. data/spec/spec_helper.rb +2 -4
  213. data/spec/spec_helper_promise.rb.erb +25 -0
  214. data/spec/storage_spec.rb +7 -7
  215. data/spec/wgxpath.install.js +49 -0
  216. data/spec/window_spec.rb +2 -2
  217. metadata +181 -93
  218. data/opal/browser/compatibility/animation_frame.rb +0 -93
  219. data/opal/browser/compatibility/dom/document/window.rb +0 -15
  220. data/opal/browser/compatibility/dom/element/css.rb +0 -15
  221. data/opal/browser/compatibility/dom/element/matches.rb +0 -31
  222. data/opal/browser/compatibility/dom/element/offset.rb +0 -20
  223. data/opal/browser/compatibility/dom/element/scroll.rb +0 -25
  224. data/opal/browser/compatibility/dom/element/style.rb +0 -15
  225. data/opal/browser/compatibility/dom/mutation_observer.rb +0 -47
  226. data/opal/browser/compatibility/http/request.rb +0 -15
  227. data/opal/browser/compatibility/immediate.rb +0 -107
  228. data/opal/browser/compatibility/window/scroll.rb +0 -27
  229. data/opal/browser/compatibility/window/size.rb +0 -13
  230. data/opal/browser/compatibility/window/view.rb +0 -13
  231. data/opal/browser/compatibility.rb +0 -59
  232. data/opal/browser/dom/compatibility.rb +0 -8
  233. data/opal/browser/dom/event/animation.rb +0 -26
  234. data/opal/browser/dom/event/base.rb +0 -207
  235. data/opal/browser/dom/event/before_unload.rb +0 -13
  236. data/opal/browser/dom/event/clipboard.rb +0 -26
  237. data/opal/browser/dom/event/close.rb +0 -35
  238. data/opal/browser/dom/event/composition.rb +0 -38
  239. data/opal/browser/dom/event/custom.rb +0 -30
  240. data/opal/browser/dom/event/device_light.rb +0 -21
  241. data/opal/browser/dom/event/device_orientation.rb +0 -36
  242. data/opal/browser/dom/event/drag.rb +0 -113
  243. data/opal/browser/dom/event/focus.rb +0 -23
  244. data/opal/browser/dom/event/gamepad.rb +0 -47
  245. data/opal/browser/dom/event/keyboard.rb +0 -93
  246. data/opal/browser/dom/event/message.rb +0 -50
  247. data/opal/browser/dom/event/page_transition.rb +0 -21
  248. data/opal/browser/dom/event/pop_state.rb +0 -21
  249. data/opal/browser/dom/event/progress.rb +0 -31
  250. data/opal/browser/dom/event/sensor.rb +0 -13
  251. data/opal/browser/dom/event/ui.rb +0 -22
  252. data/opal/browser/dom/event.rb +0 -240
  253. data/opal/browser/http/compatibility.rb +0 -1
  254. data/opal/browser/http/parameters.rb +0 -8
  255. data/opal/browser/timeout.rb +0 -60
  256. data/opal/browser/window/compatibility.rb +0 -3
  257. data/opal/browser/window/scroll.rb +0 -49
data/opal/browser/http.rb CHANGED
@@ -1,20 +1,25 @@
1
- require 'promise'
2
-
3
1
  require 'browser/http/binary'
4
2
  require 'browser/http/headers'
5
3
  require 'browser/http/request'
6
4
  require 'browser/http/response'
7
- require 'browser/http/compatibility'
8
5
 
9
6
  module Browser
10
7
 
11
8
  module HTTP
9
+ # Check if HTTP requests are supported.
10
+ def self.supported?
11
+ Browser.supports?('XHR') || Browser.supports?('ActiveXObject')
12
+ end
13
+
12
14
  # Send an asynchronous request.
13
15
  #
14
16
  # @param method [Symbol] the HTTP method to use
15
17
  # @param url [String] the URL to request
16
18
  # @param data [String, Hash] the data to send
17
- # @return [Response] the response
19
+ #
20
+ # @yieldparam request [Request] the request to configure
21
+ #
22
+ # @return [Promise] a promise that will be resolved with the response
18
23
  def self.send(method, url, data = nil, &block)
19
24
  Promise.new.tap {|promise|
20
25
  Request.new(&block).tap {|req|
@@ -32,7 +37,10 @@ module HTTP
32
37
  # Send an asynchronous GET request.
33
38
  #
34
39
  # @param url [String] the URL to request
35
- # @return [Response] the response
40
+ #
41
+ # @yieldparam request [Request] the request to configure
42
+ #
43
+ # @return [Promise] a promise that will be resolved with the response
36
44
  def self.get(url, &block)
37
45
  send(:get, url, &block)
38
46
  end
@@ -40,7 +48,10 @@ module HTTP
40
48
  # Send an asynchronous HEAD request.
41
49
  #
42
50
  # @param url [String] the URL to request
43
- # @return [Response] the response
51
+ #
52
+ # @yieldparam request [Request] the request to configure
53
+ #
54
+ # @return [Promise] a promise that will be resolved with the response
44
55
  def self.head(url, &block)
45
56
  send(:head, url, &block)
46
57
  end
@@ -49,7 +60,10 @@ module HTTP
49
60
  #
50
61
  # @param url [String] the URL to request
51
62
  # @param data [String, Hash] the data to send
52
- # @return [Response] the response
63
+ #
64
+ # @yieldparam request [Request] the request to configure
65
+ #
66
+ # @return [Promise] a promise that will be resolved with the response
53
67
  def self.post(url, data = nil, &block)
54
68
  send(:post, url, data, &block)
55
69
  end
@@ -58,7 +72,10 @@ module HTTP
58
72
  #
59
73
  # @param url [String] the URL to request
60
74
  # @param data [String, Hash] the data to send
61
- # @return [Response] the response
75
+ #
76
+ # @yieldparam request [Request] the request to configure
77
+ #
78
+ # @return [Promise] a promise that will be resolved with the response
62
79
  def self.put(url, data = nil, &block)
63
80
  send(:put, url, data, &block)
64
81
  end
@@ -67,7 +84,10 @@ module HTTP
67
84
  #
68
85
  # @param url [String] the URL to request
69
86
  # @param data [String, Hash] the data to send
70
- # @return [Response] the response
87
+ #
88
+ # @yieldparam request [Request] the request to configure
89
+ #
90
+ # @return [Promise] a promise that will be resolved with the response
71
91
  def self.delete(url, data = nil, &block)
72
92
  send(:delete, url, data, &block)
73
93
  end
@@ -77,6 +97,9 @@ module HTTP
77
97
  # @param method [Symbol] the HTTP method to use
78
98
  # @param url [String] the URL to request
79
99
  # @param data [String, Hash] the data to send
100
+ #
101
+ # @yieldparam request [Request] the request to configure
102
+ #
80
103
  # @return [Response] the response
81
104
  def self.send!(method, url, data = nil, &block)
82
105
  Request.new(&block).open(method, url, false).send(data)
@@ -85,6 +108,9 @@ module HTTP
85
108
  # Send a synchronous GET request.
86
109
  #
87
110
  # @param url [String] the URL to request
111
+ #
112
+ # @yieldparam request [Request] the request to configure
113
+ #
88
114
  # @return [Response] the response
89
115
  def self.get!(url, &block)
90
116
  send!(:get, url, &block)
@@ -93,6 +119,9 @@ module HTTP
93
119
  # Send a synchronous HEAD request.
94
120
  #
95
121
  # @param url [String] the URL to request
122
+ #
123
+ # @yieldparam request [Request] the request to configure
124
+ #
96
125
  # @return [Response] the response
97
126
  def self.head!(url, &block)
98
127
  send!(:head, url, &block)
@@ -102,6 +131,9 @@ module HTTP
102
131
  #
103
132
  # @param url [String] the URL to request
104
133
  # @param data [String, Hash] the data to send
134
+ #
135
+ # @yieldparam request [Request] the request to configure
136
+ #
105
137
  # @return [Response] the response
106
138
  def self.post!(url, data = nil, &block)
107
139
  send!(:post, url, data, &block)
@@ -111,6 +143,9 @@ module HTTP
111
143
  #
112
144
  # @param url [String] the URL to request
113
145
  # @param data [String, Hash] the data to send
146
+ #
147
+ # @yieldparam request [Request] the request to configure
148
+ #
114
149
  # @return [Response] the response
115
150
  def self.put!(url, data = nil, &block)
116
151
  send!(:put, url, data, &block)
@@ -120,6 +155,9 @@ module HTTP
120
155
  #
121
156
  # @param url [String] the URL to request
122
157
  # @param data [String, Hash] the data to send
158
+ #
159
+ # @yieldparam request [Request] the request to configure
160
+ #
123
161
  # @return [Response] the response
124
162
  def self.delete!(url, data = nil, &block)
125
163
  send!(:delete, url, data, &block)
@@ -1,9 +1,28 @@
1
- require 'browser/compatibility/immediate'
2
-
3
1
  module Browser
4
2
 
5
- # FIXME: drop the method_defined? checks when require order is fixed
3
+ # Class to easily create and dispatch an immediate call.
4
+ #
5
+ # Immediate calls are deferred function calls that happen as soon as they can
6
+ # be scheduled.
7
+ #
8
+ # Compatibility
9
+ # -------------
10
+ # The compatibility layer will try various implementations in the following
11
+ # order.
12
+ #
13
+ # + [setImmediate](https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate)
14
+ # + [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage)
15
+ # + [readystatechange](https://developer.mozilla.org/en-US/docs/Web/Reference/Events/readystatechange)
16
+ # + [setTimeout](https://developer.mozilla.org/en/docs/Web/API/window.setTimeout)
17
+ #
18
+ # The order has been chosen from best to worst for both performance and
19
+ # preemptiveness.
6
20
  class Immediate
21
+ # Create an immediate for the given function which will be called with the
22
+ # arguments and block.
23
+ #
24
+ # @param func [Proc] the function to call
25
+ # @param args [Array] the arguments to call it with
7
26
  def initialize(func, args, &block)
8
27
  @aborted = false
9
28
  @function = func
@@ -11,14 +30,88 @@ class Immediate
11
30
  @block = block
12
31
  end
13
32
 
14
- def dispatch
15
- raise NotImplementedError
16
- end unless method_defined? :dispatch
33
+ # @!method dispatch
34
+ # Dispatch the immediate.
35
+
36
+ # @!method prevent
37
+ # Prevent the immediate from being called once scheduled.
38
+ if Browser.supports? 'Immediate'
39
+ def dispatch
40
+ @id = `window.setImmediate(function() {
41
+ #{@function.call(*@arguments, &@block)};
42
+ })`
43
+ end
44
+
45
+ def prevent
46
+ `window.clearImmediate(#@id)`
47
+ end
48
+ elsif Browser.supports? 'Immediate (Internet Explorer)'
49
+ def dispatch
50
+ @id = `window.msSetImmediate(function() {
51
+ #{@function.call(*@arguments, &@block)};
52
+ })`
53
+ end
54
+
55
+ def prevent
56
+ `window.msClearImmediate(#@id)`
57
+ end
58
+ elsif Browser.supports? 'Window.send (Asynchronous)'
59
+ # @private
60
+ @@tasks = {}
61
+
62
+ # @private
63
+ @@prefix = "opal.browser.immediate.#{rand(1_000_000)}."
64
+
65
+ $window.on :message do |e|
66
+ if String === e.data && e.data.start_with?(@@prefix)
67
+ if task = @@tasks.delete(e.data[@@prefix.length .. -1])
68
+ task[0].call(*task[1], &task[2])
69
+ end
70
+ end
71
+ end
72
+
73
+ def dispatch
74
+ @id = rand(1_000_000).to_s
75
+ @@tasks[@id] = [@function, @arguments, @block]
76
+
77
+ $window.send "#{@@prefix}#{@id}"
78
+ end
79
+
80
+ def prevent
81
+ @@tasks.delete(@id)
82
+ end
83
+ elsif Browser.supports? 'Event.readystatechange'
84
+ def dispatch
85
+ %x{
86
+ var script = document.createElement("script");
87
+
88
+ script.onreadystatechange = function() {
89
+ if (!#{aborted?}) {
90
+ #{@function.call(*@arguments, &@block)};
91
+ }
92
+
93
+ script.onreadystatechange = null;
94
+ script.parentNode.removeChild(script);
95
+ };
17
96
 
18
- def prevent
19
- raise NotImplementedError
20
- end unless method_defined? :prevent
97
+ document.documentElement.appendChild(script);
98
+ }
99
+ end
21
100
 
101
+ def prevent; end
102
+ else
103
+ def dispatch
104
+ @id = `window.setTimeout(function() {
105
+ #{@function.call(*@arguments, &@block)};
106
+ }, 0)`
107
+ end
108
+
109
+ def prevent
110
+ `window.clearTimeout(#@id)`
111
+ end
112
+ end
113
+
114
+ # Abort the immediate.
22
115
  def abort
23
116
  return if aborted?
24
117
 
@@ -28,6 +121,7 @@ class Immediate
28
121
  self
29
122
  end
30
123
 
124
+ # Check if the immediate has been aborted.
31
125
  def aborted?
32
126
  @aborted
33
127
  end
@@ -35,9 +129,33 @@ end
35
129
 
36
130
  end
37
131
 
132
+ module Kernel
133
+ # (see Immediate.new)
134
+ def defer(*args, &block)
135
+ Browser::Immediate.new(block, args).tap(&:dispatch)
136
+ end
137
+ end
138
+
38
139
  class Proc
39
- # Defer the function to be called as soon as possible.
140
+ # (see Immediate.new)
40
141
  def defer(*args, &block)
41
142
  Browser::Immediate.new(self, args, &block).tap(&:dispatch)
42
143
  end
43
144
  end
145
+
146
+ class Promise
147
+ # Create a promise which will be resolved with the result of the immediate.
148
+ #
149
+ # @param args [Array] the arguments the block will be called with
150
+ def self.defer(*args, &block)
151
+ new.tap {|promise|
152
+ proc {
153
+ begin
154
+ promise.resolve(block.call(*args))
155
+ rescue Exception => e
156
+ promise.reject(e)
157
+ end
158
+ }.defer
159
+ }
160
+ end
161
+ end
@@ -1,29 +1,29 @@
1
1
  module Browser
2
2
 
3
- # This class wraps `setInterval`.
3
+ # Allows you to create an interval that executes the function every given
4
+ # seconds.
5
+ #
6
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Window.setInterval
4
7
  class Interval
5
8
  # @!attribute [r] every
6
- # @return [Number] the seconds every which the block is called
9
+ # @return [Float] the seconds every which the block is called
7
10
  attr_reader :every
8
11
 
9
12
  # Create and start an interval.
10
13
  #
11
14
  # @param window [Window] the window to start the interval on
12
- # @param time [Number] seconds every which to call the block
15
+ # @param time [Float] seconds every which to call the block
13
16
  def initialize(window, time, &block)
14
17
  @window = Native.convert(window)
15
18
  @every = time
16
19
  @block = block
17
20
 
18
21
  @aborted = false
19
- @stopped = true
20
-
21
- start
22
22
  end
23
23
 
24
24
  # Check if the interval has been stopped.
25
25
  def stopped?
26
- @stopped
26
+ @id.nil?
27
27
  end
28
28
 
29
29
  # Check if the interval has been aborted.
@@ -32,21 +32,17 @@ class Interval
32
32
  end
33
33
 
34
34
  # Abort the interval, it won't be possible to start it again.
35
- #
36
- # @return [self]
37
35
  def abort
38
36
  `#@window.clearInterval(#@id)`
39
37
 
40
38
  @aborted = true
41
39
  @id = nil
42
-
43
- self
44
40
  end
45
41
 
46
42
  # Stop the interval, it will be possible to start it again.
47
- #
48
- # @return [self]
49
43
  def stop
44
+ return if stopped?
45
+
50
46
  `#@window.clearInterval(#@id)`
51
47
 
52
48
  @stopped = true
@@ -54,16 +50,16 @@ class Interval
54
50
  end
55
51
 
56
52
  # Start the interval if it has been stopped.
57
- #
58
- # @return [self]
59
53
  def start
60
54
  raise "the interval has been aborted" if aborted?
61
-
62
55
  return unless stopped?
63
56
 
64
- @id = `#@window.setInterval(#{@block.to_n}, #@every * 1000)`
57
+ @id = `#@window.setInterval(#@block, #@every * 1000)`
58
+ end
65
59
 
66
- self
60
+ # Call the [Interval] block.
61
+ def call
62
+ @block.call
67
63
  end
68
64
  end
69
65
 
@@ -71,23 +67,45 @@ class Window
71
67
  # Execute the block every given seconds.
72
68
  #
73
69
  # @param time [Float] the seconds between every call
70
+ #
74
71
  # @return [Interval] the object representing the interval
75
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)
76
83
  Interval.new(@native, time, &block)
77
84
  end
78
85
  end
79
86
 
80
87
  end
81
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
+
82
101
  class Proc
102
+ # (see Browser::Window#every)
83
103
  def every(time)
84
104
  $window.every(time, &self)
85
105
  end
86
- end
87
106
 
88
- module Kernel
89
- # (see Browser::Window#every)
90
- def every(time, &block)
91
- $window.every(time, &block)
107
+ # (see Browser::Window#every!)
108
+ def every!(time)
109
+ $window.every!(time, &self)
92
110
  end
93
111
  end
@@ -1,7 +1,10 @@
1
1
  module Browser
2
2
 
3
+ # Allows manipulation of a location, usually from {Window} and {DOM::Document}.
4
+ #
5
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Location
3
6
  class Location
4
- include Native
7
+ include Browser::NativeCachedWrapper
5
8
 
6
9
  # Change the location.
7
10
  #
@@ -63,12 +66,25 @@ class Location
63
66
  # @return [String] the query part of the location URI
64
67
  alias_native :query, :search
65
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
66
75
  end
67
76
 
68
77
  class Window
69
- # Get the {Location} object for this window.
70
- #
71
- # @return [Location]
78
+ # @!attribute [r] location
79
+ # @return [Location] the location for the window
80
+ def location
81
+ Location.new(`#@native.location`) if `#@native.location`
82
+ end
83
+ end
84
+
85
+ class DOM::Document < DOM::Element
86
+ # @!attribute [r] location
87
+ # @return [Location] the location for the document
72
88
  def location
73
89
  Location.new(`#@native.location`) if `#@native.location`
74
90
  end
@@ -1,16 +1,18 @@
1
1
  module Browser
2
2
 
3
- # Class that represents the browser attributes.
3
+ # Representation of the navigator application.
4
+ #
5
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator
4
6
  class Navigator
5
- include Native
7
+ include Browser::NativeCachedWrapper
6
8
 
7
9
  Version = Struct.new(:major, :minor, :build)
8
10
  Product = Struct.new(:name, :version)
9
11
  Vendor = Struct.new(:name, :version)
10
12
 
11
- # Class that represents a MIME type.
13
+ # Representation of a MIME type.
12
14
  class MimeType
13
- include Native
15
+ include Browser::NativeCachedWrapper
14
16
 
15
17
  # @!attribute [r] plugin
16
18
  # @return [Plugin] the plugin for the MIME type
@@ -33,7 +35,9 @@ class Navigator
33
35
  alias_native :type
34
36
  end
35
37
 
36
- # Class to represent a browser plugin.
38
+ # Representation of a navigator plugin.
39
+ #
40
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Plugin
37
41
  class Plugin < Native::Array
38
42
  def initialize(plugin)
39
43
  super plugin do |m|
@@ -58,6 +62,27 @@ class Navigator
58
62
  alias_native :version
59
63
  end
60
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
+
61
86
  # @!attribute [r] code
62
87
  # @return [String] the browser code name
63
88
  alias_native :code, :appCodeName
@@ -108,11 +133,9 @@ class Navigator
108
133
  alias_native :platform
109
134
 
110
135
  # @!attribute [r] plugins
111
- # @return [Native::Array<Plugin>] the enabled plugins
136
+ # @return [Plugins] the enabled plugins
112
137
  def plugins
113
- Native::Array.new `#@native.plugins` do |p|
114
- Plugin.new(p)
115
- end
138
+ Plugins.new(`#@native.plugins`)
116
139
  end
117
140
 
118
141
  # @!attribute [r] product
@@ -137,14 +160,114 @@ class Navigator
137
160
  rescue
138
161
  false
139
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
140
264
  end
141
265
 
142
266
  class Window
143
- # Get the {Navigator} object for this window.
144
- #
145
- # @return [Navigator]
267
+ # @!attribute [r] navigator
268
+ # @return [Navigator] the navigator
146
269
  def navigator
147
- Navigator.new(`#@native.navigator`) if `#@native.navigator`
270
+ @navigator ||= Navigator.new(`#@native.navigator`) if `#@native.navigator`
148
271
  end
149
272
  end
150
273