opal-browser 0.2.0 → 0.3.3

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 (202) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/build.yml +78 -0
  3. data/.gitignore +3 -0
  4. data/CHANGELOG.md +11 -0
  5. data/Gemfile +17 -3
  6. data/LICENSE +2 -1
  7. data/README.md +131 -54
  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 +2 -3
  78. data/opal/browser/audio/node.rb +121 -0
  79. data/opal/browser/audio/param_schedule.rb +43 -0
  80. data/opal/browser/audio.rb +66 -0
  81. data/opal/browser/blob.rb +94 -0
  82. data/opal/browser/canvas/data.rb +1 -1
  83. data/opal/browser/canvas/gradient.rb +1 -1
  84. data/opal/browser/canvas/style.rb +3 -1
  85. data/opal/browser/canvas/text.rb +1 -1
  86. data/opal/browser/canvas.rb +17 -3
  87. data/opal/browser/console.rb +3 -1
  88. data/opal/browser/cookies.rb +72 -34
  89. data/opal/browser/crypto.rb +79 -0
  90. data/opal/browser/css/declaration.rb +1 -1
  91. data/opal/browser/css/rule.rb +1 -1
  92. data/opal/browser/css/style_sheet.rb +2 -2
  93. data/opal/browser/css.rb +23 -7
  94. data/opal/browser/database/sql.rb +7 -8
  95. data/opal/browser/delay.rb +16 -0
  96. data/opal/browser/dom/attribute.rb +1 -1
  97. data/opal/browser/dom/builder.rb +29 -10
  98. data/opal/browser/dom/document.rb +81 -13
  99. data/opal/browser/dom/document_fragment.rb +18 -0
  100. data/opal/browser/dom/document_or_shadow_root.rb +19 -0
  101. data/opal/browser/dom/element/attributes.rb +28 -4
  102. data/opal/browser/dom/element/button.rb +31 -0
  103. data/opal/browser/dom/element/custom.rb +177 -0
  104. data/opal/browser/dom/element/data.rb +17 -2
  105. data/opal/browser/dom/element/editable.rb +47 -0
  106. data/opal/browser/dom/element/form.rb +38 -0
  107. data/opal/browser/dom/element/iframe.rb +37 -0
  108. data/opal/browser/dom/element/image.rb +2 -0
  109. data/opal/browser/dom/element/input.rb +36 -0
  110. data/opal/browser/dom/element/media.rb +17 -0
  111. data/opal/browser/dom/element/scroll.rb +106 -74
  112. data/opal/browser/dom/element/select.rb +6 -0
  113. data/opal/browser/dom/element/size.rb +12 -0
  114. data/opal/browser/dom/element/template.rb +2 -0
  115. data/opal/browser/dom/element/textarea.rb +2 -0
  116. data/opal/browser/dom/element.rb +194 -50
  117. data/opal/browser/dom/mutation_observer.rb +2 -2
  118. data/opal/browser/dom/node.rb +53 -13
  119. data/opal/browser/dom/node_set.rb +13 -2
  120. data/opal/browser/dom/shadow_root.rb +12 -0
  121. data/opal/browser/dom/text.rb +2 -2
  122. data/opal/browser/dom.rb +38 -5
  123. data/opal/browser/effects.rb +170 -4
  124. data/opal/browser/event/all.rb +26 -0
  125. data/opal/browser/event/animation.rb +2 -0
  126. data/opal/browser/event/audio_processing.rb +2 -0
  127. data/opal/browser/event/base.rb +35 -4
  128. data/opal/browser/event/before_unload.rb +2 -0
  129. data/opal/browser/event/clipboard.rb +9 -0
  130. data/opal/browser/event/close.rb +2 -0
  131. data/opal/browser/event/composition.rb +2 -0
  132. data/opal/browser/event/custom.rb +1 -1
  133. data/opal/browser/event/data_transfer.rb +95 -0
  134. data/opal/browser/event/device_light.rb +2 -0
  135. data/opal/browser/event/device_motion.rb +2 -0
  136. data/opal/browser/event/device_orientation.rb +2 -0
  137. data/opal/browser/event/device_proximity.rb +2 -0
  138. data/opal/browser/event/drag.rb +9 -5
  139. data/opal/browser/event/focus.rb +2 -0
  140. data/opal/browser/event/gamepad.rb +3 -1
  141. data/opal/browser/event/hash_change.rb +2 -0
  142. data/opal/browser/event/keyboard.rb +14 -1
  143. data/opal/browser/event/message.rb +2 -0
  144. data/opal/browser/event/mouse.rb +10 -6
  145. data/opal/browser/event/page_transition.rb +2 -0
  146. data/opal/browser/event/pop_state.rb +2 -0
  147. data/opal/browser/event/progress.rb +2 -0
  148. data/opal/browser/event/sensor.rb +2 -0
  149. data/opal/browser/event/storage.rb +2 -0
  150. data/opal/browser/event/touch.rb +2 -0
  151. data/opal/browser/event/wheel.rb +2 -0
  152. data/opal/browser/event.rb +26 -116
  153. data/opal/browser/event_source.rb +1 -1
  154. data/opal/browser/form_data.rb +225 -0
  155. data/opal/browser/history.rb +4 -8
  156. data/opal/browser/http/request.rb +32 -10
  157. data/opal/browser/http/response.rb +5 -1
  158. data/opal/browser/http.rb +0 -2
  159. data/opal/browser/immediate.rb +0 -2
  160. data/opal/browser/location.rb +7 -1
  161. data/opal/browser/navigator.rb +105 -4
  162. data/opal/browser/polyfill/visual_viewport.rb +216 -0
  163. data/opal/browser/screen.rb +2 -2
  164. data/opal/browser/setup/base.rb +6 -0
  165. data/opal/browser/setup/full.rb +13 -0
  166. data/opal/browser/setup/large.rb +17 -0
  167. data/opal/browser/setup/mini.rb +8 -0
  168. data/opal/browser/setup/traditional.rb +10 -0
  169. data/opal/browser/socket.rb +3 -3
  170. data/opal/browser/storage.rb +2 -2
  171. data/opal/browser/support.rb +46 -22
  172. data/opal/browser/utils.rb +94 -14
  173. data/opal/browser/version.rb +1 -1
  174. data/opal/browser/visual_viewport.rb +39 -0
  175. data/opal/browser/window/size.rb +14 -0
  176. data/opal/browser/window/view.rb +15 -0
  177. data/opal/browser/window.rb +29 -16
  178. data/opal/browser.rb +1 -11
  179. data/opal-browser.gemspec +3 -3
  180. data/spec/database/sql_spec.rb +43 -35
  181. data/spec/delay_spec.rb +15 -12
  182. data/spec/dom/document_spec.rb +10 -8
  183. data/spec/dom/element/custom_spec.rb +106 -0
  184. data/spec/dom/element/subclass_spec.rb +144 -0
  185. data/spec/dom/element_spec.rb +42 -0
  186. data/spec/dom/mutation_observer_spec.rb +12 -8
  187. data/spec/dom/node_spec.rb +48 -0
  188. data/spec/dom_spec.rb +8 -0
  189. data/spec/event_source_spec.rb +15 -12
  190. data/spec/{dom/event_spec.rb → event_spec.rb} +44 -15
  191. data/spec/history_spec.rb +23 -19
  192. data/spec/http_spec.rb +19 -31
  193. data/spec/immediate_spec.rb +5 -4
  194. data/spec/interval_spec.rb +18 -9
  195. data/spec/native_cached_wrapper_spec.rb +46 -0
  196. data/spec/runner.rb +37 -62
  197. data/spec/socket_spec.rb +15 -12
  198. data/spec/spec_helper.rb +2 -1
  199. data/spec/spec_helper_promise.rb.erb +25 -0
  200. metadata +120 -16
  201. data/.travis.yml +0 -74
  202. data/opal/browser/window/scroll.rb +0 -59
