opal-browser 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +11 -0
  5. data/README.md +106 -0
  6. data/Rakefile +5 -0
  7. data/config.ru +63 -0
  8. data/lib/opal/browser.rb +4 -0
  9. data/opal-browser.gemspec +23 -0
  10. data/opal/browser.rb +10 -0
  11. data/opal/browser/animation_frame.rb +29 -0
  12. data/opal/browser/canvas.rb +277 -0
  13. data/opal/browser/canvas/data.rb +73 -0
  14. data/opal/browser/canvas/gradient.rb +37 -0
  15. data/opal/browser/canvas/style.rb +123 -0
  16. data/opal/browser/canvas/text.rb +55 -0
  17. data/opal/browser/compatibility.rb +59 -0
  18. data/opal/browser/compatibility/animation_frame.rb +93 -0
  19. data/opal/browser/compatibility/dom/document/window.rb +15 -0
  20. data/opal/browser/compatibility/dom/element/css.rb +15 -0
  21. data/opal/browser/compatibility/dom/element/matches.rb +31 -0
  22. data/opal/browser/compatibility/dom/element/offset.rb +20 -0
  23. data/opal/browser/compatibility/dom/element/scroll.rb +25 -0
  24. data/opal/browser/compatibility/dom/element/style.rb +15 -0
  25. data/opal/browser/compatibility/dom/mutation_observer.rb +47 -0
  26. data/opal/browser/compatibility/http/request.rb +15 -0
  27. data/opal/browser/compatibility/immediate.rb +107 -0
  28. data/opal/browser/compatibility/window/scroll.rb +27 -0
  29. data/opal/browser/compatibility/window/size.rb +13 -0
  30. data/opal/browser/compatibility/window/view.rb +13 -0
  31. data/opal/browser/console.rb +137 -0
  32. data/opal/browser/cookies.rb +79 -0
  33. data/opal/browser/css.rb +24 -0
  34. data/opal/browser/css/declaration.rb +88 -0
  35. data/opal/browser/css/rule.rb +48 -0
  36. data/opal/browser/css/rule/style.rb +16 -0
  37. data/opal/browser/css/style_sheet.rb +83 -0
  38. data/opal/browser/css/unit.rb +188 -0
  39. data/opal/browser/dom.rb +95 -0
  40. data/opal/browser/dom/attribute.rb +19 -0
  41. data/opal/browser/dom/builder.rb +97 -0
  42. data/opal/browser/dom/cdata.rb +9 -0
  43. data/opal/browser/dom/character_data.rb +37 -0
  44. data/opal/browser/dom/comment.rb +9 -0
  45. data/opal/browser/dom/compatibility.rb +8 -0
  46. data/opal/browser/dom/document.rb +83 -0
  47. data/opal/browser/dom/document_fragment.rb +7 -0
  48. data/opal/browser/dom/element.rb +290 -0
  49. data/opal/browser/dom/element/input.rb +17 -0
  50. data/opal/browser/dom/element/offset.rb +67 -0
  51. data/opal/browser/dom/element/position.rb +37 -0
  52. data/opal/browser/dom/element/scroll.rb +49 -0
  53. data/opal/browser/dom/event.rb +240 -0
  54. data/opal/browser/dom/event/animation.rb +26 -0
  55. data/opal/browser/dom/event/audio_processing.rb +31 -0
  56. data/opal/browser/dom/event/base.rb +207 -0
  57. data/opal/browser/dom/event/before_unload.rb +13 -0
  58. data/opal/browser/dom/event/clipboard.rb +26 -0
  59. data/opal/browser/dom/event/close.rb +35 -0
  60. data/opal/browser/dom/event/composition.rb +38 -0
  61. data/opal/browser/dom/event/custom.rb +30 -0
  62. data/opal/browser/dom/event/device_light.rb +21 -0
  63. data/opal/browser/dom/event/device_motion.rb +38 -0
  64. data/opal/browser/dom/event/device_orientation.rb +36 -0
  65. data/opal/browser/dom/event/device_proximity.rb +31 -0
  66. data/opal/browser/dom/event/drag.rb +113 -0
  67. data/opal/browser/dom/event/focus.rb +23 -0
  68. data/opal/browser/dom/event/gamepad.rb +47 -0
  69. data/opal/browser/dom/event/hash_change.rb +26 -0
  70. data/opal/browser/dom/event/keyboard.rb +93 -0
  71. data/opal/browser/dom/event/message.rb +50 -0
  72. data/opal/browser/dom/event/mouse.rb +253 -0
  73. data/opal/browser/dom/event/page_transition.rb +21 -0
  74. data/opal/browser/dom/event/pop_state.rb +21 -0
  75. data/opal/browser/dom/event/progress.rb +31 -0
  76. data/opal/browser/dom/event/sensor.rb +13 -0
  77. data/opal/browser/dom/event/storage.rb +41 -0
  78. data/opal/browser/dom/event/touch.rb +69 -0
  79. data/opal/browser/dom/event/ui.rb +22 -0
  80. data/opal/browser/dom/event/wheel.rb +49 -0
  81. data/opal/browser/dom/mutation_observer.rb +118 -0
  82. data/opal/browser/dom/node.rb +317 -0
  83. data/opal/browser/dom/node_set.rb +88 -0
  84. data/opal/browser/dom/text.rb +21 -0
  85. data/opal/browser/effects.rb +39 -0
  86. data/opal/browser/event_source.rb +67 -0
  87. data/opal/browser/history.rb +54 -0
  88. data/opal/browser/http.rb +129 -0
  89. data/opal/browser/http/binary.rb +57 -0
  90. data/opal/browser/http/compatibility.rb +1 -0
  91. data/opal/browser/http/headers.rb +90 -0
  92. data/opal/browser/http/parameters.rb +8 -0
  93. data/opal/browser/http/request.rb +331 -0
  94. data/opal/browser/http/response.rb +115 -0
  95. data/opal/browser/immediate.rb +43 -0
  96. data/opal/browser/interval.rb +93 -0
  97. data/opal/browser/location.rb +77 -0
  98. data/opal/browser/navigator.rb +151 -0
  99. data/opal/browser/screen.rb +40 -0
  100. data/opal/browser/socket.rb +115 -0
  101. data/opal/browser/storage.rb +149 -0
  102. data/opal/browser/timeout.rb +60 -0
  103. data/opal/browser/utils.rb +56 -0
  104. data/opal/browser/version.rb +3 -0
  105. data/opal/browser/window.rb +113 -0
  106. data/opal/browser/window/compatibility.rb +3 -0
  107. data/opal/browser/window/scroll.rb +49 -0
  108. data/opal/browser/window/size.rb +35 -0
  109. data/opal/browser/window/view.rb +18 -0
  110. data/spec/dom/builder_spec.rb +69 -0
  111. data/spec/dom/document_spec.rb +40 -0
  112. data/spec/dom/element_spec.rb +46 -0
  113. data/spec/dom/event_spec.rb +127 -0
  114. data/spec/dom/mutation_observer_spec.rb +37 -0
  115. data/spec/dom/node_spec.rb +154 -0
  116. data/spec/dom_spec.rb +13 -0
  117. data/spec/event_source_spec.rb +42 -0
  118. data/spec/history_spec.rb +48 -0
  119. data/spec/http_spec.rb +87 -0
  120. data/spec/immediate_spec.rb +12 -0
  121. data/spec/json2.js +486 -0
  122. data/spec/sizzle.js +5 -0
  123. data/spec/socket_spec.rb +43 -0
  124. data/spec/spec_helper.rb +37 -0
  125. data/spec/storage_spec.rb +26 -0
  126. data/spec/window_spec.rb +10 -0
  127. metadata +240 -0
