opal-browser 0.2.0.beta1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/build.yml +95 -0
  3. data/.gitignore +3 -0
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +17 -3
  6. data/LICENSE +2 -1
  7. data/README.md +183 -52
  8. data/Rakefile +29 -1
  9. data/config.ru +20 -3
  10. data/docs/polyfills.md +24 -0
  11. data/examples/2048/Gemfile +6 -0
  12. data/examples/2048/README.md +13 -0
  13. data/examples/2048/app/application.rb +169 -0
  14. data/examples/2048/config.ru +9 -0
  15. data/examples/canvas/Gemfile +6 -0
  16. data/examples/canvas/README.md +9 -0
  17. data/examples/canvas/app/application.rb +55 -0
  18. data/examples/canvas/config.ru +9 -0
  19. data/examples/component/Gemfile +6 -0
  20. data/examples/component/README.md +10 -0
  21. data/examples/component/app/application.rb +66 -0
  22. data/examples/component/config.ru +9 -0
  23. data/examples/integrations/README.md +24 -0
  24. data/examples/integrations/dynamic-rack-opal-sprockets-server/Gemfile +6 -0
  25. data/examples/integrations/dynamic-rack-opal-sprockets-server/README.md +16 -0
  26. data/examples/integrations/dynamic-rack-opal-sprockets-server/app/application.rb +6 -0
  27. data/examples/integrations/dynamic-rack-opal-sprockets-server/config.ru +9 -0
  28. data/examples/integrations/dynamic-roda-roda-sprockets/.gitignore +1 -0
  29. data/examples/integrations/dynamic-roda-roda-sprockets/Gemfile +7 -0
  30. data/examples/integrations/dynamic-roda-roda-sprockets/README.md +22 -0
  31. data/examples/integrations/dynamic-roda-roda-sprockets/Rakefile +4 -0
  32. data/examples/integrations/dynamic-roda-roda-sprockets/app/application.rb +6 -0
  33. data/examples/integrations/dynamic-roda-roda-sprockets/app.rb +32 -0
  34. data/examples/integrations/dynamic-roda-roda-sprockets/config.ru +3 -0
  35. data/examples/integrations/dynamic-roda-tilt/.gitignore +1 -0
  36. data/examples/integrations/dynamic-roda-tilt/Gemfile +8 -0
  37. data/examples/integrations/dynamic-roda-tilt/README.md +17 -0
  38. data/examples/integrations/dynamic-roda-tilt/Rakefile +6 -0
  39. data/examples/integrations/dynamic-roda-tilt/app/application.rb +6 -0
  40. data/examples/integrations/dynamic-roda-tilt/app.rb +50 -0
  41. data/examples/integrations/dynamic-roda-tilt/config.ru +3 -0
  42. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/Gemfile +7 -0
  43. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/README.md +16 -0
  44. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/app/application.rb +6 -0
  45. data/examples/integrations/dynamic-sinatra-opal-sprockets-server/config.ru +29 -0
  46. data/examples/integrations/static-bash/.gitignore +2 -0
  47. data/examples/integrations/static-bash/Gemfile +3 -0
  48. data/examples/integrations/static-bash/README.md +8 -0
  49. data/examples/integrations/static-bash/app/application.rb +6 -0
  50. data/examples/integrations/static-bash/build.sh +4 -0
  51. data/examples/integrations/static-bash/index.html +10 -0
  52. data/examples/integrations/static-bash-opal-parser/.gitignore +3 -0
  53. data/examples/integrations/static-bash-opal-parser/Gemfile +3 -0
  54. data/examples/integrations/static-bash-opal-parser/README.md +10 -0
  55. data/examples/integrations/static-bash-opal-parser/build.sh +4 -0
  56. data/examples/integrations/static-bash-opal-parser/index.html +19 -0
  57. data/examples/integrations/static-rake/.gitignore +1 -0
  58. data/examples/integrations/static-rake/Gemfile +4 -0
  59. data/examples/integrations/static-rake/README.md +7 -0
  60. data/examples/integrations/static-rake/Rakefile +10 -0
  61. data/examples/integrations/static-rake/app/application.rb +6 -0
  62. data/examples/integrations/static-rake/index.html +9 -0
  63. data/examples/integrations/static-rake-guard/.gitignore +1 -0
  64. data/examples/integrations/static-rake-guard/Gemfile +6 -0
  65. data/examples/integrations/static-rake-guard/Guardfile +3 -0
  66. data/examples/integrations/static-rake-guard/README.md +10 -0
  67. data/examples/integrations/static-rake-guard/Rakefile +10 -0
  68. data/examples/integrations/static-rake-guard/app/application.rb +6 -0
  69. data/examples/integrations/static-rake-guard/index.html +9 -0
  70. data/examples/svg/.gitignore +1 -0
  71. data/examples/svg/Gemfile +4 -0
  72. data/examples/svg/README.md +7 -0
  73. data/examples/svg/Rakefile +10 -0
  74. data/examples/svg/app/application.rb +11 -0
  75. data/examples/svg/index.html +17 -0
  76. data/examples/svg/index.svg +6 -0
  77. data/index.html.erb +8 -6
  78. data/lib/opal-browser.rb +1 -0
  79. data/opal/browser/animation_frame.rb +26 -1
  80. data/opal/browser/audio/node.rb +121 -0
  81. data/opal/browser/audio/param_schedule.rb +43 -0
  82. data/opal/browser/audio.rb +66 -0
  83. data/opal/browser/blob.rb +94 -0
  84. data/opal/browser/canvas/data.rb +1 -11
  85. data/opal/browser/canvas/gradient.rb +1 -11
  86. data/opal/browser/canvas/style.rb +3 -11
  87. data/opal/browser/canvas/text.rb +1 -11
  88. data/opal/browser/canvas.rb +17 -13
  89. data/opal/browser/console.rb +3 -1
  90. data/opal/browser/cookies.rb +78 -42
  91. data/opal/browser/crypto.rb +79 -0
  92. data/opal/browser/css/declaration.rb +1 -1
  93. data/opal/browser/css/rule.rb +1 -1
  94. data/opal/browser/css/style_sheet.rb +2 -2
  95. data/opal/browser/css.rb +23 -7
  96. data/opal/browser/database/sql.rb +193 -0
  97. data/opal/browser/delay.rb +41 -7
  98. data/opal/browser/dom/attribute.rb +13 -12
  99. data/opal/browser/dom/builder.rb +31 -17
  100. data/opal/browser/dom/document.rb +174 -42
  101. data/opal/browser/dom/document_fragment.rb +18 -0
  102. data/opal/browser/dom/document_or_shadow_root.rb +19 -0
  103. data/opal/browser/dom/element/attributes.rb +111 -0
  104. data/opal/browser/dom/element/button.rb +31 -0
  105. data/opal/browser/dom/element/custom.rb +177 -0
  106. data/opal/browser/dom/element/data.rb +82 -0
  107. data/opal/browser/dom/element/editable.rb +47 -0
  108. data/opal/browser/dom/element/form.rb +38 -0
  109. data/opal/browser/dom/element/iframe.rb +37 -0
  110. data/opal/browser/dom/element/image.rb +2 -0
  111. data/opal/browser/dom/element/input.rb +48 -1
  112. data/opal/browser/dom/element/media.rb +17 -0
  113. data/opal/browser/dom/element/offset.rb +5 -0
  114. data/opal/browser/dom/element/position.rb +11 -2
  115. data/opal/browser/dom/element/scroll.rb +123 -24
  116. data/opal/browser/dom/element/select.rb +42 -0
  117. data/opal/browser/dom/element/size.rb +17 -0
  118. data/opal/browser/dom/element/template.rb +11 -0
  119. data/opal/browser/dom/element/textarea.rb +26 -0
  120. data/opal/browser/dom/element.rb +468 -238
  121. data/opal/browser/dom/mutation_observer.rb +4 -4
  122. data/opal/browser/dom/node.rb +142 -60
  123. data/opal/browser/dom/node_set.rb +73 -44
  124. data/opal/browser/dom/shadow_root.rb +12 -0
  125. data/opal/browser/dom/text.rb +2 -2
  126. data/opal/browser/dom.rb +40 -16
  127. data/opal/browser/effects.rb +180 -3
  128. data/opal/browser/event/all.rb +26 -0
  129. data/opal/browser/{dom/event → event}/animation.rb +4 -2
  130. data/opal/browser/{dom/event → event}/audio_processing.rb +4 -2
  131. data/opal/browser/{dom/event → event}/base.rb +98 -9
  132. data/opal/browser/{dom/event → event}/before_unload.rb +4 -2
  133. data/opal/browser/{dom/event → event}/clipboard.rb +11 -2
  134. data/opal/browser/{dom/event → event}/close.rb +4 -2
  135. data/opal/browser/{dom/event → event}/composition.rb +4 -2
  136. data/opal/browser/{dom/event → event}/custom.rb +3 -3
  137. data/opal/browser/event/data_transfer.rb +95 -0
  138. data/opal/browser/{dom/event → event}/device_light.rb +4 -2
  139. data/opal/browser/{dom/event → event}/device_motion.rb +4 -2
  140. data/opal/browser/{dom/event → event}/device_orientation.rb +4 -2
  141. data/opal/browser/{dom/event → event}/device_proximity.rb +4 -2
  142. data/opal/browser/{dom/event → event}/drag.rb +11 -7
  143. data/opal/browser/{dom/event → event}/focus.rb +4 -2
  144. data/opal/browser/{dom/event → event}/gamepad.rb +5 -3
  145. data/opal/browser/{dom/event → event}/hash_change.rb +4 -2
  146. data/opal/browser/{dom/event → event}/keyboard.rb +16 -3
  147. data/opal/browser/{dom/event → event}/message.rb +4 -2
  148. data/opal/browser/{dom/event → event}/mouse.rb +12 -8
  149. data/opal/browser/{dom/event → event}/page_transition.rb +4 -2
  150. data/opal/browser/{dom/event → event}/pop_state.rb +4 -2
  151. data/opal/browser/{dom/event → event}/progress.rb +4 -2
  152. data/opal/browser/{dom/event → event}/sensor.rb +4 -2
  153. data/opal/browser/{dom/event → event}/storage.rb +4 -2
  154. data/opal/browser/{dom/event → event}/touch.rb +4 -2
  155. data/opal/browser/{dom/event → event}/ui.rb +2 -2
  156. data/opal/browser/{dom/event → event}/wheel.rb +4 -2
  157. data/opal/browser/event.rb +163 -0
  158. data/opal/browser/event_source.rb +2 -2
  159. data/opal/browser/form_data.rb +225 -0
  160. data/opal/browser/history.rb +4 -8
  161. data/opal/browser/http/binary.rb +1 -0
  162. data/opal/browser/http/headers.rb +16 -2
  163. data/opal/browser/http/request.rb +46 -48
  164. data/opal/browser/http/response.rb +5 -1
  165. data/opal/browser/http.rb +25 -2
  166. data/opal/browser/immediate.rb +9 -5
  167. data/opal/browser/interval.rb +34 -11
  168. data/opal/browser/location.rb +7 -1
  169. data/opal/browser/navigator.rb +127 -7
  170. data/opal/browser/polyfill/visual_viewport.rb +216 -0
  171. data/opal/browser/screen.rb +3 -3
  172. data/opal/browser/setup/base.rb +6 -0
  173. data/opal/browser/setup/full.rb +13 -0
  174. data/opal/browser/setup/large.rb +17 -0
  175. data/opal/browser/setup/mini.rb +8 -0
  176. data/opal/browser/setup/traditional.rb +10 -0
  177. data/opal/browser/socket.rb +8 -4
  178. data/opal/browser/storage.rb +53 -35
  179. data/opal/browser/support.rb +72 -5
  180. data/opal/browser/utils.rb +94 -14
  181. data/opal/browser/version.rb +1 -1
  182. data/opal/browser/visual_viewport.rb +39 -0
  183. data/opal/browser/window/size.rb +31 -3
  184. data/opal/browser/window/view.rb +15 -0
  185. data/opal/browser/window.rb +46 -25
  186. data/opal/browser.rb +1 -10
  187. data/opal/opal-browser.rb +1 -0
  188. data/opal-browser.gemspec +3 -3
  189. data/spec/database/sql_spec.rb +139 -0
  190. data/spec/delay_spec.rb +41 -0
  191. data/spec/dom/attribute_spec.rb +49 -0
  192. data/spec/dom/builder_spec.rb +25 -8
  193. data/spec/dom/document_spec.rb +22 -0
  194. data/spec/dom/element/attributes_spec.rb +52 -0
  195. data/spec/dom/element/custom_spec.rb +106 -0
  196. data/spec/dom/element/subclass_spec.rb +144 -0
  197. data/spec/dom/element_spec.rb +181 -4
  198. data/spec/dom/mutation_observer_spec.rb +12 -8
  199. data/spec/dom/node_set_spec.rb +44 -0
  200. data/spec/dom/node_spec.rb +48 -0
  201. data/spec/dom_spec.rb +8 -0
  202. data/spec/event_source_spec.rb +15 -12
  203. data/spec/{dom/event_spec.rb → event_spec.rb} +44 -15
  204. data/spec/history_spec.rb +23 -19
  205. data/spec/http_spec.rb +19 -31
  206. data/spec/immediate_spec.rb +5 -4
  207. data/spec/interval_spec.rb +59 -0
  208. data/spec/native_cached_wrapper_spec.rb +46 -0
  209. data/spec/runner.rb +62 -69
  210. data/spec/socket_spec.rb +16 -12
  211. data/spec/spec_helper.rb +2 -5
  212. data/spec/spec_helper_promise.rb.erb +25 -0
  213. data/spec/storage_spec.rb +1 -1
  214. metadata +172 -50
  215. data/.travis.yml +0 -60
  216. data/opal/browser/dom/event.rb +0 -253
  217. data/opal/browser/http/parameters.rb +0 -8
  218. data/opal/browser/window/scroll.rb +0 -59