@@ -0,0 +1,225 @@
1
+ module Browser
2
+
3
+ class FormData
4
+ include NativeCachedWrapper
5
+
6
+ module Converter
7
+ # Encode as URI component.
8
+ #
9
+ # @return [String] the string encoded for usage as URI component
10
+ def encode(string)
11
+ `encodeURIComponent(#{string})`
12
+ end
13
+
14
+ # Decode as URI component.
15
+ #
16
+ # @return [String] the string decoded as URI component
17
+ def decode(string)
18
+ `decodeURIComponent(#{string})`
19
+ end
20
+
21
+ # Encode as URI.
22
+ #
23
+ # @return [String] the string encoded as URI
24
+ def encode_uri(string)
25
+ `encodeURI(#{string})`
26
+ end
27
+
28
+ # Decode as URI.
29
+ #
30
+ # @return [String] the string decoded as URI
31
+ def decode_uri(string)
32
+ `decodeURI(#{string})`
33
+ end
34
+
35
+ # Flattens a hash to build a flat array, later to be formatted to
36
+ # produce a nested query.
37
+ #
38
+ # This code should be compatible with what Rack::Utils#build_nested_query [1]
39
+ # does.
40
+ #
41
+ # [1] https://github.com/rack/rack/blob/master/lib/rack/utils.rb
42
+ def flatten(value, key="")
43
+ case value
44
+ when Hash
45
+ out = []
46
+ value.each do |k,v|
47
+ k = "#{key}[#{k}]" if key != ''
48
+ out += flatten(v,k)
49
+ end
50
+ out
51
+ when Array
52
+ out = []
53
+ value.each do |v|
54
+ k = "#{key}[]"
55
+ out += flatten(v,k)
56
+ end
57
+ out
58
+ else
59
+ [[key,value]]
60
+ end
61
+ end
62
+
63
+ # Converts a flat array to a Hash.
64
+ #
65
+ # This code should be compatible with what Rack::Utils#parse_nested_query [1]
66
+ # does.
67
+ #
68
+ # [1] https://github.com/rack/rack/blob/master/lib/rack/utils.rb
69
+ def unflatten(array)
70
+ out = {}
71
+ array.each do |k,v|
72
+ path = [k.split("[").first] + k.scan(/\[(.*?)\]/).flatten
73
+ c = out
74
+
75
+ set = proc { |v,weak| } # Do nothing for the first level
76
+
77
+ path.each do |i|
78
+ case i
79
+ when "" # Array
80
+ set.([], true)
81
+ set = proc do |v,weak|
82
+ c << v
83
+ c = c.last
84
+ end
85
+ else # Hash
86
+ set.({}, true)
87
+ set = proc do |v,weak|
88
+ c[i] ||= v
89
+ c[i] = v if !weak
90
+ c = c[i]
91
+ end
92
+ end
93
+ end
94
+ set.(v, false)
95
+
96
+ end
97
+ out
98
+ end
99
+
100
+ # Checks if a query Hash contains any files.
101
+ def contain_files?(hash)
102
+ flatten(hash).any? { |k,v| [File, Blob].include?(v.class) }
103
+ end
104
+
105
+ # Convert a query Hash to a query string
106
+ #
107
+ # @return [String] the string encoded as URI
108
+ def build_query(hash, sep=?&)
109
+ flatten(hash).map { |k,v| encode(k) + ?= + encode(v.to_s) }.join(sep)
110
+ end
111
+
112
+ # Convert a query Hash to a FormData instance
113
+ #
114
+ # @return [FormData] the instance of FormData
115
+ def build_form_data(hash)
116
+ fd = FormData.create
117
+ flatten(hash).each { |k,v| fd << [k,v] }
118
+ fd
119
+ end
120
+
121
+ # Convert a query string to a query Hash
122
+ #
123
+ # @return [Hash] the query hash
124
+ def parse_query(string, sep=?&)
125
+ unflatten(string.split(sep).map { |s| s.split(?=).map(&method(:decode)) })
126
+ end
127
+
128
+ # Converts a JS native value to a wrapped one if possible.
129
+ #
130
+ # @return [String, File, Blob]
131
+ def from_native(n)
132
+ %x{
133
+ var c = #{n}.constructor;
134
+ if (c === File) {
135
+ #{n = File.new(n)}
136
+ }
137
+ else if (c === Blob) {
138
+ #{n = Blob.new(n)}
139
+ }
140
+ }
141
+ n
142
+ end
143
+ end
144
+
145
+ extend Converter
146
+ include Enumerable
147
+
148
+ # Create a new FormData instance
149
+ def self.create(hash=nil)
150
+ if Hash === hash
151
+ FormData.build_form_data(hash)
152
+ elsif DOM::Element::Form === hash
153
+ new(`new FormData(#{hash.to_n})`)
154
+ else
155
+ new(`new FormData()`)
156
+ end
157
+ end
158
+
159
+ # Append a tuple to this FormData instance
160
+ #
161
+ # @param tuple [Array(String, String), Array(String, Blob), Array(String, File),
162
+ # Array(String, Blob, String), Array(String, File, String)]
163
+ # a tuple of a key, value and possibly a filename
164
+ def <<(tuple)
165
+ key, value, filename = tuple
166
+
167
+ unless filename
168
+ `#@native.append(#{key}, #{Native.convert(value)})`
169
+ else
170
+ `#@native.append(#{key}, #{Native.convert(value)}, #{filename})`
171
+ end
172
+ end
173
+
174
+ # Get a field from this FormData instance with a given name
175
+ def [](key)
176
+ FormData.from_native(`#@native.get(#{key})`)
177
+ end
178
+
179
+ # Set a field in this FormData instance with a given name
180
+ def set(key, value, filename = nil)
181
+ unless filename
182
+ `#@native.set(#{key}, #{Native.convert(value)})`
183
+ else
184
+ `#@native.set(#{key}, #{Native.convert(value)}, #{filename})`
185
+ end
186
+ end
187
+ alias []= set
188
+
189
+ # Convert to hash
190
+ def to_h
191
+ hash = {}
192
+ %x{
193
+ var pair, v, e = #@native.entries();
194
+ while (true) {
195
+ v = e.next();
196
+ if (v.done) break;
197
+ pair = v.value;
198
+ #{hash[`pair[0]`] = FormData.from_native(`pair[1]`)}
199
+ }
200
+ }
201
+ hash
202
+ end
203
+
204
+ # Convert to array
205
+ def to_a
206
+ to_h.to_a
207
+ end
208
+
209
+ # Iterate over all elements of this FormData
210
+ def each(&block)
211
+ to_h.each(&block)
212
+ end
213
+
214
+ # Checks if a field of this name exists in this FormData instance
215
+ def include?(key)
216
+ `#@native.has(#{key})`
217
+ end
218
+
219
+ # Delete a field from this FormData instance
220
+ def delete(key)
221
+ `#@native.delete(#{key})`
222
+ end
223
+ end
224
+
225
+ end
@@ -11,7 +11,7 @@ class History
11
11
  Browser.supports? 'History'
