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
@@ -0,0 +1,111 @@
1
+ module Browser; module DOM; class Element < Node
2
+
3
+ class Attributes
4
+ attr_reader :namespace
5
+
6
+ def initialize(element, options)
7
+ @element = element
8
+ @native = element.to_n
9
+ @namespace = options[:namespace]
10
+ end
11
+
12
+ if Browser.supports?('Element.className') || Browser.supports?('Element.htmlFor')
13
+ def [](name, options = {})
14
+ if name == :class && Browser.supports?('Element.className')
15
+ name = :className
16
+ elsif name == :for && Browser.supports?('Element.htmlFor')
17
+ name = :htmlFor
18
+ end
19
+
20
+ if namespace = options[:namespace] || @namespace
21
+ `#@native.getAttributeNS(#{namespace.to_s}, #{name.to_s}) || nil`
22
+ else
23
+ `#@native.getAttribute(#{name.to_s}) || nil`
24
+ end
25
+ end
26
+
27
+ def []=(name, value, options = {})
28
+ if name == :class && Browser.supports?('Element.className')
29
+ name = :className
30
+ elsif name == :for && Browser.supports?('Element.htmlFor')
31
+ name = :htmlFor
32
+ end
33
+
34
+ if namespace = options[:namespace] || @namespace
35
+ if value
36
+ `#@native.setAttributeNS(#{namespace.to_s}, #{name.to_s}, #{value})`
37
+ else
38
+ `#@native.removeAttributeNS(#{namespace.to_s}, #{name.to_s})`
39
+ end
40
+ else
41
+ if value
42
+ `#@native.setAttribute(#{name.to_s}, #{value.to_s})`
43
+ else
44
+ `#@native.removeAttribute(#{name.to_s})`
45
+ end
46
+ end
47
+ end
48
+ else
49
+ def [](name, options = {})
50
+ if namespace = options[:namespace] || @namespace
51
+ `#@native.getAttributeNS(#{namespace.to_s}, #{name.to_s}) || nil`
52
+ else
53
+ `#@native.getAttribute(#{name.to_s}) || nil`
54
+ end
55
+ end
56
+
57
+ def []=(name, value, options = {})
58
+ if namespace = options[:namespace] || @namespace
59
+ if value
60
+ `#@native.setAttributeNS(#{namespace.to_s}, #{name.to_s}, #{value})`
61
+ else
62
+ `#@native.removeAttributeNS(#{namespace.to_s}, #{name.to_s})`
63
+ end
64
+ else
65
+ if value
66
+ `#@native.setAttribute(#{name.to_s}, #{value.to_s})`
67
+ else
68
+ `#@native.removeAttribute(#{name.to_s})`
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ # Deletes an attribute with a given name
75
+ # @return [String] an attribute value before deletion
76
+ def delete(name)
77
+ attr = self[name]
78
+ self[name] = nil
79
+ attr
80
+ end
81
+
82
+ include Enumerable
83
+
84
+ def each(&block)
85
+ return enum_for :each unless block_given?
86
+
87
+ @element.attribute_nodes.each {|attr|
88
+ yield attr.name, attr.value
89
+ }
90
+
91
+ self
92
+ end
93
+
94
+ alias get []
95
+
96
+ def has_key?(name)
97
+ !!self[name]
98
+ end
99
+
100
+ def merge!(hash)
101
+ hash.each {|name, value|
102
+ self[name] = value
103
+ }
104
+
105
+ self
106
+ end
107
+
108
+ alias set []=
109
+ end
110
+
111
+ end; end; end
@@ -0,0 +1,31 @@
1
+ module Browser; module DOM; class Element < Node
2
+
3
+ class Button < Element
4
+ def_selector "button"
5
+
6
+ def disabled?
7
+ `#@native.disabled`
8
+ end
9
+
10
+ def disabled=(value)
11
+ `#@native.disabled = #{value}`
12
+ end
13
+
14
+ def autofocus?
15
+ `#@native.autofocus`
16
+ end
17
+
18
+ def autofocus=(value)
19
+ `#@native.autofocus = #{value}`
20
+ end
21
+
22
+ def name_
23
+ `#@native.name`
24
+ end
25
+
26
+ def name_=(value)
27
+ `#@native.name = #{value}`
28
+ end
29
+ end
30
+
31
+ end; end; end
@@ -0,0 +1,177 @@
1
+ # use_strict: true
2
+ # helpers: truthy
3
+
4
+ module Browser; module DOM; class Element < Node
5
+
6
+ # CustomElements implementation for opal-browser. See examples/custom_elements/.
7
+ #
8
+ # @see https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
9
+ # @abstract This class should not be used directly. Please extend it and implement needed methods.
10
+ class Custom < Element
11
+ # The reason why we wrap class definition with an eval is kind of selfish. I want it to work
12
+ # with opal-optimizer which doesn't support the new class syntax. I would do it with prototypes,
13
+ # but the prototypes system is so messy I gave up.
14
+ #
15
+ # Therefore, for it to be cleaned up, one of those two must happen:
16
+ # - we raise the supported ES version in Opal and we implement those ES syntax features in
17
+ # rkelly-turbo. And then we remove the polyfill.
18
+ # - we reimplement it in terms of prototypes.
19
+ %x{
20
+ var make_custom_class = Function('self,base_class',
21
+ '"use strict"; \
22
+ var klass = class extends base_class { \
23
+ constructor() { \
24
+ super(); \
25
+ self.$_dispatch_constructor(this); \
26
+ } \
27
+ connectedCallback() { \
28
+ return this.$$opal_native_cached.$attached(); \
29
+ } \
30
+ disconnectedCallback() { \
31
+ return this.$$opal_native_cached.$detached(); \
32
+ } \
33
+ adoptedCallback() { \
34
+ return this.$$opal_native_cached.$adopted(); \
35
+ } \
36
+ attributeChangedCallback(attr, from, to) { \
37
+ if (from === null) from = Opal.nil; \
38
+ if (to === null) to = Opal.nil; \
39
+ return this.$$opal_native_cached.$attribute_changed(attr, from, to); \
40
+ } \
41
+ \
42
+ static get observedAttributes() { \
43
+ return self.$observed_attributes(); \
44
+ } \
45
+ }; \
46
+ klass.$$opal_class = self; \
47
+ return klass;'
48
+ );
49
+ } if Browser.supports? 'Custom Elements' #'
50
+
51
+ module ClassMethods
52
+ if Browser.supports? 'Custom Elements'
53
+ # Defines a new custom element. This should come as the last call
54
+ # in the class definition, because at this point the methods may
55
+ # be called!
56
+ #
57
+ # @opalopt uses:_dispatch_constructor,attached,detached,adopted,attribute_changed,observed_attributes
58
+ def def_custom(tag_name, base_class: nil, extends: nil)
59
+ if `base_class !== nil`
60
+ elsif self.superclass == Custom
61
+ base_class = `HTMLElement`
62
+ elsif self.ancestors.include? Custom
63
+ base_class = `#{self.superclass}.custom_class`
64
+ else
65
+ raise ArgumentError, "You must define base_class"
66
+ end
67
+
68
+ @custom_class = `make_custom_class(self, #{base_class})`
69
+ @observed_attributes ||= []
70
+
71
+ def_selector tag_name
72
+
73
+ %x{
74
+ if ($truthy(#{extends})) customElements.define(#{tag_name}, #{@custom_class}, {extends: #{extends}});
75
+ else customElements.define(#{tag_name}, #{@custom_class});
76
+ }
77
+ end
78
+ elsif Browser.supports? 'MutationObserver'
79
+ # Can we polyfill it?
80
+ Browser::DOM::MutationObserver.new do |obs|
81
+ obs.each do |e|
82
+ target = e.target
83
+
84
+ case e.type
85
+ when :attribute
86
+ if Custom::Mixin === target && target.class.observed_attributes.include?(e.name)
87
+ target.attribute_changed(e.name, e.old, target[e.name])
88
+ end
89
+ when :tree
90
+ e.added.each { |n| n.attached_once if Custom::Mixin === n }
91
+ e.removed.each { |n| n.detached_once if Custom::Mixin === n }
92
+ end
93
+ end
94
+ end.observe($document, tree: true, children: true, attributes: :old)
95
+ end
96
+
97
+ unless Browser.supports? 'Custom Elements'
98
+ # The polyfilled implementation. Define the selector and then
99
+ # try to upgrade the elements that are already in the document.
100
+ def def_custom(tag_name, base_class: nil, extends: nil)
101
+ def_selector tag_name
102
+
103
+ $document.body.css(tag_name).each do |elem|
104
+ _dispatch_constructor(elem.to_n)&.attached_once
105
+ end
106
+ end
107
+ end
108
+
109
+ private def _dispatch_constructor(obj)
110
+ %x{
111
+ if (typeof obj.$$opal_native_cached !== 'undefined') {
112
+ delete obj.$$opal_native_cached;
113
+ return self.$new(obj);
114
+ }
115
+ else {
116
+ self.$new(obj);
117
+ return nil;
118
+ }
119
+ }
120
+ end
121
+
122
+ # This must be defined before def_custom is called!
123
+ attr_accessor :observed_attributes
124
+
125
+ attr_reader :custom_class
126
+ end
127
+
128
+ module Mixin
129
+ def self.included(klass)
130
+ klass.extend ClassMethods
131
+ end
132
+
133
+ # @abstract
134
+ def attached
135
+ end
136
+
137
+ # @abstract
138
+ def detached
139
+ end
140
+
141
+ # @abstract
142
+ def adopted
143
+ end
144
+
145
+ # Note: for this method to fire, you will need to define
146
+ # the observed attributes.
147
+ #
148
+ # @abstract
149
+ def attribute_changed(attr, from, to)
150
+ end
151
+
152
+ # Return true if the node is a custom element.
153
+ def custom?
154
+ true
155
+ end
156
+
157
+ # Those methods keep track of the attachment status of the elements,
158
+ # so that #attached/#detached isn't called twice.
159
+ unless Browser.supports? 'Custom Elements'
160
+ # @private
161
+ def attached_once
162
+ attached unless @_polyfill_attached
163
+ @_polyfill_attached = true
164
+ end
165
+
166
+ # @private
167
+ def detached_once
168
+ detached if @_polyfill_attached
169
+ @_polyfill_attached = false
170
+ end
171
+ end
172
+ end
173
+
174
+ include Mixin
175
+ end
176
+
177
+ end; end; end
@@ -0,0 +1,82 @@
1
+ module Browser; module DOM; class Element < Node
2
+
3
+ class Data
4
+ attr_reader :element
5
+
6
+ def initialize(element)
7
+ @element = element
8
+ @native = element.to_n
9
+
10
+ unless defined?(`#@native.$data`)
11
+ `#@native.$data = {}`
12
+ end
13
+ end
14
+
15
+ include Enumerable
16
+
17
+ def each(&block)
18
+ return enum_for :each unless block
19
+
20
+ %x{
21
+ var data = #@native.$data;
22
+
23
+ for (var key in data) {
24
+ #{block.call `key`, `data[key]`};
25
+ }
26
+ }
27
+
28
+ @element.attributes.each {|name, value|
29
+ if name =~ /^data-(.*)$/
30
+ block.call $1, value
31
+ end
32
+ }
33
+
34
+ self
35
+ end
36
+
37
+ def assign(data)
38
+ data.each {|name, value|
39
+ self[name] = value
40
+ }
41
+
42
+ self
43
+ end
44
+
45
+ def [](name)
46
+ if data = @element["data-#{name}"]
47
+ return data
48
+ end
49
+
50
+ %x{
51
+ var value = #@native.$data[name];
52
+
53
+ if (value === undefined) {
54
+ return nil;
55
+ }
56
+ else {
57
+ return value;
58
+ }
59
+ }
60
+ end
61
+
62
+ def []=(name, value)
63
+ `delete #@native.$data[name]`
64
+ if [true, false, nil].include?(value)
65
+ @element["data-#{name}"] = value
66
+ elsif value.respond_to? :to_str
67
+ @element["data-#{name}"] = value.to_str
68
+ elsif value.respond_to? :to_int
69
+ @element["data-#{name}"] = value.to_int.to_s
70
+ else
71
+ `#@native.$data[name] = value`
72
+ end
73
+ end
74
+
75
+ def delete(name)
76
+ data = self[name]
77
+ self[name] = nil
78
+ data
79
+ end
80
+ end
81
+
82
+ end; end; end
@@ -0,0 +1,47 @@
1
+ module Browser; module DOM
2
+
3
+ class Element < Node
4
+ # @!attribute editable
5
+ # @return [Boolean?] the value of contentEditable for this element
6
+ def editable
7
+ case `#@native.contentEditable`
8
+ when "true"
9
+ true
10
+ when "false"
11
+ false
12
+ when "inherit"
13
+ nil
14
+ end
15
+ end
16
+
17
+ def editable=(value)
18
+ value = case value
19
+ when true
20
+ "true"
21
+ when false
22
+ "false"
23
+ when nil
24
+ "inherit"
25
+ end
26
+ `#@native.contentEditable = #{value}`
27
+ end
28
+
29
+ def editable?
30
+ `#@native.isContentEditable`
31
+ end
32
+
33
+ # Execute a contentEditable command
34
+ def edit(command, value=nil)
35
+ command = command.gsub(/_./) { |i| i[1].upcase }
36
+
37
+ focus
38
+
39
+ if value
40
+ `#{document}.native.execCommand(#{command}, false, #{value})`
41
+ else
42
+ `#{document}.native.execCommand(#{command}, false)`
43
+ end
44
+ end
45
+ end
46
+
47
+ end; end
@@ -0,0 +1,38 @@
1
+ module Browser; module DOM; class Element < Node
2
+
3
+ class Form < Element
4
+ def_selector "form"
5
+
6
+ # Capture the content of this form to a new {FormData} object,
7
+ #
8
+ # @return [FormData]
9
+ def form_data
10
+ FormData.create(self)
11
+ end
12
+
13
+ # Submit a form. This will fire a submit event.
14
+ def submit
15
+ `#@native.submit()`
16
+ end
17
+
18
+ # Reset a form. This will fire a reset event.
19
+ def reset
20
+ `#@native.reset()`
21
+ end
22
+
23
+ alias_native :action
24
+ alias_native :action=
25
+ alias_native :method
26
+ alias_native :method=
27
+ alias_native :target
28
+ alias_native :target=
29
+ alias_native :encoding
30
+ alias_native :encoding=
31
+
32
+ # Return a NodeSet containing all form controls belonging to this form element.
33
+ def controls
34
+ NodeSet[Native::Array.new(`#@native.elements`)]
35
+ end
36
+ end
37
+
38
+ end; end; end
@@ -0,0 +1,37 @@
1
+ module Browser; module DOM; class Element < Node
2
+
3
+ class Iframe < Element
4
+ def_selector "iframe"
5
+
6
+ # @!attribute src
7
+ # @return [String] the URL of the page to embed
8
+ alias_native :src
9
+ alias_native :src=
10
+
11
+ # @!attribute [r] content_window
12
+ # @return [Window] window of content of this iframe
13
+ def content_window
14
+ Browser::Window.new(`#@native.contentWindow`)
15
+ end
16
+
17
+ # @!attribute [r] content_document
18
+ # @return [Document] document of content of this iframe
19
+ def content_document
20
+ DOM(`#@native.contentDocument || #@native.contentWindow.document`)
21
+ end
22
+
23
+ # Send a message to the iframe content's window.
24
+ #
25
+ # @param message [String] the message
26
+ # @param options [Hash] optional `to: target`
27
+ def send(message, options={})
28
+ content_window.send(message, options)
29
+ end
30
+ end
31
+
32
+ # Object is not an iframe, but acts the same.
33
+ class Object < Iframe
34
+ def_selector "object"
35
+ end
36
+
37
+ end; end; end
@@ -1,6 +1,8 @@
1
1
  module Browser; module DOM; class Element < Node
2
2
 
3
3
  class Image < Element
4
+ def_selector "img"
5
+
4
6
  def complete?
5
7
  `#@native.complete`
6
8
  end
@@ -1,17 +1,64 @@
1
1
  module Browser; module DOM; class Element < Node
2
2
 
3
3
  class Input < Element
4
+ def_selector "input"
5
+
4
6
  def value
5
- `#@native.value`
7
+ %x{
8
+ if (#@native.value == "") {
9
+ return nil;
10
+ }
11
+ else {
12
+ return #@native.value;
13
+ }
14
+ }
6
15
  end
7
16
 
8
17
  def value=(value)
9
18
  `#@native.value = #{value}`
10
19
  end
11
20
 
21
+ def name_
22
+ `#@native.name`
23
+ end
24
+
25
+ def type
26
+ `#@native.type`
27
+ end
28
+
29
+ def checked?
30
+ `#@native.checked`
31
+ end
32
+
33
+ def check!
34
+ `#@native.checked = 'checked'`
35
+ end
36
+
37
+ def uncheck!
38
+ `#@native.checked = ''`
39
+ end
40
+
41
+ def enabled?
42
+ `#@native.enabled`
43
+ end
44
+
45
+ def disable!
46
+ `#@native.disabled = 'disabled'`
47
+ end
48
+
49
+ def enable!
50
+ `#@native.disabled = ''`
51
+ end
52
+
12
53
  def clear
13
54
  `#@native.value = ''`
14
55
  end
56
+
57
+ # @!attribute [r] files
58
+ # @return [Array<File>] list of files attached to this {Input}
59
+ def files
60
+ Native::Array.new(`#@native.files`).map { |f| File.new(f.to_n) }
61
+ end
15
62
  end
16
63
 
17
64
  end; end; end
@@ -0,0 +1,17 @@
1
+ module Browser; module DOM; class Element < Node
2
+
3
+ class Media < Element
4
+ def play
5
+ `#@native.play()`
6
+ end
7
+ end
8
+
9
+ class Video < Media
10
+ def_selector "video"
11
+ end
12
+
13
+ class Audio < Media
14
+ def_selector "audio"
15
+ end
16
+
17
+ end; end; end
@@ -3,6 +3,7 @@ module Browser; module DOM; class Element < Node
3
3
  class Offset
4
4
  attr_reader :element
5
5
 
6
+ # @private
6
7
  def initialize(element)
7
8
  @element = element
8
9
  @native = element.to_n
@@ -12,6 +13,8 @@ class Offset
12
13
  DOM(`#@native.offsetParent || #{@element.document.root.to_n}`)
13
14
  end
14
15
 
16
+ # @!attribute x
17
+ # @return [Integer]
15
18
  def x
16
19
  get.x
17
20
  end
@@ -20,6 +23,8 @@ class Offset
20
23
  set value, nil
21
24
  end
22
25
 
26
+ # @!attribute y
27
+ # @return [Integer]
23
28
  def y
24
29
  get.y
25
30
  end
@@ -1,11 +1,15 @@
1
1
  module Browser; module DOM; class Element < Node
2
2
 
3
3
  class Position
4
+ attr_reader :element
5
+
6
+ # @private
4
7
  def initialize(element)
5
8
  @element = element
6
9
  @native = element.to_n
7
10
  end
8
11
 
12
+ # @private
9
13
  def get
10
14
  offset = @element.offset
11
15
  position = offset.get
@@ -21,14 +25,19 @@ class Position
21
25
  parent_offset.y += parent.style['border-left-width'].to_i
22
26
  end
23
27
 
24
- Browser::Position.new(position.x - parent_offset.x - @element.style['margin-left'].to_i,
25
- position.y - parent_offset.y - @element.style['margin-top'].to_i)
28
+ Browser::Position.new(
29
+ position.x - parent_offset.x - @element.style['margin-left'].to_i,
30
+ position.y - parent_offset.y - @element.style['margin-top'].to_i)
26
31
  end
27
32
 
33
+ # @!attribute [r] x
34
+ # @return [Integer] the position of the element on the x axis
28
35
  def x
29
36
  get.x
30
37
  end
31
38
 
39
+ # @!attribute [r] y
40
+ # @return [Integer] the position of the element on the y axis
32
41
  def y
33
42
  get.y
34
43
  end