@@ -9,11 +9,11 @@ class MutationObserver
9
9
  Browser.supports? :MutationObserver
10
10
  end
11
11
 
12
- include Native
12
+ include Native::Wrapper
13
13
 
14
14
  # Encapsulates a recorded change.
15
15
  class Record
16
- include Native
16
+ include Browser::NativeCachedWrapper
17
17
 
18
18
  # @!attribute [r] type
19
19
  # @return [:attributes, :tree, :cdata] the type of the recorded change
@@ -49,7 +49,7 @@ class MutationObserver
49
49
  []
50
50
  end
51
51
 
52
- NodeSet.new($document, array)
52
+ NodeSet[array]
53
53
  end
54
54
 
55
55
  # @!attribute [r] removed
@@ -61,7 +61,7 @@ class MutationObserver
61
61
  []
62
62
  end
63
63
 
64
- NodeSet.new($document, array)
64
+ NodeSet[array]
65
65
  end
66
66
 
67
67
  # @!attribute [r] target
@@ -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,20 +38,27 @@ 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]
44
49
  def ==(other)
45
- `#@native === #{Native.try_convert(other)}`
50
+ `#@native === #{Native.convert(other)}`
46
51
  end
47
52
 
48
- # Return true if the node name matches the given name case-insensitively.
53
+ # Initialize a new node after `#dup` or `#clone`.
49
54
  #