12
12
  end
13
13
 
14
- include Native
14
+ include Browser::NativeCachedWrapper
15
15
 
16
16
  # @!attribute [r] length
17
17
  # @return [Integer] how many items are in the history
@@ -36,9 +36,7 @@ class History
36
36
  # @param item [String] the item to push in the history
37
37
  # @param data [Object] additional state to push
38
38
  def push(item, data = nil)
39
- data = `null` if data.nil?
40
-
41
- `#@native.pushState(data, null, item)`
39
+ `#@native.pushState(#{data.to_n}, null, item)`
42
40
  end
43
41
 
44
42
  # Replace the current history item with another.
@@ -46,15 +44,13 @@ class History
46
44
  # @param item [String] the item to replace with
47
45
  # @param data [Object] additional state to replace
48
46
  def replace(item, data = nil)
49
- data = `null` if data.nil?
50
-
51
- `#@native.replaceState(data, null, item)`
47
+ `#@native.replaceState(#{data.to_n}, null, item)`
52
48
  end
53
49
 
54
50
  # @!attribute [r] current
55
51
  # @return [String] the current item
56
52
  def current
57
- $window.location.path
53
+ $window.location.full_path
58
54
  end
59
55
 
60
56
  # @!attribute [r] state
@@ -1,7 +1,8 @@
1
1
  module Browser; module HTTP
