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
@@ -4,7 +4,7 @@ module Browser; module DOM
4
4
  #
5
5
  # @see https://developer.mozilla.org/en-US/docs/Web/API/Node
6
6
  class Node
7
- include Native
7
+ include Browser::NativeCachedWrapper
8
8
 
9
9
  ELEMENT_NODE = 1
10
10
  ATTRIBUTE_NODE = 2
@@ -38,6 +38,11 @@ class Node
38
38
  end
39
39
  end
40
40
 
41
+ def initialize(node)
42
+ raise ArgumentError, "Please ensure that #initialize of #{self.class} accepts one argument" unless node
43
+ super
44
+ end
45
+
41
46
  # Return true of the other element is the same underlying DOM node.
42
47
  #
43
48
  # @return [Boolean]
@@ -45,6 +50,17 @@ class Node
45
50
  `#@native === #{Native.convert(other)}`
46
51
  end
47
52
 
53
+ # Initialize a new node after `#dup` or `#clone`.
54
+ #
55
+ # This method is not to be called directly. Use `Node#dup` or
56
+ # `Node#clone`.
57
+ #
58
+ # This method creates a deep detached clone of a DOM subtree to be used
59
+ # in the same document. The new node will have all events detached.
60
+ def initialize_copy(old)
61
+ set_native_reference `#{old.to_n}.cloneNode(true)`
62
+ end
63
+
48
64
  # Append a child to the node.
49
65
  #
50
66
  # When passing a {String} a text node will be created.
@@ -59,6 +75,8 @@ class Node
59
75
  if Opal.respond_to? node, :each
60
76
  node.each { |n| self << n }
61
77
  return self
78
+ elsif Opal.respond_to? node, :to_dom
79
+ node = node.to_dom(document)
62
80
  end
63
81
 
64
82
  unless native?(node)
@@ -78,6 +96,8 @@ class Node
78
96
  if Opal.respond_to? node, :each
79
97
  node.each { |n| self >> n }
80
98
  return self
99
+ elsif Opal.respond_to? node, :to_dom
100
+ node = node.to_dom(document)
81
101
  end
82
102
 
83
103
  unless native?(node)
@@ -114,6 +134,7 @@ class Node
114
134
  unless node
115
135
  node = DOM(&block)
116
136
  end
137
+ node = node.to_dom(document) if Opal.respond_to? node, :to_dom
117
138
 
118
139
  unless native?(node)
119
140
  if String === node
@@ -135,6 +156,7 @@ class Node
135
156
  unless node
136
157
  node = DOM(&block)
137
158
  end
159
+ node = node.to_dom(document) if Opal.respond_to? node, :to_dom
138
160
 
139
161
  unless native?(node)
140
162
  if String === node
@@ -154,6 +176,7 @@ class Node
154
176
  # @param node [Node] the node to append to
155
177
  def append_to(node)
156
178
  node << self
179
+ self
157
180
  end
158
181
 
159
182
  # Get an array of ancestors.
@@ -183,11 +206,16 @@ class Node
183
206
  NodeSet.new(parents)
184
207
  end
185
208
 
209
+ def attached?
210
+ `#@native.isConnected`
211
+ end
212
+
186
213
  alias before add_previous_sibling
187
214
 
188
215
  # Remove the node from its parent.
189
216
  def remove
190
217
  parent.remove_child(self) if parent
218
+ self
191
219
  end
192
220
 
193
221
  # Remove all the children of the node.
@@ -253,12 +281,22 @@ class Node
253
281
  node_type == COMMENT_NODE
254
282
  end
255
283
 
256
- # @!attribute [r] document
284
+ # Return true if the node is a custom element.
285
+ def custom?
286
+ false
287
+ end
288
+
289
+ # @!attribute [rw] document
257
290
  # @return [Document?] the document the node is attached to
258
291
  def document
259
292
  DOM(`#@native.ownerDocument`) if defined?(`#@native.ownerDocument`)
260
293
  end
261
294
 