50
- # @param name [String] the name to match with
55
+ # This method is not to be called directly. Use `Node#dup` or
56
+ # `Node#clone`.
51
57
  #
52
- # @return [Boolean]
53
- def =~(name)
54
- self.name.downcase == name.downcase
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)`
55
62
  end
56
63
 
57
64
  # Append a child to the node.
@@ -65,36 +72,79 @@ class Node
65
72
  #
66
73
  # @return [self]
67
74
  def <<(node)
68
- if native?(node)
75
+ if Opal.respond_to? node, :each
76
+ node.each { |n| self << n }
77
+ return self
78
+ elsif Opal.respond_to? node, :to_dom
79
+ node = node.to_dom(document)
80
+ end
81
+
82
+ unless native?(node)
83
+ if String === node
84
+ node = `#@native.ownerDocument.createTextNode(node)`
85
+ else
86
+ node = Native.convert(node)
87
+ end
88
+ end
89
+
90
+ `#@native.appendChild(node)`
91
+
92
+ self
93
+ end
94
+
95
+ def >>(node)
96
+ if Opal.respond_to? node, :each
97
+ node.each { |n| self >> n }
98
+ return self
99
+ elsif Opal.respond_to? node, :to_dom
100
+ node = node.to_dom(document)
101
+ end
102
+
103
+ unless native?(node)
104
+ if String === node
105
+ node = `#@native.ownerDocument.createTextNode(node)`
106
+ else
107
+ node = Native.convert(node)
108
+ end
109
+ end
110
+
111
+ if `#@native.firstChild == null`
69
112
  `#@native.appendChild(node)`