2
2
 
3
3
  class Request
4
- include Native
4
+ include Native::Wrapper
5
+ include Event::Target
5
6
 
6
7
  # Default headers.
7
8
  HEADERS = {
@@ -10,6 +11,8 @@ class Request
10
11
  'Accept' => 'text/javascript, text/html, application/xml, text/xml, */*'
11
12
  }
12
13
 
14
+ STATES = %w[uninitialized loading loaded interactive complete]
15
+
13
16
  # @!attribute [r] headers
14
17
  # @return [Headers] the request headers
15
18
  attr_reader :headers
@@ -191,8 +194,12 @@ class Request
191
194
  # @param what [Symbol, String] the event name
192
195
  #
193
196
  # @yieldparam response [Response] the response for the event
194
- def on(what, &block)
195
- @callbacks[what] << block
197
+ def on(what, *, &block)
198
+ if STATES.include?(what) || %w[success failure].include?(what) || Integer === what
199
+ @callbacks[what] << block
200
+ else
201
+ super
202
+ end
196
203
  end
197
204
 
198
205
  # Open the request.
@@ -229,7 +236,7 @@ class Request
229
236
  url += ??
230
237
  end
231
238
 
232
- url += @query.encode_uri
239
+ url += FormData.build_query(@query)
233
240
  end
