opal-browser 0.2.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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