70
- elsif node.respond_to? :each
71
- node.each { |n| add_child(n) }
72
- elsif String === node
73
- `#@native.appendChild(#@native.ownerDocument.createTextNode(node))`
74
113
  else
75
- `#@native.appendChild(#{Native.convert(node)})`
114
+ `#@native.insertBefore(node, #@native.firstChild)`
76
115
  end
77
116
 
78
117
  self
79
118
  end
80
119
 
81
- alias add_child <<
120
+ def add_child(node = nil, &block)
121
+ unless node
122
+ node = DOM(&block)
123
+ end
124
+
125
+ self << node
126
+ end
82
127
 
83
128
  # Add the passed node after this one.
84
129
  #
85
130
  # When passing a {String} a text node will be created.
86
131
  #
87
132
  # @param node [String, Node, #to_n] the node to add
88
- def add_next_sibling(node)
89
- if native?(node)
90
- `#@native.parentNode.insertBefore(node, #@native.nextSibling)`
91
- elsif String === node
92
- `#@native.parentNode.insertBefore(
93
- #@native.ownerDocument.createTextNode(node), #@native.nextSibling)`
94
- else
95
- `#@native.parentNode.insertBefore(#{Native.convert(node)},
96
- #@native.nextSibling)`
133
+ def add_next_sibling(node = nil, &block)
134
+ unless node
135
+ node = DOM(&block)
136
+ end
137
+ node = node.to_dom(document) if Opal.respond_to? node, :to_dom
138
+
139
+ unless native?(node)
140
+ if String === node
141
+ node = `#@native.ownerDocument.createTextNode(node)`
142
+ else
143
+ node = Native.convert(node)
144
+ end
97
145
  end