295
+ # Detach a node and transfer it to another document.
296
+ def document=(new_document)
297
+ `#{Native.try_convert(new_document, new_document)}.adoptNode(#@native)`
298
+ end
299
+
262
300
  # Return true if the node is a document.
263
301
  def document?
264
302
  node_type == DOCUMENT_NODE
@@ -290,16 +328,6 @@ class Node
290
328
  node_type == DOCUMENT_FRAGMENT_NODE
291
329
  end
292
330
 
293
- # @!attribute inner_html
294
- # @return [String] the inner HTML of the node
295
- def inner_html
296
- `#@native.innerHTML`
297
- end
298
-
299
- def inner_html=(value)
300
- `#@native.innerHTML = #{value}`
301
- end
302
-
303
331
  alias inner_text content
304
332
  alias inner_text= content=
305
333
 
@@ -357,6 +385,14 @@ class Node
357
385
  `#@native.nodeType`
358
386
  end
359
387
 
388
+ # @!attribute outer_html
389
+ # @return [String] the simulated outer html of the node
390
+ def outer_html
391
+ div = $document.create_element("DIV")
392
+ div << self.dup
393
+ div.inner_html
394
+ end
395
+
360
396
  # @!attribute parent
361
397
  # @return [Element?] the parent of the node
362
398
  def parent
@@ -380,6 +416,7 @@ class Node
380
416
  # @param node [Node] the node to prepend to
381
417
  def prepend_to(node)
382
418
  node >> self
419
+ self
383
420
  end
384
421
 
385
422
  # @!attribute previous
@@ -407,6 +444,7 @@ class Node
407
444
  # Remove the given node from the children of this node.
408
445
  def remove_child(node)
409
446
  `#@native.removeChild(#{Native.try_convert(node)})`
447
+ self
410
448
  end
411
449
 
412
450
  # Replace the node with the given one.
@@ -416,6 +454,8 @@ class Node
416
454
  # @param node [Node] the node to replace with
417
455
  # @return [Node] the passed node
418
456
  def replace(node)
457
+ node = node.to_dom(document) if Opal.respond_to? node, :to_dom
458
+
419
459
  unless native?(node)
420
460
  if String === node
421
461
  node = `#@native.ownerDocument.createTextNode(node)`
@@ -426,7 +466,7 @@ class Node
426
466
 
427
467
  `#@native.parentNode.replaceChild(node, #@native)`
428
468
 
429
- node
469
+ DOM(node)
430
470
  end
431
471
 
432
472
  alias replace_with replace
@@ -35,6 +35,10 @@ class NodeSet
35
35
  end
36
36
  end
37
37
 
38
+ def respond_to_missing?(name, *)
39
+ @literal.respond_to?(name)
40
+ end
41
+
38
42
  # Get the first node matching the given CSS selectors.
39
43
  #
40
44
  # @param rules [Array<String>] the CSS selectors to match with
@@ -67,7 +71,7 @@ class NodeSet
67
71
 
68
72
  # Query for children matching the given CSS selector.
69
73
  #
70
- # @param selector [String] the CSS selector
74
+ # @param path [String] the CSS selector
71
75
  #
72
76
  # @return [NodeSet]
73
77
  def css(path)
@@ -83,7 +87,7 @@ class NodeSet
83
87
  #
84
88
  # @return [NodeSet] the new {NodeSet} with the matching nodes
85
89
  def filter(expression)
86
- @literal.select { |node| node =~ expression }
90
+ NodeSet[@literal.select { |node| node =~ expression }]
87
91
  end
88
92
 
89
93
  # Search for multiple selectors
@@ -91,6 +95,11 @@ class NodeSet
91
95
  NodeSet[@literal.map { |node| node.search(*what) }]
92
96
  end
93
97
 
98
+ # Outer HTML of the entire nodeset
99
+ def outer_html
100
+ @literal.map(&:outer_html).join
101
+ end
102
+
94
103
  # Query for children matching the given XPath.