234
241
 
235
242
  `#@native.open(#{@method.to_s.upcase}, #{url.to_s}, #{@asynchronous}, #{@user.to_n}, #{@password.to_n})`
@@ -289,14 +296,29 @@ class Request
289
296
 
290
297
  if String === parameters
291
298
  data = parameters
292
- elsif Hash === parameters && !parameters.empty?
293
- data = parameters.map {|vals|
294
- vals.map(&:encode_uri_component).join(?=)
295
- }.join(?&)
299
+ elsif (Hash === parameters && !parameters.empty?) || FormData === parameters
300
+ data = if Hash === parameters
301
+ if FormData.contain_files?(parameters)
302
+ FormData.build_form_data(parameters)
303
+ else
304
+ FormData.build_query(parameters)
305
+ end
306
+ else #if FormData === parameters
307
+ parameters
308
+ end
296
309
 
297
310
  unless @content_type
298
- `#@native.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')`
311
+ if FormData === data
312
+ # I thought it's done this way, but it isn't. It actually is
313
+ # "multipart/form-data; boundary=-----------.......". Let's miss it
314
+ # purposefully, because it's filled in automatically in this example.
315
+ # `#@native.setRequestHeader('Content-Type', 'multipart/form-data')`
316
+ else
317
+ `#@native.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')`
318
+ end
299
319
  end
320
+
321
+ data = data.to_n
300
322
  else
301
323
  data = `null`
302
324
  end
@@ -314,7 +336,7 @@ class Request
314
336
  private
315
337
  def callback
316
338
  -> event {
317
- state = %w[uninitialized loading loaded interactive complete][`#@native.readyState`]
339
+ state = STATES[`#@native.readyState`]
318
340
  res = response
319
341
 
320
342
  @callbacks[state].each { |b| b.(res) }
@@ -4,7 +4,7 @@ module Browser; module HTTP
4
4
 
5
5
  # Represents an HTTP response.
6
6
  class Response
7
- include Native
7
+ include Native::Wrapper
8
8
 
9
9
  Status = Struct.new(:code, :text)
10
10
 
@@ -47,6 +47,10 @@ class Response
47
47
  !success?
48
48
  end
49
49
 
50
+ # @!attribute [r] url
51
+ # @return [String] the response URL (after redirects)
52
+ alias_native :url, :responseURL
53
+
50
54
  # @!attribute [r] text
51
55
  # @return [String] the response body as text
52
56
  def text
data/opal/browser/http.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'promise'
2
-
3
1
  require 'browser/http/binary'
4
2
  require 'browser/http/headers'
5
3
  require 'browser/http/request'
@@ -1,5 +1,3 @@
1
- require 'promise'
2
-
3
1
  module Browser
4
2
 
5
3
  # Class to easily create and dispatch an immediate call.
@@ -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,7 +62,7 @@ class Navigator
62
62
  alias_native :version
63
63
  end
64
64
 
65
- # Representation for the arary of plugins.
65
+ # Representation for the array of plugins.
66
66
  #
67
67
  # @see https://developer.mozilla.org/en-US/docs/Web/API/NavigatorPlugins
68
68
  class Plugins < Native::Array
@@ -160,13 +160,114 @@ class Navigator
160
160
  rescue
161
161
  false
162
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
163
264
  end
164
265
 
165
266
  class Window
166
267
  # @!attribute [r] navigator
167
268
  # @return [Navigator] the navigator
168
269
  def navigator
169
- Navigator.new(`#@native.navigator`) if `#@native.navigator`
270
+ @navigator ||= Navigator.new(`#@native.navigator`) if `#@native.navigator`
170
271
  end
171
272
  end
172
273