146
+
147
+ `#@native.parentNode.insertBefore(node, #@native.nextSibling)`
98
148
  end
99
149
 
100
150
  # Add the passed node before this one.
@@ -102,15 +152,21 @@ class Node
102
152
  # When passing a {String} a text node will be created.
103
153
  #
104
154
  # @param node [String, Node, #to_n] the node to add
105
- def add_previous_sibling(node)
106
- if native?(node)
107
- `#@native.parentNode.insertBefore(node, #@native)`
108
- elsif String === node
109
- `#@native.parentNode.insertBefore(
110
- #@native.ownerDocument.createTextNode(node), #@native)`
111
- else
112
- `#@native.parentNode.insertBefore(#{Native.convert(node)}, #@native)`
155
+ def add_previous_sibling(node = nil, &block)
156
+ unless node
157
+ node = DOM(&block)
158
+ end
159
+ node = node.to_dom(document) if Opal.respond_to? node, :to_dom
160
+
161
+ unless native?(node)
162
+ if String === node
163
+ node = `#@native.ownerDocument.createTextNode(node)`
164
+ else
165
+ node = Native.convert(node)
166
+ end
113
167
  end
168
+
169
+ `#@native.parentNode.insertBefore(node, #@native)`
114
170
  end
115
171
 
116
172
  alias after add_next_sibling
@@ -119,7 +175,8 @@ class Node
119
175
  #
120
176
  # @param node [Node] the node to append to
121
177
  def append_to(node)
122
- node.add_child(self)
178
+ node << self
179
+ self
123
180
  end
124
181
 
125
182
  # Get an array of ancestors.
@@ -130,7 +187,7 @@ class Node
130
187
  #
131
188
  # @return [NodeSet]
132
189
  def ancestors(expression = nil)