@@ -0,0 +1,95 @@
1
+ require 'browser/dom/event'
2
+ require 'browser/dom/node_set'
3
+ require 'browser/dom/node'
4
+ require 'browser/dom/attribute'
5
+ require 'browser/dom/character_data'
6
+ require 'browser/dom/text'
7
+ require 'browser/dom/cdata'
8
+ require 'browser/dom/comment'
9
+ require 'browser/dom/element'
10
+ require 'browser/dom/document'
11
+ require 'browser/dom/document_fragment'
12
+ require 'browser/dom/builder'
13
+ require 'browser/dom/mutation_observer'
14
+
15
+ require 'browser/dom/compatibility'
16
+
17
+ module Kernel
18
+ # Parse an XML string into a DOM usable {Browser::DOM::Document}
19
+ #
20
+ # @param what [String] the string to parse
21
+ # @return [Browser::DOM::Document] the document
22
+ def XML(what)
23
+ %x{
24
+ var doc;
25
+
26
+ if (window.DOMParser) {
27
+ doc = new DOMParser().parseFromString(what, 'text/xml');
28
+ }
29
+ else {
30
+ doc = new ActiveXObject('Microsoft.XMLDOM');
31
+ doc.async = 'false';
32
+ doc.loadXML(what);
33
+ }
34
+ }
35
+
36
+ DOM(`doc`)
37
+ end
38
+
39
+ # Wrap a native element or create a DOM tree using the {Paggio::HTML} DSL.
40
+ #
41
+ # @return [Browser::DOM::Node]
42
+ def DOM(*args, &block)
43
+ if block
44
+ document = args.shift || $document
45
+ element = args.shift
46
+
47
+ roots = Browser::DOM::Builder.new(document, element, &block).to_a
48
+
49
+ if roots.length == 1
50
+ roots.first
51
+ else
52
+ Browser::DOM::NodeSet.new(document, roots)
53
+ end
54
+ else
55
+ what = args.shift
56
+ document = args.shift || $document
57
+
58
+ if native?(what)
59
+ Browser::DOM::Node.new(what)
60
+ elsif Browser::DOM::Node === what
61
+ what
62
+ elsif String === what
63
+ %x{
64
+ var doc = #{Native.try_convert(document)}.createElement('div');
65
+ doc.innerHTML = what;
66
+
67
+ return #{DOM(`doc.childNodes.length == 1 ? doc.childNodes[0] : doc`)};
68
+ }
69
+ else
70
+ raise ArgumentError, "argument not DOM convertible"
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ module Browser
77
+
78
+ class Window
79
+ include DOM::Event::Target
80
+
81
+ target {|value|
82
+ $window if `#{value} == window`
83
+ }
84
+
85
+ # Get the {DOM::Document} for this window.
86
+ #
87
+ # @return [DOM::Document]
88
+ def document
89
+ DOM(`#@native.document`)
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ $document = $window.document
@@ -0,0 +1,19 @@
1
+ module Browser; module DOM
2
+
3
+ class Attribute
4
+ include Native
5
+
6
+ def id?
7
+ `#@native.isId`
8
+ end
9
+
10
+ def name
11
+ `#@native.name`
12
+ end
13
+
14
+ def value
15
+ `#@native.value`
16
+ end
17
+ end
18
+
19
+ end; end
@@ -0,0 +1,97 @@
1
+ # Temporary fix for a bug in String#scan
2
+ module Paggio::Utils
3
+ def self.heredoc(string)
4
+ string
5
+ end
6
+ end
7
+
8
+ class Paggio::HTML::Element < BasicObject
9
+ def on(*args, &block)
10
+ (@on ||= []) << [args, block]
11
+ end
12
+
13
+ def style(*args, &block)
14
+ @style = [args, block]
15
+ end
16
+ end
17
+
18
+ module Browser; module DOM
19
+
20
+ class Builder
21
+ def self.to_h
22
+ @builders ||= {}
23
+ end
24
+
25
+ def self.for(klass, &block)
26
+ if block
27
+ to_h[klass] = block
28
+ else
29
+ to_h[klass]
30
+ end
31
+ end
32
+
33
+ def self.build(builder, item)
34
+ to_h.each {|klass, block|
35
+ if klass === item
36
+ break block.call(builder, item)
37
+ end
38
+ }
39
+ end
40
+
41
+ attr_reader :document, :element
42
+
43
+ def initialize(document, element = nil, &block)
44
+ @document = document
45
+ @element = element
46
+ @builder = Paggio::HTML.new(&block)
47
+ @roots = @builder.each.map { |e| Builder.build(self, e) }
48
+
49
+ if @element
50
+ @roots.each {|root|
51
+ @element << root
52
+ }
53
+ end
54
+ end
55
+
56
+ def to_a
57
+ @roots
58
+ end
59
+ end
60
+
61
+ Builder.for String do |b, item|
62
+ b.document.create_text(item)
63
+ end
64
+
65
+ Builder.for Paggio::HTML::Element do |b, item|
66
+ dom = b.document.create_element(`item.name`)
67
+
68
+ if Hash === `item.attributes`
69
+ dom.attributes.merge!(`item.attributes`)
70
+ end
71
+
72
+ `item.class_names`.each {|value|
73
+ dom.add_class value
74
+ }
75
+
76
+ if on = `item.on || nil`
77
+ on.each {|args, block|
78
+ dom.on(*args, &block)
79
+ }
80
+ end
81
+
82
+ if style = `item.style || nil`
83
+ dom.style(*style[0], &style[1])
84
+ end
85
+
86
+ if inner = `item.inner_html || nil`
87
+ dom.inner_html = inner
88
+ else
89
+ item.each {|child|
90
+ dom << Builder.build(b, child)
91
+ }
92
+ end
93
+
94
+ dom
95
+ end
96
+
97
+ end; end
@@ -0,0 +1,9 @@
1
+ module Browser; module DOM
2
+
3
+ class CDATA < Text
4
+ def inspect
5
+ "#<DOM::CDATA: #{value}>"
6
+ end
7
+ end
8
+
9
+ end; end
@@ -0,0 +1,37 @@
1
+ module Browser; module DOM
2
+
3
+ class CharacterData < Node
4
+ def data
5
+ `#@native.data`
6
+ end
7
+
8
+ def append(string)
9
+ `#@native.appendData(string)`
10
+
11
+ self
12
+ end
13
+
14
+ def insert(string, offset = 0)
15
+ `#@native.insertData(offset, string)`
16
+
17
+ self
18
+ end
19
+
20
+ def delete(count, offset = 0)
21
+ `#@native.deleteData(offset, count)`
22
+
23
+ self
24
+ end
25
+
26
+ def replace(string, offset = 0, count = `#@native.length`)
27
+ `#@native.replaceData(offset, count, string)`
28
+
29
+ self
30
+ end
31
+
32
+ def substring(count, offset = 0)
33
+ `#@native.substringData(offset, count)`
34
+ end
35
+ end
36
+
37
+ end; end
@@ -0,0 +1,9 @@
1
+ module Browser; module DOM
2
+
3
+ class Comment < CharacterData
4
+ def inspect
5
+ "#<DOM::Comment: #{value}>"
6
+ end
7
+ end
8
+
9
+ end; end
@@ -0,0 +1,8 @@
1
+ require 'browser/compatibility/dom/document/window'
2
+
3
+ require 'browser/compatibility/dom/mutation_observer'
4
+
5
+ require 'browser/compatibility/dom/element/matches'
6
+ require 'browser/compatibility/dom/element/css'
7
+ require 'browser/compatibility/dom/element/offset'
8
+ require 'browser/compatibility/dom/element/style'
@@ -0,0 +1,83 @@
1
+ require 'browser/location'
2
+
3
+ module Browser; module DOM
4
+
5
+ class Document < Element
6
+ def create_element(name, options = {})
7
+ if ns = options[:namespace]
8
+ DOM(`#@native.createElementNS(#{ns}, #{name})`)
9
+ else
10
+ DOM(`#@native.createElement(name)`)
11
+ end
12
+ end
13
+
14
+ def window
15
+ Window.new(`#@native.defaultView`)
16
+ end
17
+
18
+ def create_text(content)
19
+ DOM(`#@native.createTextNode(#{content})`)
20
+ end
21
+
22
+ def [](what)
23
+ %x{
24
+ var result = #@native.getElementById(what);
25
+
26
+ if (result) {
27
+ return #{DOM(`result`)};
28
+ }
29
+ }
30
+
31
+ xpath(what).first || css(what).first
32
+ end
33
+
34
+ alias at []
35
+
36
+ def cookies
37
+ Cookies.new(@native) if defined?(`#@native.cookie`)
38
+ end
39
+
40
+ def document
41
+ self
42
+ end
43
+
44
+ def inspect
45
+ "#<DOM::Document: #{children.inspect}>"
46
+ end
47
+
48
+ def location
49
+ Location.new(`#@native.location`) if `#@native.location`
50
+ end
51
+
52
+ def title
53
+ `#@native.title`
54
+ end
55
+
56
+ def title=(value)
57
+ `#@native.title = value`
58
+ end
59
+
60
+ def root
61
+ DOM(`#@native.documentElement`)
62
+ end
63
+
64
+ def head
65
+ DOM(`#@native.getElementsByTagName("head")[0]`)
66
+ end
67
+
68
+ def body
69
+ DOM(`#@native.body`)
70
+ end
71
+
72
+ def style_sheets
73
+ Native::Array.new(`#@native.styleSheets`) {|e|
74
+ CSS::StyleSheet.new(e)
75
+ }
76
+ end
77
+
78
+ def root=(element)
79
+ `#@native.documentElement = #{Native.convert(element)}`
80
+ end
81
+ end
82
+
83
+ end; end
@@ -0,0 +1,7 @@
1
+ module Browser; module DOM
2
+
3
+ class DocumentFragment < Element
4
+
5
+ end
6
+
7
+ end; end
@@ -0,0 +1,290 @@
1
+ require 'browser/dom/element/position'
2
+ require 'browser/dom/element/offset'
3
+ require 'browser/dom/element/scroll'
4
+
5
+ require 'browser/dom/element/input'
6
+
7
+ module Browser; module DOM
8
+
9
+ class Element < Node
10
+ def self.create(*args)
11
+ $document.create_element(*args)
12
+ end
13
+
14
+ def self.new(node)
15
+ if self == Element
16
+ case `node.nodeName`.downcase
17
+ when :input
18
+ Input.new(node)
19
+
20
+ else super
21
+ end
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ include Event::Target
28
+
29
+ target {|value|
30
+ DOM(value) rescue nil
31
+ }
32
+
33
+ alias_native :id
34
+
35
+ def add_class(*names)
36
+ `#@native.className = #{(class_names + names).uniq.join ' '}`
37
+
38
+ self
39
+ end
40
+
41
+ def remove_class(*names)
42
+ `#@native.className = #{(class_names - names).join ' '}`
43
+
44
+ self
45
+ end
46
+
47
+ alias_native :class_name, :className
48
+
49
+ def class_names
50
+ `#@native.className`.split(/\s+/).reject(&:empty?)
51
+ end
52
+
53
+ alias attribute attr
54
+
55
+ def attribute_nodes
56
+ Native::Array.new(`#@native.attributes`, get: :item) { |e| DOM(e) }
57
+ end
58
+
59
+ def attributes(options = {})
60
+ Attributes.new(self, options)
61
+ end
62
+
63
+ def get(name, options = {})
64
+ if namespace = options[:namespace]
65
+ `#@native.getAttributeNS(#{namespace.to_s}, #{name.to_s}) || nil`
66
+ else
67
+ `#@native.getAttribute(#{name.to_s}) || nil`
68
+ end
69
+ end
70
+
71
+ def set(name, value, options = {})
72
+ if namespace = options[:namespace]
73
+ `#@native.setAttributeNS(#{namespace.to_s}, #{name.to_s}, #{value})`
74
+ else
75
+ `#@native.setAttribute(#{name.to_s}, #{value.to_s})`
76
+ end
77
+ end
78
+
79
+ alias [] get
80
+ alias []= set
81
+
82
+ alias attr get
83
+ alias attribute get
84
+
85
+ alias get_attribute get
86
+ alias set_attribute set
87
+
88
+ def key?(name)
89
+ !!self[name]
90
+ end
91
+
92
+ def keys
93
+ attributes_nodesmap(&:name)
94
+ end
95
+
96
+ def values
97
+ attribute_nodes.map(&:value)
98
+ end
99
+
100
+ def remove_attribute(name)
101
+ `#@native.removeAttribute(name)`
102
+ end
103
+
104
+ def size(*inc)
105
+ Size.new(`#@native.offsetWidth`, `#@native.offsetHeight`)
106
+ end
107
+
108
+ def position
109
+ Position.new(self)
110
+ end
111
+
112
+ def offset(*values)
113
+ off = Offset.new(self)
114
+
115
+ unless values.empty?
116
+ off.set(*values)
117
+ end
118
+
119
+ off
120
+ end
121
+
122
+ def offset=(value)
123
+ offset.set(*value)
124
+ end
125
+
126
+ def scroll
127
+ Scroll.new(self)
128
+ end
129
+
130
+ def inner_dom(&block)
131
+ # FIXME: when block passing is fixed
132
+ doc = document
133
+ clear; Builder.new(doc, self, &block)
134
+
135
+ self
136
+ end
137
+
138
+ def inner_dom=(node)
139
+ clear; self << node
140
+ end
141
+
142
+ def /(*paths)
143
+ paths.map { |path| xpath(path) }.flatten.uniq
144
+ end
145
+
146
+ def at(path)
147
+ xpath(path).first || css(path).first
148
+ end
149
+
150
+ def at_css(*rules)
151
+ rules.each {|rule|
152
+ found = css(rule).first
153
+
154
+ return found if found
155
+ }
156
+
157
+ nil
158
+ end
159
+
160
+ def at_xpath(*paths)
161
+ paths.each {|path|
162
+ found = xpath(path).first
163
+
164
+ return found if found
165
+ }
166
+
167
+ nil
168
+ end
169
+
170
+ def search(*selectors)
171
+ NodeSet.new document, selectors.map {|selector|
172
+ xpath(selector).to_a.concat(css(selector).to_a)
173
+ }.flatten.uniq
174
+ end
175
+
176
+ def css(path)
177
+ NodeSet.new(document, Native::Array.new(`#@native.querySelectorAll(path)`))
178
+ end
179
+
180
+ def xpath(path)
181
+ result = []
182
+
183
+ begin
184
+ %x{
185
+ var tmp = (#@native.ownerDocument || #@native).evaluate(
186
+ path, #@native, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
187
+
188
+ result = #{Native::Array.new(`tmp`, get: :snapshotItem, length: :snapshotLength)};
189
+ }
190
+ rescue; end
191
+
192
+ NodeSet.new(document, result)
193
+ end
194
+
195
+ def style(data = nil, &block)
196
+ style = CSS::Declaration.new(`#@native.style`)
197
+
198
+ return style unless data || block
199
+
200
+ if data.is_a?(String)
201
+ style.replace(data)
202
+ elsif data.is_a?(Enumerable)
203
+ style.assign(data)
204
+ end
205
+
206
+ if block
207
+ style.apply(&block)
208
+ end
209
+
210
+ self
211
+ end
212
+
213
+ def style!
214
+ CSS::Declaration.new(`#{window.to_n}.getComputedStyle(#@native, null)`)
215
+ end
216
+
217
+ def data(what)
218
+ unless defined?(`#@native.$data`)
219
+ `#@native.$data = {}`
220
+ end
221
+
222
+ if Hash === what
223
+ what.each {|name, value|
224
+ `#@native.$data[name] = value`
225
+ }
226
+ else
227
+ %x{
228
+ var value = #@native.$data[what];
229
+
230
+ if (value === undefined) {
231
+ return nil;
232
+ }
233
+ else {
234
+ return value;
235
+ }
236
+ }
237
+ end
238
+ end
239
+
240
+ def matches?(selector)
241
+ `#@native.matches(#{selector})`
242
+ end
243
+
244
+ def window
245
+ document.window
246
+ end
247
+
248
+ def inspect
249
+ "#<DOM::Element: #{name}>"
250
+ end
251
+
252
+ class Attributes
253
+ include Enumerable
254
+
255
+ attr_reader :namespace
256
+
257
+ def initialize(element, options)
258
+ @element = element
259
+ @namespace = options[:namespace]
260
+ end
261
+
262
+ def each(&block)
263
+ return enum_for :each unless block_given?
264
+
265
+ @element.attribute_nodes.each {|attr|
266
+ yield attr.name, attr.value
267
+ }
268
+
269
+ self
270
+ end
271
+
272
+ def [](name)
273
+ @element.get_attribute(name, namespace: @namespace)
274
+ end
275
+
276
+ def []=(name, value)
277
+ @element.set_attribute(name, value, namespace: @namespace)
278
+ end
279
+
280
+ def merge!(hash)
281
+ hash.each {|name, value|
282
+ self[name] = value
283
+ }
284
+
285
+ self
286
+ end
287
+ end
288
+ end
289
+
290
+ end; end