red 4.1.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,216 @@
1
+ require 'selectors'
2
+
3
+ # The +Document+ object enables access to top-level HTML elements like
4
+ # <i><head></i>, <i><html></i>, and <i><body></i>.
5
+ #
6
+ # +Document+ also provides "DOM-ready" functionality with
7
+ # <tt>Document.ready?</tt> and element-finding functionality through the
8
+ # powerful <tt>Document.[]</tt> method.
9
+ #
10
+ module Document
11
+
12
+ `document.head=document.getElementsByTagName('head')[0]`
13
+ `document.html=document.getElementsByTagName('html')[0]`
14
+ `document.window=(document.defaultView||document.parentWindow)`
15
+ `c$Document.__native__= document`
16
+
17
+ # call-seq:
18
+ # Document["#id"] -> element or nil
19
+ # Document["selector"] -> [element, ...]
20
+ # Document[str, ...] -> [element, ...]
21
+ #
22
+ # The first form returns the +Element+ identified by the id <i>str</i> (e.g.
23
+ # <i>'#my_id'</i>), or +nil+ if no such element is found. The second form
24
+ # returns the array of elements identified by the selector <i>str</i>.
25
+ # The third form returns the array of elements found by calling
26
+ # <tt>Document.[]</tt> on each arg. The fourth form returns _element_
27
+ # unchanged.
28
+ #
29
+ # The first form returns the +Element+ identified by the string
30
+ # <i>"#id"</i>, or +nil+ if no such element is found.
31
+ #
32
+ # Document['#content'] #=> #<Element: DIV id="content">
33
+ #
34
+ # The second form returns the array of +Element+ objects that match the
35
+ # string <i>"selector"</i>, which takes the format of a CSS3 selector. See
36
+ # http://www.w3.org/TR/css3-selectors/ for details.
37
+ #
38
+ #
39
+ #
40
+ # Available Selectors
41
+ # Document[str] in its second form takes a selector string and scans the document
42
+ # returing an array of any elements that match the selector string.
43
+ #
44
+ # The selectors string takes the format of a CSS3 selector: http://www.w3.org/TR/css3-selectors/
45
+ #
46
+ # Class Selector
47
+ # Returns an array of elements that have the specified class name
48
+ # Document['.my_class'] #=> [#<Element: DIV class="my_class">, #<Element: DIV class="my_class">, #<Element: SPAN class="my_class">]
49
+ #
50
+ # Tag Selector
51
+ # Returns an array of elements that have the specified tag
52
+ # Document['a'] #=> [#<Element: A href="/foo/bar">, #<Element: A href="/foo/baz">, #<Element: A href="/foo/bat">]
53
+ #
54
+ def self.[](*args)
55
+ if args.length == 1
56
+ return self.find_by_string(args[0])
57
+ else
58
+ args.map do |str|
59
+ Document[str]
60
+ end.compact
61
+ #return self.find_many_with_array(args.unshift(obj))
62
+ end
63
+ end
64
+
65
+ # call-seq:
66
+ # Document.body -> element
67
+ #
68
+ # Returns the <i><body></i> element of the current page.
69
+ #
70
+ # Document.body #=> #<Element: BODY>
71
+ #
72
+ def self.body
73
+ `$E(document.body)`
74
+ end
75
+
76
+ # call-seq:
77
+ # Document.execute_js(str) -> str
78
+ #
79
+ # Executes _str_ as JavaScript, then returns _str_.
80
+ #
81
+ def self.execute_js(str)
82
+ return str if str == ''
83
+ if `window.execScript`
84
+ `window.execScript(str.__value__)`
85
+ else
86
+ `scriptElement = document.createElement('script')`
87
+ `scriptElement.setAttribute('type','text/javascript')`
88
+ `scriptElement.text = str`
89
+ `document.head.appendChild(scriptElement)`
90
+ `document.head.removeChild(scriptElement)`
91
+ end
92
+ return str
93
+ end
94
+
95
+ # call-seq:
96
+ # Document.head -> element
97
+ #
98
+ # Returns the <i><head></i> element of the current page.
99
+ #
100
+ # Document.head #=> #<Element: HEAD>
101
+ #
102
+ def self.head
103
+ `$E(document.head)`
104
+ end
105
+
106
+ # call-seq:
107
+ # Document.html -> element
108
+ #
109
+ # Returns the <i><html></i> element of the current page.
110
+ #
111
+ # Document.html #=> #<Element: HTML>
112
+ #
113
+ def self.html
114
+ `$E(document.html)`
115
+ end
116
+
117
+ # call-seq:
118
+ # Document.ready? { block } -> nil
119
+ #
120
+ # When the DOM is finished loading, executes the given _block_, then returns
121
+ # +nil+.
122
+ #
123
+ # Document.ready? do
124
+ # puts self.title
125
+ # end
126
+ #
127
+ # produces:
128
+ #
129
+ # "RDoc Documentation"
130
+ #
131
+ def self.ready?(&block)
132
+ @proc = block
133
+ `document.addEventListener('DOMContentLoaded', function(){document.__loaded__=true;#{@proc.call};}.m$(this), false)`
134
+ return nil
135
+ end
136
+
137
+ # call-seq:
138
+ # Document.title -> string
139
+ #
140
+ # Returns the title of the current page.
141
+ #
142
+ # Document.title #=> "RDoc Documentation"
143
+ #
144
+ def self.title
145
+ `$q(document.title)`
146
+ end
147
+
148
+ # Uses the Selector library to find native elements and retrun an array of extended elements
149
+ def self.find_all_by_selector(selector) # :nodoc:
150
+ `Array.fromCollection(Selectors.Utils.search(document, selector.__value__, {}));`
151
+ end
152
+
153
+ # Uses the browser's native getElementById to find an element. Returns an extended element.
154
+ def self.find_by_id(str) # :nodoc:
155
+ `$E(document.getElementById(str.__value__))`
156
+ end
157
+
158
+ # Finds an element from a provided string. If the string is a represents a single id ('#my_id') calls
159
+ # self.find_all_by_id, otherwise calls self.find_all_by_selector
160
+ def self.find_by_string(str) # :nodoc:
161
+ `str.__value__.match(/^#[a-zA-z_]*$/)` ? self.find_by_id(`$q(str.__value__.replace('#',''))`) : self.find_all_by_selector(str)
162
+ end
163
+
164
+ def self.find_many_with_array(ary) # :nodoc:
165
+ `for(var i=0,l=ary.length,result=[];i<l;++i){var el=#{Document[`ary[i]`]};if($T(el)){result.push(el);};}`
166
+ return `result`.flatten
167
+ end
168
+
169
+ # Walks the DOM from a particular element. The first step it talks in the walk with either
170
+ # be the specified start_relation or the specified path. If 'all' is set to false, the walk
171
+ # will termiante and return the single element. Otherwise, it will continue the walk until it
172
+ # has reached the end of the specified path.
173
+ # The walk will then termiante and return an array of all elements on the path.
174
+ #
175
+ # if an optional <tt>match_selector<tt> is provided, only element(s) that match
176
+ # the selector can be returned
177
+ #
178
+ # Examples:
179
+ # Document.walk(my_element, 'parentNode', nil, nil, false)
180
+ # will go down the parentNode path of my_element, starting at the my_element, and
181
+ # return just a single element
182
+ #
183
+ # Document.walk(my_element, 'parentNode', nil, nil, true)
184
+ # will go down the parentNode path of my_element, starting at my_element, and
185
+ # return an array of all elements on the parentNode path
186
+ #
187
+ # Document.walk(my_element, 'nextSibling', 'firstChild', nil, true)
188
+ # will go down the nextSibling path of my_element, starting at my_element's firstChild
189
+ # and return an array of all elements on the nextSibling path from that starting point.
190
+ #
191
+ # Document.walk(my_element, 'nextSibling', 'firstChild', '.my_class', true)
192
+ # will go down the nextSibling path of my_element, starting at my_element's firstChild
193
+ # and return an array of all elements on the nextSibling path from that starting point that
194
+ # have the class 'my_class'
195
+ #
196
+ def self.walk(element, path, startRelation, matchSelector, all) # :nodoc
197
+ `if(startRelation){startRelation = startRelation.__value__;};
198
+ var el = element.__native__[startRelation || path.__value__],elements = [];
199
+ while (el){
200
+ if (el.nodeType == 1 && (#{!matchSelector} || Element.match(el, #{matchSelector}))){
201
+ if (!all) {return $E(el);}
202
+ elements.push($E(el));
203
+ }
204
+ el = el[path.__value__];
205
+ }`
206
+ return `(all) ? elements : nil`
207
+ end
208
+
209
+ def self.window # :nodoc:
210
+ Window
211
+ end
212
+
213
+ def self.document # :nodoc
214
+ self
215
+ end
216
+ end
@@ -0,0 +1,417 @@
1
+ require 'document'
2
+ require 'user_events'
3
+ require 'code_events'
4
+ require 'accessors'
5
+
6
+ # +Element+ objects represent DOM elements from the browser.
7
+ #
8
+ class Element
9
+ `window.$E=function(element){if(element==null){return nil;};var E=c$Element.m$new(null);E.__native__=element;return E;}`
10
+
11
+ include UserEvents
12
+ include CodeEvents
13
+
14
+ def self.destroy(elem) # :nodoc:
15
+ `var el = elem.__native__ || elem`
16
+ `c$Element.m$empty(el)`
17
+ `c$Element.m$remove(el)`
18
+ return true
19
+ end
20
+
21
+ def self.empty(elem) # :nodoc:
22
+ `for (var c=(elem.__native__||elem).childNodes,i=c.length;i>0;){c$Element.m$destroy(c[--i]);};`
23
+ return true
24
+ end
25
+
26
+ def self.remove(elem) # :nodoc:
27
+ `var el = elem.__native__ || elem`
28
+ `(el.parentNode) ? el.parentNode.removeChild(el) : this`
29
+ end
30
+
31
+ # call-seq:
32
+ # Element.new(sym, attributes = {}) -> element
33
+ #
34
+ # Returns a new +Element+ with the tag _sym_ and the given attributes.
35
+ #
36
+ # Element.new(:div, :id => 'my_div', :class => 'inserted') #=> #<Element: DIV id="my_div" class="inserted">
37
+ #
38
+ def initialize(tag, attributes = {})
39
+ `if(!tag){return nil;}`
40
+ `this.__native__ = document.createElement(tag.__value__)`
41
+ self.set_properties(attributes)
42
+ end
43
+
44
+ def ==(elem) # :nodoc:
45
+ `this.__native__ === elem.__native__`
46
+ end
47
+
48
+ def ===(elem) # :nodoc:
49
+ `this.__native__ === elem.__native__`
50
+ end
51
+
52
+ # call-seq:
53
+ # elem[str] -> array
54
+ #
55
+ # *args?
56
+ #
57
+ def [](expression, *args)
58
+ `if (expression.__value__.match(/^#[a-zA-z_]*$/) && #{args.empty?}){return #{self}.__native__.getElementById(expression.__value__.replace('#',''));}`
59
+ expression = expression.split(',')
60
+ items = []
61
+ `for (var i = 0, l = expression.length, local = {}; i < l; i++){
62
+ var selector = expression[i].__value__, elements = Selectors.Utils.search(#{self}.__native__, selector, local);
63
+ elements = Array.fromCollection(elements);
64
+ items = (i == 0) ? elements : items.concat(elements);
65
+ }`
66
+ return items
67
+ end
68
+
69
+ # call-seq:
70
+ # elem.children -> array
71
+ #
72
+ # Returns the array of _elem_'s child elements on the DOM tree.
73
+ #
74
+ # <div id="container">
75
+ # <div id="a_element"></div>
76
+ # <div id="a_inner_element"></div>
77
+ # </div>
78
+ # <div id="b_element"></div>
79
+ # </div>
80
+ #
81
+ # Document['#container'].children #=> [#<Element: DIV id="a_element">, #<Element: DIV id="b_element">]
82
+ #
83
+ def children(match_selector = nil)
84
+ Document.walk(self, 'nextSibling', 'firstChild', match_selector, true)
85
+ end
86
+
87
+ # TODO: re: the native object and event listeners; does it really do this stuff?
88
+ # Removes the element from the page and the DOM, destroys the element object, the native object,
89
+ # any attached event listeners, and frees their memory location. Returns +true+ if successful, +false+ otherwise.
90
+
91
+ # call-seq:
92
+ # elem.destroy! -> true
93
+ #
94
+ # Removes _elem_ and all its children from the page and from the DOM, then
95
+ # returns +true+.
96
+ #
97
+ def destroy!
98
+ Element.destroy(self)
99
+ return true
100
+ end
101
+
102
+ def document # :nodoc:
103
+ `this.__native__.ownerDocument`
104
+ end
105
+
106
+ # call-seq:
107
+ # elem.empty! -> true or false
108
+ #
109
+ # Removes _elem_'s inner text and child elements from the page.
110
+ #
111
+ def empty!
112
+ Element.empty(self)
113
+ return self
114
+ end
115
+
116
+ def eql?(elem) # :nodoc:
117
+ `this.__native__ === elem.__native__`
118
+ end
119
+
120
+ # call-seq:
121
+ # elem.first_child -> element
122
+ #
123
+ # Returns the first child element of _elem_ on the DOM tree, or +nil+ if no
124
+ # such element is found.
125
+ #
126
+ # <div id="container">
127
+ # <div id="a_element"></div>
128
+ # <div id="b_element"></div>
129
+ # <div id="c_element"></div>
130
+ # <div id="d_element"></div>
131
+ # </div>
132
+ #
133
+ # Document['#container'].first_child #=> #<Element: DIV id="a_element">
134
+ #
135
+ def first_child(match_selector = nil)
136
+ Document.walk(self, 'nextSibling', 'firstChild', match_selector, false)
137
+ end
138
+
139
+ # call-seq:
140
+ # elem.insert(other, sym = :bottom) -> elem
141
+ #
142
+ # Returns _elem_ with _other_ inserted at location _sym_.
143
+ #
144
+ # Inserting into <tt>:bottom</tt> (default location):
145
+ #
146
+ # <div id='a_element'>
147
+ # <div id='b_element'></div>
148
+ # <div id='c_element'></div>
149
+ # </div>
150
+ # <div id='d_element'></div>
151
+ #
152
+ # elem = Document['#a_element']
153
+ # other = Document['#d_element']
154
+ # elem.insert(other)
155
+ #
156
+ # produces:
157
+ #
158
+ # <div id='a_element'>
159
+ # <div id='b_element'></div>
160
+ # <div id='c_element'></div>
161
+ # <div id='d_element'></div>
162
+ # </div>
163
+ #
164
+ # Inserting into <tt>:top</tt>:
165
+ #
166
+ # <div id='a_element'>
167
+ # <div id='b_element'></div>
168
+ # <div id='c_element'></div>
169
+ # </div>
170
+ # <div id='d_element'></div>
171
+ #
172
+ # elem = Document['#a_element']
173
+ # other = Document['#d_element']
174
+ # elem.insert(other, :top)
175
+ #
176
+ # produces:
177
+ #
178
+ # <div id='a_element'>
179
+ # <div id='d_element'></div>
180
+ # <div id='b_element'></div>
181
+ # <div id='c_element'></div>
182
+ # </div>
183
+ #
184
+ # Inserting <tt>:before</tt>:
185
+ #
186
+ # <div id='a_element'>
187
+ # <div id='b_element'></div>
188
+ # <div id='c_element'></div>
189
+ # </div>
190
+ # <div id='d_element'></div>
191
+ #
192
+ # elem = Document['#a_element']
193
+ # other = Document['#d_element']
194
+ # elem.insert(other, :before)
195
+ #
196
+ # produces:
197
+ #
198
+ # <div id='d_element'></div>
199
+ # <div id='a_element'>
200
+ # <div id='b_element'></div>
201
+ # <div id='c_element'></div>
202
+ # </div>
203
+ #
204
+ def insert(element, where = :bottom)
205
+ self.send("insert_#{where.to_s}", element)
206
+ return self
207
+ end
208
+
209
+ def insert_after(element) # :nodoc
210
+ `if (!element.parentNode) return`
211
+ `next = #{self}.__native__.nextSibling`
212
+ `(next) ? #{self}.__native__.parentNode.insertBefore(#{element}.__native__, next) : #{self}__native__.parentNode.appendChild(#{element}.__native__)`
213
+ return true
214
+ end
215
+
216
+ def insert_before(element) # :nodoc:
217
+ `if (#{self}.__native__.parentNode) #{self}.__native__.parentNode.insertBefore(#{element}.__native__, #{self}.__native__)`
218
+ return true
219
+ end
220
+
221
+ def insert_bottom(element) # :nodoc
222
+ `#{self}.__native__.appendChild(#{element}.__native__)`
223
+ return true
224
+ end
225
+ alias :insert_inside :insert_bottom
226
+
227
+ def insert_top(element) # :nodoc:
228
+ `first = #{self}.__native__.firstChild`
229
+ `(first) ? #{self}.__native__.insertBefore(#{element}.__native__, first) : #{self}.__native__.appendChild(#{element}.__native__)`
230
+ return true
231
+ end
232
+
233
+ # call-seq:
234
+ # elem.inspect -> string
235
+ #
236
+ # Returns a string representation of _elem_ including its tag name, classes,
237
+ # and id.
238
+ #
239
+ # <div style="width:300px" id="a_element" class="draggable container">
240
+ #
241
+ # Document['#a_element'].inspect #=> "#<Element: DIV id=\"a_element\" class=\"draggable container\">"
242
+ #
243
+ def inspect
244
+ attributes = [`$q(this.__native__.tagName.toUpperCase())`]
245
+ attributes << `$q('id="'+this.__native__.id+'"')` if `this.__native__.id!==''`
246
+ attributes << `$q('class="'+this.__native__.className+'"')` if `this.__native__.className!==''`
247
+ "#<Element: %s>" % attributes.join(' ')
248
+ end
249
+
250
+ # call-seq:
251
+ # elem.is_body? -> true or false
252
+ #
253
+ # Returns +true+ if the element is the body element, +false+ otherwise.
254
+ #
255
+ # Document['#my_div'].is_body? #=> false
256
+ # Document.body.is_body? #=> true
257
+ #
258
+ def is_body?
259
+ `(/^(?:body|html)$/i).test(#{self}.__native__.tagName)`
260
+ end
261
+
262
+ # call-seq:
263
+ # elem.last_child -> element or nil
264
+ #
265
+ # Returns the last child element of _elem_ on the DOM tree, or +nil+ if no
266
+ # such element is found.
267
+ #
268
+ # <div id="container">
269
+ # <div id="a_element"></div>
270
+ # <div id="b_element"></div>
271
+ # <div id="c_element"></div>
272
+ # <div id="d_element"></div>
273
+ # </div>
274
+ #
275
+ # Document['#container'].last_child #=> #<Element: DIV id="d_element">
276
+ #
277
+ def last_child(match_selector = nil)
278
+ Document.walk(self, 'previousSibling', 'lastChild', match_selector, false)
279
+ end
280
+
281
+ # call-seq:
282
+ # elem.next_element -> element or nil
283
+ #
284
+ # Returns the sibling element immediately following _elem_ on the DOM tree,
285
+ # or +nil+ if no such element is found.
286
+ #
287
+ # <div id='container'>
288
+ # <div id='a_element'></div>
289
+ # <div id='b_element'></div>
290
+ # <div id='c_element'></div>
291
+ # <div id='d_element'></div>
292
+ # </div>
293
+ #
294
+ # Document['#b_element'].next_element #=> #<Element: DIV id="c_element">
295
+ #
296
+ def next_element(match_selector = nil)
297
+ Document.walk(self, 'nextSibling', nil, match_selector, false)
298
+ end
299
+
300
+ # call-seq:
301
+ # elem.next_elements -> array
302
+ #
303
+ # Returns the array of sibling elements that follow _elem_ on the DOM tree.
304
+ #
305
+ # <div id='container'>
306
+ # <div id='a_element'></div>
307
+ # <div id='b_element'></div>
308
+ # <div id='c_element'></div>
309
+ # <div id='d_element'></div>
310
+ # </div>
311
+ #
312
+ # elem = Document['#b_element'] #=> #<Element: DIV id="b_element">
313
+ # elem.previous_elements #=> [#<Element: DIV id="c_element">, #<Element: DIV id="d_element">]
314
+ #
315
+ def next_elements(match_selector = nil)
316
+ Document.walk(self, 'nextSibling', nil, match_selector, true)
317
+ end
318
+
319
+ # call-seq:
320
+ # elem.parent -> element or nil
321
+ #
322
+ # Returns the parent element of _elem_ on the DOM tree, or +nil+ if no such
323
+ # element is found.
324
+ #
325
+ # <div id="container">
326
+ # <div id="a_element"></div>
327
+ # <div id="b_element"></div>
328
+ # <div id="c_element"></div>
329
+ # <div id="d_element"></div>
330
+ # </div>
331
+ #
332
+ # Document['#c_element'].parent #=> #<Element: DIV id="container">
333
+ # Document.body.parent #=> #<Element: HTML>
334
+ # Document.body.parent.parent #=> nil
335
+ #
336
+ def parent(match_selector = nil)
337
+ Document.walk(self, 'parentNode', nil, match_selector, false)
338
+ end
339
+
340
+ # call-seq:
341
+ # elem.parents -> array
342
+ #
343
+ # Returns the array of _elem_'s ancestors on the DOM tree.
344
+ #
345
+ # <div id='container'>
346
+ # <div id='a_element'></div>
347
+ # <div id='b_element'>
348
+ # <div id='b_inner_element'></div>
349
+ # </div>
350
+ # <div id='c_element'></div>
351
+ # <div id='d_element'></div>
352
+ # </div>
353
+ #
354
+ # Document['#b_inner_element'].parents #=> [#<Element: DIV id="b_element">, #<Element: DIV id="container">, <Element: BODY>, <Element: HTML>]
355
+ # Document.html.parents #=> []
356
+ #
357
+ def parents(match_selector = nil)
358
+ Document.walk(self, 'parentNode', nil, match_selector, true)
359
+ end
360
+
361
+ # call-seq:
362
+ # elem.previous_element -> element or nil
363
+ #
364
+ # Returns the sibling element immediately preceding _elem_ on the DOM tree,
365
+ # or +nil+ if no such element is found.
366
+ #
367
+ # <div id='container'>
368
+ # <div id='a_element'></div>
369
+ # <div id='b_element'></div>
370
+ # <div id='c_element'></div>
371
+ # <div id='d_element'></div>
372
+ # </div>
373
+ #
374
+ # Document['#b_element'].previous_element #=> #<Element: DIV id="a_element">
375
+ #
376
+ def previous_element(match_selector = nil)
377
+ Document.walk(self, 'previousSibling', nil, match_selector, false)
378
+ end
379
+
380
+ # call-seq:
381
+ # elem.previous_elements -> array
382
+ #
383
+ # Returns the array of sibling elements that precede _elem_ on the DOM tree.
384
+ #
385
+ # <div id='container'>
386
+ # <div id='a_element'></div>
387
+ # <div id='b_element'></div>
388
+ # <div id='c_element'></div>
389
+ # <div id='d_element'></div>
390
+ # </div>
391
+ #
392
+ # elem = Document['#c_element'] #=> #<Element: DIV id="c_element">
393
+ # elem.previous_elements #=> [#<Element: DIV id="a_element">, #<Element: DIV id="b_element">]
394
+ #
395
+ def previous_elements(match_selector = nil)
396
+ Document.walk(self, 'previousSibling', nil, match_selector, true)
397
+ end
398
+
399
+ # call-seq:
400
+ # elem.remove! -> elem
401
+ #
402
+ # Removes _elem_ and all of its child elements from the page, then returns
403
+ # _elem_.
404
+ #
405
+ def remove!
406
+ Element.remove(self)
407
+ return self
408
+ end
409
+
410
+ # call-seq:
411
+ # elem.to_s -> string
412
+ #
413
+ # FIX: Incomplete
414
+ #
415
+ def to_s
416
+ end
417
+ end