133
- return NodeSet.new(document) unless parent
190
+ return NodeSet[] unless parent
134
191
 
135
192
  parents = [parent]
136
193
 
@@ -143,12 +200,14 @@ class Node
143
200
  end
144
201
 
145
202
  if expression
146
- parents.select! {|p|
147
- p.matches? expression
148
- }
203
+ parents.select! { |p| p =~ expression }
149
204
  end
150
205
 
151
- NodeSet.new document, parents
206
+ NodeSet.new(parents)
207
+ end
208
+
209
+ def attached?
210
+ `#@native.isConnected`
152
211
  end
153
212
 
154
213
  alias before add_previous_sibling
@@ -156,11 +215,12 @@ class Node
156
215
  # Remove the node from its parent.
157
216
  def remove
158
217
  parent.remove_child(self) if parent
218
+ self
159
219
  end
160
220
 
161
221
  # Remove all the children of the node.
162
222
  def clear
163
- children.each(&:remove)
223
+ children.remove
164
224
  end
165
225
 
166
226
  # @!attribute content
@@ -209,7 +269,7 @@ class Node
209
269
  # @!attribute children
210
270
  # @return [NodeSet] the children of the node
211
271
  def children
212
- NodeSet.new(document, Native::Array.new(`#@native.childNodes`))
272
+ NodeSet[Native::Array.new(`#@native.childNodes`)]
213
273
  end
214
274
 
215
275
  def children=(node)
@@ -221,12 +281,22 @@ class Node
221
281
  node_type == COMMENT_NODE
222
282
  end
223
283
 
224
- # @!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
225
290
  # @return [Document?] the document the node is attached to
226
291
  def document
227
292
  DOM(`#@native.ownerDocument`) if defined?(`#@native.ownerDocument`)
228
293
  end
229
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
+
230
300
  # Return true if the node is a document.
231
301
  def document?
232
302
  node_type == DOCUMENT_NODE
@@ -258,16 +328,6 @@ class Node
258
328
  node_type == DOCUMENT_FRAGMENT_NODE
259
329
  end
260
330
 
261
- # @!attribute inner_html
262
- # @return [String] the inner HTML of the node
263
- def inner_html
264
- `#@native.innerHTML`
265
- end
266
-
267
- def inner_html=(value)
268
- `#@native.innerHTML = #{value}`
269
- end
270
-
271
331
  alias inner_text content
272
332
  alias inner_text= content=
273
333
 
@@ -277,13 +337,6 @@ class Node
277
337
  element_children.last
278
338
  end
279
339
 
280
- # Check if the node matches the given CSS selector.
281
- #
282
- # @param expression [String] the CSS selector
283
- def matches?(expression)
284
- false
285
- end
286
-
287
340
  # @!attribute name
288
341
  # @return [String] the name of the node
289
342
  def name
@@ -332,6 +385,14 @@ class Node
332
385
  `#@native.nodeType`
333
386
  end
334
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
+
335
396
  # @!attribute parent
336
397
  # @return [Element?] the parent of the node
337
398
  def parent
@@ -339,7 +400,7 @@ class Node
339
400
  end
340
401
 
341
402
  def parent=(node)
342
- `#@native.parentNode = #{Native.try_convert(node)}`
403
+ `#@native.parentNode = #{Native.convert(node)}`
343
404
  end
344
405
 
345
406
  def parse(text, options = {})
@@ -350,6 +411,14 @@ class Node
350
411
  raise NotImplementedError
351
412
  end
352
413
 
414
+ # Prepend the node to the passed one.
415
+ #
416
+ # @param node [Node] the node to prepend to
417
+ def prepend_to(node)
418
+ node >> self
419
+ self
420
+ end
421
+
353
422
  # @!attribute previous
354
423
  # @return [Node?] the previous sibling of the node
355
424
  def previous
@@ -375,6 +444,7 @@ class Node
375
444
  # Remove the given node from the children of this node.
376
445
  def remove_child(node)
377
446
  `#@native.removeChild(#{Native.try_convert(node)})`
447
+ self
378
448
  end
379
449
 
380
450
  # Replace the node with the given one.