95
104
  #
96
105
  # @param path [String] the XPath
@@ -105,6 +114,8 @@ class NodeSet
105
114
  def to_ary
106
115
  @literal
107
116
  end
117
+
118
+ alias to_a to_ary
108
119
  end
109
120
 
110
121
  end; end
@@ -0,0 +1,12 @@
1
+ module Browser; module DOM
2
+
3
+ class ShadowRoot < DocumentFragment
4
+ include DocumentOrShadowRoot
5
+
6
+ # Use: Element#shadow
7
+ def self.create
8
+ raise ArgumentError
9
+ end
10
+ end
11
+
12
+ end; end
@@ -5,8 +5,8 @@ module Browser; module DOM
5
5
  # @see https://developer.mozilla.org/en-US/docs/Web/API/Text
6
6
  class Text < CharacterData
7
7
  # (see Document#create_text)
8
- def self.create(*args)
9
- $document.create_text(*args)
8
+ def self.create(content)
9
+ $document.create_text(content)
10
10
  end
11
11
 
12
12
  # @!attribute [r] whole
data/opal/browser/dom.rb CHANGED
@@ -6,9 +6,10 @@ require 'browser/dom/text'
6
6
  require 'browser/dom/cdata'
7
7
  require 'browser/dom/comment'
8
8
  require 'browser/dom/element'
9
+ require 'browser/dom/document_or_shadow_root'
9
10
  require 'browser/dom/document'
10
11
  require 'browser/dom/document_fragment'
11
- require 'browser/dom/builder'
12
+ require 'browser/dom/shadow_root'
12
13
  require 'browser/dom/mutation_observer'
13
14
 
14
15
  module Kernel
@@ -33,9 +34,33 @@ module Kernel
33
34
  DOM(`doc`)
34
35
  end
35
36
 
36
- # Wrap a native element or create a DOM tree using the {Paggio::HTML} DSL.
37
+ # @overload DOM(document = $document, &block)
38
+ #
39
+ # Create a DOM tree using the {Paggio::HTML} DSL.
40
+ #
41
+ # @param document [Browser::DOM::Document] the document instance
42
+ # we intend to use
43
+ #
44
+ # @return [Browser::DOM::Node, Browser::DOM::NodeSet]
45
+ #
46
+ # @overload DOM(string, document = $document)
47
+ #
48
+ # Create a DOM tree from a HTML string.
49
+ #
50
+ # @param string [String] the HTML string
51
+ # @param document [Browser::DOM::Document] the document instance
52
+ # we intend to use
53
+ #
54
+ # @return [Browser::DOM::Node]
55
+ #
56
+ # @overload DOM(native)
57
+ #
58
+ # Wrap a native element to create a DOM tree.
59
+ #
60
+ # @param native [Native] the Native node
61
+ #
62
+ # @return [Browser::DOM::Node]
37
63
  #
38
- # @return [Browser::DOM::Node]
39
64
  def DOM(*args, &block)
40
65
  if block
41
66
  document = args.shift || $document
@@ -50,10 +75,18 @@ module Kernel
50
75
  what = args.shift
51
76
  document = args.shift || $document
52
77
 
53
- if native?(what)
78
+ what = what.to_dom(document) if Opal.respond_to? what, :to_dom
79
+
80
+ if `typeof(#{what}) === 'undefined' || #{what} === null`
81
+ raise ArgumentError, 'argument is null'
82
+ elsif native?(what)
54
83
  Browser::DOM::Node.new(what)
55
84
  elsif Browser::DOM::Node === what
56
85
  what
86
+ elsif Opal.respond_to? what, :each # eg. NodeSet, Array
87
+ document.create_element("DIV").tap do |div|
88
+ div << what
89
+ end
57
90
  elsif String === what
58
91
  %x{
59
92
  var doc = #{Native.try_convert(document)}.createElement('div');
@@ -62,7 +95,7 @@ module Kernel
62
95
  return #{DOM(`doc.childNodes.length == 1 ? doc.childNodes[0] : doc`)};
63
96
  }
