opal-browser 0.2.0.beta1 → 0.3.2

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