@@ -384,11 +454,23 @@ class Node
384
454
  # @param node [Node] the node to replace with
385
455
  # @return [Node] the passed node
386
456
  def replace(node)
387
- `#@native.parentNode.replaceChild(#@native, #{Native.try_convert(node)})`
457
+ node = node.to_dom(document) if Opal.respond_to? node, :to_dom
458
+
459
+ unless native?(node)
460
+ if String === node
461
+ node = `#@native.ownerDocument.createTextNode(node)`
462
+ else
463
+ node = Native.convert(node)
464
+ end
465
+ end
388
466
 
389
- node
467
+ `#@native.parentNode.replaceChild(node, #@native)`
468
+
469
+ DOM(node)
390
470
  end
391
471
 
472
+ alias replace_with replace
473
+
392
474
  alias text content
393
475
  alias text= content=
394
476
 
@@ -1,25 +1,20 @@
1
1
  module Browser; module DOM
2
2
 
3
+ # Allows manipulation of a set of {Node}s.
3
4
  class NodeSet
4
- attr_reader :document
5
-
6
- def initialize(document, list = [])
7
- @document = document
8
- @literal = []
9
-
10
- list.each {|el|
11
- if NodeSet === el
12
- @literal.concat(el.to_a)
13
- else
14
- @literal.push DOM(Native.convert(el))
15
- end
16
- }
5
+ # Create a new {NodeSet} from the given nodes.
6
+ #
7
+ # Note that the nodes are flattened and converted with DOM automatically,
8
+ # this means you can pass {NodeSet}s and {Native::Array}s as well.
9
+ def self.[](*nodes)
10
+ new(nodes.flatten.map { |x| DOM(Native.convert(x)) }.uniq)
17
11
  end
18
12
 
19
- def respond_to_missing?(name)
20
- @literal.respond_to?(name)
13
+ def initialize(literal)
14
+ @literal = literal
21
15
  end
22
16
 
17
+ # Any other method will be called on every node in the set.
23
18
  def method_missing(name, *args, &block)
24
19
  unless @literal.respond_to? name