64
97
  else
65
- raise ArgumentError, "argument not DOM convertible"
98
+ raise ArgumentError, 'argument is not DOM convertible'
66
99
  end
67
100
  end
68
101
  end
@@ -1,3 +1,5 @@
1
+ require 'browser/animation_frame'
2
+
1
3
  module Browser; module DOM
2
4
 
3
5
  class Document < Element
@@ -14,37 +16,201 @@ class Element
14
16
  # @param what [Symbol] how to display it
15
17
  def show(what = :block)
16
18
  style[:display] = what
19
+ self
17
20
  end
18
21
 
19
22
  # Hide the element.
20
23
  def hide
21
24
  style[:display] = :none
25
+ self
26
+ end
27
+
28
+ def visible?
29
+ # Let's check if we want to lie about the real visibility of an element.
30
+ # It could be wise to lie about it when it's in a process of animation...
31
+ if !@virtually_visible.nil?
32
+ @virtually_visible
33
+ else
34
+ style![:display] != :none
35
+ end
22
36
  end
23
37
 
24
38
  # Toggle the visibility of the element, hide it if it's shown, show it if
25
39
  # it's hidden.
26
- def toggle
27
- if style![:display] == :none
28
- show
29
- else
40
+ def toggle(what = :block)
41
+ if visible?
30
42
  hide
43
+ else
44
+ show(what)
31
45
  end
46
+ self
32
47
  end
33
48
 
34
49
  # Set the focus on the element.
35
50
  def focus
36
51
  `#@native.focus()`
52
+ self
37
53
  end
38
54
 
39
55
  # Blur the focus from the element.
40
56
  def blur
41
57
  `#@native.blur()`
58
+ self
42
59
  end
43
60
 
44
61
  # Check if the element is focused.
45
62
  def focused?
46
63
  `#@native.hasFocus`
47
64
  end
65
+
66
+ # Queue the block to happen when currently queued animations finish or during
67
+ # the next animation frame.
68
+ def animation_queue &block
69
+ promise = Promise.new
70
+
71
+ promise_resolve = proc do
72
+ @animation_promise = nil if @animation_promise == promise
73
+ promise.resolve
74
+ end
75
+
76
+ @animation_promise = (@animation_promise || Promise.value(true)).then do
77
+ animation_frame do
78
+ yield promise_resolve
79
+ end
80
+ promise
81
+ end
82
+ end
83
+
84
+ # Transform an element smoothly using CSS transitions, jQuery style. Yield
85
+ # a block afterwards if it's provided.
86
+ def animate(properties, duration: 0.4.s, easing: :ease, resolve: false, &block)
87
+ animation_queue(resolve) do |res|
88
+ duration = 0.6.s if duration == :slow
89
+ duration = 0.2.s if duration == :fast
90
+
91
+ original_value = style['transition']
92
+
93
+ style['transition'] = [original_value,
94
+ *properties.keys.map do |key|
95
+ "#{key} #{duration} #{easing}"
96
+ end].compact.join(", ")
97
+
98
+ properties.each do |key, value|
99
+ style[key] = value
100
+ end
101
+
102
+ promise = Promise.new
103
+
104
+ one :transitionend do |*args|
105
+ style['transition'] = original_value
106
+
107
+ yield(*args) if block_given?
108
+
109
+ res.call
110
+ end
111
+ end
112
+ self
113
+ end
114
+
115
+ # Show a hidden element with a "fade in" animation. Yield a block afterwards.
116
+ def fade_in(**kwargs, &block)
117
+ animation_queue do |resolve|
118
+ if !visible?
119
+ @virtually_visible = true
120
+ show
121
+
122
+ style[:opacity] = 0.0
123
+ animate opacity: 1.0, **kwargs do |*args|
124
+ @virtually_visible = nil
125
+ style[:opacity] = nil
126
+ yield(*args) if block_given?
127
+ end
128
+ end
129
+ resolve.call
130
+ end
131
+ self
132
+ end
133
+
134
+ # Hide a visible element with a "fade out" animation. Yield a block afterwards.
135
+ def fade_out(**kwargs, &block)
136
+ animation_queue do |resolve|
137
+ if visible?
138
+ @virtually_visible = false
139
+
140
+ style[:opacity] = 1.0
141
+ animate opacity: 0.0, **kwargs do |*args|
142
+ @virtually_visible = nil
143
+ style[:opacity] = nil
144
+ hide
145
+ yield(*args) if block_given?
146
+ end
147
+ end
148
+ resolve.call
149
+ end
150
+ self
151
+ end
152
+
153
+ # Toggle a visibility of an element with a "fade in"/"fade out" animation. Yield
154
+ # a block afterwards.
155
+ def fade_toggle(**kwargs, &block)
156
+ if visible?
157
+ fade_out(**kwargs, &block)
158
+ else
159
+ fade_in(**kwargs, &block)
160
+ end
161
+ self
162
+ end
163
+
164
+ # Show a hidden element with a "slide down" animation. Yield a block afterwards.
165
+ def slide_down(**kwargs, &block)
166
+ animation_queue do |resolve|
167
+ if !visible?
168
+ @virtually_visible = true
169
+ show
170
+ height = size.height
171
+ orig_height = style[:height]
172
+ style[:height] = 0.px
173
+
174
+ animate height: height.px, **kwargs do |*args|
175
+ @virtually_visible = nil
176
+ style[:height] = orig_height
177
+ yield(*args) if block_given?
178
+ end
179
+ end
180
+ resolve.call
181
+ end
182
+ self
183
+ end
184
+
185
+ # Hide a visible element with a "slide up" animation. Yield a block afterwards.
186
+ def slide_up(**kwargs, &block)
187
+ animation_queue do |resolve|
188
+ if visible?
189
+ @virtually_visible = false
190
+ orig_height = style[:height]
191
+
192
+ animate height: 0.px, **kwargs do |*args|
193
+ @virtually_visible = nil
194
+ style[:height] = orig_height
195
+ hide
196
+ yield(*args) if block_given?
197
+ end
198
+ end
199
+ resolve.call
200
+ end
201
+ self
202
+ end
203
+
204
+ # Toggle a visibility of an element with a "slide up"/"slide down" animation.
205
+ # Yield a block afterwards.
206
+ def slide_toggle(**kwargs, &block)
207
+ if visible?
208
+ slide_up(**kwargs, &block)
209
+ else
210
+ slide_down(**kwargs, &block)
211
+ end
212
+ self
213
+ end
48
214
  end
49
215
 
50
216
  end; end
@@ -0,0 +1,26 @@
1
+ # browser/event/all: Load support for all events
2
+
3
+ require 'browser/event/focus'
4
+ require 'browser/event/wheel'
5
+ require 'browser/event/data_transfer'
6
+ require 'browser/event/composition'
7
+ require 'browser/event/animation'
8
+ require 'browser/event/audio_processing'
9
+ require 'browser/event/before_unload'
10
+ require 'browser/event/composition'
11
+ require 'browser/event/clipboard'
12
+ require 'browser/event/device_light'
13
+ require 'browser/event/device_motion'
14
+ require 'browser/event/device_orientation'
15
+ require 'browser/event/device_proximity'
16
+ require 'browser/event/drag'
17
+ require 'browser/event/gamepad'
18
+ require 'browser/event/hash_change'
19
+ require 'browser/event/progress'
20
+ require 'browser/event/page_transition'
21
+ require 'browser/event/pop_state'
22
+ require 'browser/event/storage'
23
+ require 'browser/event/touch'
24
+ require 'browser/event/sensor'
25
+ require 'browser/event/message'
26
+ require 'browser/event/close'
@@ -1,6 +1,8 @@
1
1
  module Browser; class Event
2
2
 
3
3
  class Animation < Event