25
20
  each {|el|
@@ -34,59 +29,93 @@ class NodeSet
34
29
  if `result === #@literal`
35
30
  self
36
31
  elsif Array === result
37
- NodeSet.new(@document, result)
32
+ NodeSet.new(result)
38
33
  else
39
34
  result
40
35
  end
41
36
  end
42
37
 
43
- def dup
44
- NodeSet.new(document, to_ary.dup)
38
+ def respond_to_missing?(name, *)
39
+ @literal.respond_to?(name)
45
40
  end
46
41
 
47
- def filter(expression)
48
- NodeSet.new(document, @literal.select { |node| node.matches?(expression) })
49
- end
42
+ # Get the first node matching the given CSS selectors.
43
+ #
44
+ # @param rules [Array<String>] the CSS selectors to match with
45
+ #
46
+ # @return [Node?]
47
+ def at_css(*rules)
48
+ each {|node|
49
+ if node = node.at_css(*rules)
50
+ return node
51
+ end
52
+ }
50
53
 
51
- def after(node)
52
- last.after node
54
+ nil
53
55
  end
54
56
 
55
- def at(path)
56
- raise NotImplementedError
57
- end
57
+ # Get the first node matching the given XPath.
58
+ #
59
+ # @param paths [Array<String>] the XPath to match with
60
+ #
61
+ # @return [Node?]
62
+ def at_xpath(*paths)
63
+ each {|node|
64
+ if node = node.at_xpath(*paths)
65
+ return node
66
+ end
67
+ }
58
68
 
59
- def at_css(*rules)
60
- raise NotImplementedError
69
+ nil
61
70
  end
62
71
 
63
- def at_xpath(*paths)
64
- raise NotImplementedError
72
+ # Query for children matching the given CSS selector.
73
+ #
74
+ # @param path [String] the CSS selector
75
+ #
76
+ # @return [NodeSet]
77
+ def css(path)
78
+ NodeSet[@literal.map {|node|
79
+ node.css(path)
80
+ }]
65
81
  end
66
82
 
67
- def before
68
- first.before
83
+ # Create another {NodeSet} with all the nodes that match the given
84
+ # expression.
85
+ #
86
+ # @param expression [String] a CSS selector
87
+ #
88
+ # @return [NodeSet] the new {NodeSet} with the matching nodes
89
+ def filter(expression)
90
+ NodeSet[@literal.select { |node| node =~ expression }]
69
91
  end
70
92
 
71
- def children
72
- result = NodeSet.new(document)
73
-
74
- each { |n| result.concat(n.children) }
75
-
76
- result
93
+ # Search for multiple selectors
94
+ def search(*what)
95
+ NodeSet[@literal.map { |node| node.search(*what) }]
77
96
  end
78
97
 
79
- def css(*paths)
80
- raise NotImplementedError
98
+ # Outer HTML of the entire nodeset
99
+ def outer_html
100
+ @literal.map(&:outer_html).join
81
101
  end
82
102
 
83
- def search(*what)
84
- map { |n| n.search(*what) }.flatten.uniq
103
+ # Query for children matching the given XPath.
104
+ #
105
+ # @param path [String] the XPath
106
+ #
107
+ # @return [NodeSet]
108
+ def xpath(path)
109
+ NodeSet[@literal.map {|node|
110
+ node.xpath(path)
111
+ }]
85
112
  end
86
113
 
87
- def inspect
88
- "#<DOM::NodeSet: #{@literal.inspect[1 .. -2]}"
114
+ def to_ary
115
+ @literal
89
116
  end
117
+
118
+ alias to_a to_ary
90
119
  end
91
120
 
92
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
@@ -1,4 +1,3 @@
1
- require 'browser/dom/event'
2
1
  require 'browser/dom/node_set'
3
2
  require 'browser/dom/node'
4
3
  require 'browser/dom/attribute'
@@ -7,9 +6,10 @@ require 'browser/dom/text'
7
6
  require 'browser/dom/cdata'
8
7
  require 'browser/dom/comment'
9
8
  require 'browser/dom/element'
9
+ require 'browser/dom/document_or_shadow_root'
10
10
  require 'browser/dom/document'
11
11
  require 'browser/dom/document_fragment'
12
- require 'browser/dom/builder'
12
+ require 'browser/dom/shadow_root'
13
13
  require 'browser/dom/mutation_observer'
14
14
 
15
15
  module Kernel
@@ -34,29 +34,59 @@ module Kernel
34
34
  DOM(`doc`)
35
35
  end
36
36
 
37
- # 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]
38
63
  #
39
- # @return [Browser::DOM::Node]
40
64
  def DOM(*args, &block)
41
65
  if block
42
66
  document = args.shift || $document
43
- element = args.shift
44
-
45
- roots = Browser::DOM::Builder.new(document, element, &block).to_a
67
+ roots = Browser::DOM::Builder.new(document, &block).to_a
46
68
 
47
69
  if roots.length == 1
48
70
  roots.first
49
71
  else
50
- Browser::DOM::NodeSet.new(document, roots)
72
+ Browser::DOM::NodeSet.new(roots)
51
73
  end
52
74
  else
53
75
  what = args.shift
54
76
  document = args.shift || $document
55
77
 
56
- 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)
57
83
  Browser::DOM::Node.new(what)
58
84
  elsif Browser::DOM::Node === what
59
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
60
90
  elsif String === what
61
91
  %x{
62
92
  var doc = #{Native.try_convert(document)}.createElement('div');
@@ -65,7 +95,7 @@ module Kernel
65
95
  return #{DOM(`doc.childNodes.length == 1 ? doc.childNodes[0] : doc`)};
66
96
  }
67
97
  else
68
- raise ArgumentError, "argument not DOM convertible"
98
+ raise ArgumentError, 'argument is not DOM convertible'
69
99
  end
70
100
  end
71
101
  end
@@ -74,12 +104,6 @@ end
74
104
  module Browser
75
105
 
76
106
  class Window
77
- include DOM::Event::Target
78
-
79
- target {|value|
80
- $window if `#{value} == window`
81
- }
82
-
83
107
  # Get the {DOM::Document} for this window.
84
108
  #
85
109
  # @return [DOM::Document]