4
+ handles 'animationend', 'animationiteration', 'animationstart'
5
+
4
6
  def self.supported?
5
7
  Browser.supports? 'Event.Animation'
6
8
  end
@@ -1,6 +1,8 @@
1
1
  module Browser; class Event
2
2
 
3
3
  class AudioProcessing < Event
4
+ handles 'audioprocess'
5
+
4
6
  def self.supported?
5
7
  Browser.supports? 'Event.AudioProcessing'
6
8
  end
@@ -1,11 +1,11 @@
1
1
  module Browser
2
2
 
3
3
  class Event
4
- include Native
4
+ include Native::Wrapper
5
5
 
6
6
  # @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
7
7
  class Definition
8
- include Native
8
+ include Native::Wrapper
9
9
 
10
10
  # @private
11
11
  def self.new(&block)
@@ -270,6 +270,37 @@ class Event
270
270
  end
271
271
  end
272
272
 
273
+ # @overload one(name, &block)
274
+ #
275
+ # Start listening for an event on the target. Remove the event after firing
276
+ # so that it is fired at most once.
277
+ #
278
+ # @param name [String] the event name
279
+ #
280
+ # @yieldparam event [Event] the event
281
+ #
282
+ # @return [Callback]
283
+ #
284
+ # @overload one(name, selector, &block)
285
+ #
286
+ # Start listening for an event on the target children. Remove the event after
287
+ # firing so that it is fired at most once.
288
+ #
289
+ # @param name [String] the event name
290
+ # @param selector [String] the CSS selector to trigger the event on
291
+ #
292
+ # @yieldparam event [Event] the event
293
+ #
294
+ # @return [Delegate]
295
+ def one (name, selector = nil, &block)
296
+ raise ArgumentError, 'no block has been given' unless block
297
+
298
+ cb = on name, selector do |*args|
299
+ out = block.call(*args)
300
+ cb.off
301
+ out
302
+ end
303
+ end
273
304
  # @overload off()
274
305
  # Stop listening for any event.
275
306
  #
@@ -344,7 +375,7 @@ class Event
344
375
 
345
376
  # Trigger an event on the target.
346
377
  #
347
- # @param name [String] the event name
378
+ # @param event [String] the event name
348
379
  # @param args [Array] optional arguments to the event callback
349
380
  #
350
381
  # @yieldparam definition [Definition] definition to customize the event
@@ -358,7 +389,7 @@ class Event
358
389
 
359
390
  # Trigger an event on the target without bubbling.
360
391
  #
361
- # @param name [String] the event name
392
+ # @param event [String] the event name
362
393
  # @param args [Array] optional arguments to the event callback
363
394
  #
364
395
  # @yieldparam definition [Definition] definition to customize the event
@@ -1,6 +1,8 @@
1
1
  module Browser; class Event
2
2
 
3
3
  class BeforeUnload < Event
4
+ handles 'beforeunload'
5
+
4
6
  def self.supported?
5
7
  Browser.supports? 'Event.BeforeUnload'
6
8
  end
@@ -1,6 +1,8 @@
1
1
  module Browser; class Event
2
2
 
3
3
  class Clipboard < Event
4
+ handles 'copy', 'cut', 'paste'
5
+
4
6
  def self.supported?
5
7
  Browser.supports? 'Event.Clipboard'
6
8
  end
@@ -23,6 +25,13 @@ class Clipboard < Event
23
25
 
24
26
  alias_native :data
25
27
  alias_native :type, :dataType
28
+
29
+ # Returns a {DataTransfer} related to this event
30
+ #
31
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer
32
+ def transfer
33
+ DataTransfer.new(`#@native.clipboardData`)
34
+ end
26
35
  end
27
36
 
28
37
  end; end
@@ -1,6 +1,8 @@
1
1
  module Browser; class Event
2
2
 
3
3
  class Close < Event
4
+ handles 'close'
5
+
4
6
  def self.supported?
5
7
  Browser.supports? 'Event.Close'
6
8
  end