vapir-ie 1.7.0.rc1

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.
@@ -0,0 +1,9 @@
1
+ require 'vapir-ie/element'
2
+
3
+ require 'vapir-ie/frame'
4
+ require 'vapir-ie/form'
5
+ require 'vapir-ie/non_control_elements'
6
+ require 'vapir-ie/input_elements'
7
+ require 'vapir-ie/table'
8
+ require 'vapir-ie/image'
9
+ require 'vapir-ie/link'
@@ -0,0 +1,8 @@
1
+ require 'vapir-ie/element'
2
+ require 'vapir-common/elements/elements'
3
+
4
+ module Vapir
5
+ class IE::Form < IE::Element
6
+ include Vapir::Form
7
+ end # class IE::Form
8
+ end
@@ -0,0 +1,24 @@
1
+ require 'vapir-ie/element'
2
+ require 'vapir-common/elements/elements'
3
+ require 'vapir-ie/page_container'
4
+
5
+ module Vapir
6
+ class IE::Frame < IE::Element
7
+ include Frame
8
+ include IE::PageContainer
9
+
10
+ def content_window_object
11
+ element_object.contentWindow
12
+ end
13
+
14
+ def document_object
15
+ content_window_object.document
16
+ end
17
+ alias document document_object
18
+
19
+ def attach_command
20
+ @container.page_container.attach_command + ".frame(#{@how.inspect}, #{@what.inspect})"
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,880 @@
1
+ require 'vapir-ie/container'
2
+ require 'vapir-ie/page_container'
3
+ require 'vapir-ie/close_all'
4
+ require 'vapir-ie/modal_dialog'
5
+ require 'vapir-ie/win32ole'
6
+ require 'vapir-ie/ie-process'
7
+ require 'vapir-ie/logger'
8
+
9
+ module Vapir
10
+ class IE < Browser
11
+ include Vapir::Exception
12
+ include IE::PageContainer
13
+
14
+ # Maximum number of seconds to wait when attaching to a window
15
+ @@attach_timeout = 2.0 # default value
16
+ def self.attach_timeout
17
+ @@attach_timeout
18
+ end
19
+ def self.attach_timeout=(timeout)
20
+ @@attach_timeout = timeout
21
+ end
22
+
23
+ # Return the options used when creating new instances of IE.
24
+ # BUG: this interface invites misunderstanding/misuse such as IE.options[:speed] = :zippy]
25
+ def self.options
26
+ {:speed => self.speed, :visible => self.visible, :attach_timeout => self.attach_timeout}
27
+ end
28
+ # set values for options used when creating new instances of IE.
29
+ def self.set_options options
30
+ options.each do |name, value|
31
+ send "#{name}=", value
32
+ end
33
+ end
34
+ # The globals $FAST_SPEED and $HIDE_IE are checked both at initialization
35
+ # and later, because they
36
+ # might be set after initialization. Setting them beforehand (e.g. from
37
+ # the command line) will affect the class, otherwise it is only a temporary
38
+ # effect
39
+ @@speed = $FAST_SPEED ? :fast : :slow
40
+ def self.speed
41
+ return :fast if $FAST_SPEED
42
+ @@speed
43
+ end
44
+ def self.speed= x
45
+ $FAST_SPEED = nil
46
+ @@speed = x
47
+ end
48
+ @@visible = $HIDE_IE ? false : true
49
+ def self.visible
50
+ return false if $HIDE_IE
51
+ @@visible
52
+ end
53
+ def self.visible= x
54
+ $HIDE_IE = nil
55
+ @@visible = x
56
+ end
57
+
58
+ # IE inserts some element whose tagName is empty and just acts as block level element
59
+ # Probably some IE method of cleaning things
60
+ # To pass the same to the xml parser we need to give some name to empty tagName
61
+ EMPTY_TAG_NAME = "DUMMY"
62
+
63
+ # The time, in seconds, it took for the new page to load after executing the
64
+ # the last command
65
+ attr_reader :down_load_time
66
+
67
+ # the OLE Internet Explorer object
68
+ attr_accessor :ie
69
+
70
+ # access to the logger object
71
+ attr_accessor :logger
72
+
73
+ # this contains the list of unique urls that have been visited
74
+ attr_reader :url_list
75
+
76
+ # Create a new IE window. Works just like IE.new in Watir 1.4.
77
+ def self.new_window
78
+ ie = new true
79
+ ie._new_window_init
80
+ ie
81
+ end
82
+
83
+ # Create an IE browser.
84
+ def initialize suppress_new_window=nil
85
+ _new_window_init unless suppress_new_window
86
+ end
87
+
88
+ def _new_window_init
89
+ create_browser_window
90
+ initialize_options
91
+ goto 'about:blank' # this avoids numerous problems caused by lack of a document
92
+ end
93
+
94
+ # Create a new IE Window, starting at the specified url.
95
+ # If no url is given, start empty.
96
+ def self.start url=nil
97
+ start_window url
98
+ end
99
+
100
+ # Create a new IE window, starting at the specified url.
101
+ # If no url is given, start empty. Works like IE.start in Watir 1.4.
102
+ def self.start_window url=nil
103
+ ie = new_window
104
+ ie.goto url if url
105
+ ie
106
+ end
107
+
108
+ # Create a new IE window in a new process.
109
+ # This method will not work when
110
+ # Vapir/Ruby is run under a service (instead of a user).
111
+ def self.new_process
112
+ ie = new true
113
+ ie._new_process_init
114
+ ie
115
+ end
116
+
117
+ def _new_process_init
118
+ iep = Process.start
119
+ @ie = iep.window
120
+ @process_id = iep.process_id
121
+ initialize_options
122
+ goto 'about:blank'
123
+ end
124
+
125
+ # Create a new IE window in a new process, starting at the specified URL.
126
+ # Same as IE.start.
127
+ def self.start_process url=nil
128
+ ie = new_process
129
+ ie.goto url if url
130
+ ie
131
+ end
132
+
133
+ # Return a Vapir::IE object for an existing IE window. Window can be
134
+ # referenced by url, title, or window handle.
135
+ # Second argument can be either a string or a regular expression in the
136
+ # case of of :url or :title.
137
+ # IE.attach(:url, 'http://www.google.com')
138
+ # IE.attach(:title, 'Google')
139
+ # IE.attach(:hwnd, 528140)
140
+ # This method will not work when
141
+ # Vapir/Ruby is run under a service (instead of a user).
142
+ def self.attach how, what
143
+ ie = new true # don't create window
144
+ ie._attach_init(how, what)
145
+ ie
146
+ end
147
+
148
+ # this method is used internally to attach to an existing window
149
+ def _attach_init how, what
150
+ attach_browser_window how, what
151
+ initialize_options
152
+ wait
153
+ end
154
+
155
+ # Return an IE object that wraps the given window, typically obtained from
156
+ # Shell.Application.windows.
157
+ def self.bind window
158
+ ie = new true
159
+ ie.ie = window
160
+ ie.initialize_options
161
+ ie
162
+ end
163
+
164
+ def create_browser_window
165
+ @ie = WIN32OLE.new('InternetExplorer.Application')
166
+ end
167
+ private :create_browser_window
168
+
169
+ def initialize_options
170
+ self.visible = IE.visible
171
+ self.speed = IE.speed
172
+
173
+ @element_object = nil
174
+ @page_container = self
175
+ @error_checkers = []
176
+
177
+ @logger = DefaultLogger.new
178
+ @url_list = []
179
+ end
180
+
181
+ # Specifies the speed that commands will be executed at. Choices are:
182
+ # * :slow (default)
183
+ # * :fast
184
+ # * :zippy
185
+ # With IE#speed= :zippy, text fields will be entered at once, instead of
186
+ # character by character (default).
187
+ def speed= how_fast
188
+ case how_fast
189
+ when :zippy then
190
+ @typingspeed = 0
191
+ @pause_after_wait = 0.01
192
+ @type_keys = false
193
+ @speed = :fast
194
+ when :fast then
195
+ @typingspeed = 0
196
+ @pause_after_wait = 0.01
197
+ @type_keys = true
198
+ @speed = :fast
199
+ when :slow then
200
+ @typingspeed = 0.08
201
+ @pause_after_wait = 0.1
202
+ @type_keys = true
203
+ @speed = :slow
204
+ else
205
+ raise ArgumentError, "Invalid speed: #{how_fast}"
206
+ end
207
+ end
208
+
209
+ def speed
210
+ return @speed if @speed == :slow
211
+ return @type_keys ? :fast : :zippy
212
+ end
213
+
214
+ # deprecated: use speed = :fast instead
215
+ def set_fast_speed
216
+ self.speed = :fast
217
+ end
218
+
219
+ # deprecated: use speed = :slow instead
220
+ def set_slow_speed
221
+ self.speed = :slow
222
+ end
223
+
224
+ def visible
225
+ assert_exists
226
+ @ie.visible
227
+ end
228
+ def visible=(boolean)
229
+ assert_exists
230
+ @ie.visible = boolean if boolean != @ie.visible
231
+ end
232
+
233
+ # Yields successively to each IE window on the current desktop. Takes a block.
234
+ # This method will not work when
235
+ # Vapir/Ruby is run under a service (instead of a user).
236
+ # Yields to the window and its hwnd.
237
+ def self.each
238
+ shell = WIN32OLE.new('Shell.Application')
239
+ shell.Windows.each do |window|
240
+ next unless (window.path =~ /Internet Explorer/ rescue false)
241
+ next unless (hwnd = window.hwnd rescue false)
242
+ ie = IE.bind(window)
243
+ ie.hwnd = hwnd
244
+ yield ie
245
+ end
246
+ end
247
+
248
+ # return internet explorer instance as specified. if none is found,
249
+ # return nil.
250
+ # arguments:
251
+ # :url, url -- the URL of the IE browser window
252
+ # :title, title -- the title of the browser page
253
+ # :hwnd, hwnd -- the window handle of the browser window.
254
+ # This method will not work when
255
+ # Vapir/Ruby is run under a service (instead of a user).
256
+ def self.find(how, what)
257
+ ie_ole = IE._find(how, what)
258
+ IE.bind ie_ole if ie_ole
259
+ end
260
+
261
+ def self._find(how, what)
262
+ ieTemp = nil
263
+ IE.each do |ie|
264
+ window = ie.ie
265
+
266
+ case how
267
+ when :url
268
+ ieTemp = window if Vapir::fuzzy_match(window.locationURL, what)
269
+ when :title
270
+ # normal windows explorer shells do not have document
271
+ # note window.document will fail for "new" browsers
272
+ begin
273
+ title = window.locationname
274
+ title = window.document.title
275
+ rescue WIN32OLERuntimeError
276
+ end
277
+ ieTemp = window if Vapir::fuzzy_match(title, what)
278
+ when :hwnd
279
+ begin
280
+ ieTemp = window if what == window.HWND
281
+ rescue WIN32OLERuntimeError
282
+ end
283
+ else
284
+ raise ArgumentError
285
+ end
286
+ end
287
+ return ieTemp
288
+ end
289
+
290
+ def attach_browser_window how, what
291
+ log "Seeking Window with #{how}: #{what}"
292
+ ieTemp = nil
293
+ begin
294
+ Vapir::until_with_timeout do
295
+ ieTemp = IE._find how, what
296
+ end
297
+ rescue TimeOutException
298
+ raise NoMatchingWindowFoundException,
299
+ "Unable to locate a window with #{how} of #{what}"
300
+ end
301
+ @ie = ieTemp
302
+ end
303
+ private :attach_browser_window
304
+
305
+ def browser_object
306
+ assert_exists
307
+ @ie
308
+ end
309
+
310
+ # Return the current window handle
311
+ def hwnd
312
+ assert_exists
313
+ @hwnd ||= @ie.hwnd
314
+ end
315
+ attr_writer :hwnd
316
+
317
+ def win_window
318
+ @win_window||= WinWindow.new(hwnd)
319
+ end
320
+
321
+ def modal_dialog(options={})
322
+ assert_exists do
323
+ raise ArgumentError, "options argument must be a hash; received #{options.inspect} (#{options.class})" unless options.is_a?(Hash)
324
+ modal=IE::ModalDialog.new(self, options.merge(:error => false))
325
+ modal.exists? ? modal : nil
326
+ end
327
+ end
328
+
329
+ def modal_dialog!(options={})
330
+ assert_exists do
331
+ IE::ModalDialog.new(self, options.merge(:error => true))
332
+ end
333
+ end
334
+
335
+ # we expect one of these error codes when quitting or checking existence.
336
+ ExistenceFailureCodesRE = Regexp.new(
337
+ { '0x800706ba' => 'The RPC server is unavailable',
338
+ '0x80010108' => 'The object invoked has disconnected from its clients.',
339
+ '0x800706be' => 'The remote procedure call failed.',
340
+ }.keys.join('|'), Regexp::IGNORECASE)
341
+
342
+ # Are we attached to an open browser?
343
+ def exists?
344
+ !!(@ie && begin
345
+ @ie.name
346
+ rescue WIN32OLERuntimeError
347
+ raise unless $!.message =~ ExistenceFailureCodesRE
348
+ false
349
+ end)
350
+ end
351
+ alias :exist? :exists?
352
+
353
+ # deprecated: use logger= instead
354
+ def set_logger(logger)
355
+ @logger = logger
356
+ end
357
+
358
+ def log(what)
359
+ @logger.debug(what) if @logger
360
+ end
361
+
362
+ #
363
+ # Accessing data outside the document
364
+ #
365
+
366
+ # Return the title of the document
367
+ def title
368
+ @ie.document.title
369
+ end
370
+
371
+ # Return the status of the window, typically from the status bar at the bottom.
372
+ def status
373
+ return @ie.statusText
374
+ end
375
+
376
+ #
377
+ # Navigation
378
+ #
379
+
380
+ # Navigate to the specified URL.
381
+ # * url - string - the URL to navigate to
382
+ def goto(url)
383
+ assert_exists do
384
+ @ie.navigate(url)
385
+ wait
386
+ return @down_load_time
387
+ end
388
+ end
389
+
390
+ # Go to the previous page - the same as clicking the browsers back button
391
+ # an WIN32OLERuntimeError exception is raised if the browser cant go back
392
+ def back
393
+ assert_exists do
394
+ @ie.GoBack
395
+ wait
396
+ end
397
+ end
398
+
399
+ # Go to the next page - the same as clicking the browsers forward button
400
+ # an WIN32OLERuntimeError exception is raised if the browser cant go forward
401
+ def forward
402
+ assert_exists do
403
+ @ie.GoForward
404
+ wait
405
+ end
406
+ end
407
+
408
+ module RefreshConstants
409
+ # http://msdn.microsoft.com/en-us/library/bb268230%28v=VS.85%29.aspx
410
+ REFRESH_NORMAL = 0
411
+ REFRESH_IFEXPIRED = 1
412
+ REFRESH_COMPLETELY = 3
413
+ end
414
+ # Refresh the current page - the same as clicking the browsers refresh button
415
+ # an WIN32OLERuntimeError exception is raised if the browser cant refresh
416
+ def refresh
417
+ assert_exists do
418
+ @ie.refresh2(RefreshConstants::REFRESH_COMPLETELY)
419
+ wait
420
+ end
421
+ end
422
+
423
+ # clear the list of urls that we have visited
424
+ def clear_url_list
425
+ @url_list.clear
426
+ end
427
+
428
+ # Closes the Browser
429
+ def close
430
+ assert_exists
431
+ @ie.stop
432
+ @ie.quit
433
+ # TODO/fix timeout; this shouldn't be a hard-coded magic number.
434
+ ::Waiter.try_for(32, :exception => WindowFailedToCloseException.new("The browser window did not close"), :interval => 1) do
435
+ begin
436
+ if exists?
437
+ @ie.quit
438
+ false
439
+ else
440
+ true
441
+ end
442
+ rescue WIN32OLERuntimeError
443
+ raise unless $!.message =~ ExistenceFailureCodesRE
444
+ true
445
+ end
446
+ end
447
+ @ie=nil
448
+ end
449
+
450
+ # Maximize the window (expands to fill the screen)
451
+ def maximize
452
+ win_window.maximize!
453
+ end
454
+
455
+ # Minimize the window (appears as icon on taskbar)
456
+ def minimize
457
+ win_window.minimize!
458
+ end
459
+
460
+ # Restore the window (after minimizing or maximizing)
461
+ def restore
462
+ win_window.restore!
463
+ end
464
+
465
+ # Make the window come to the front
466
+ def bring_to_front
467
+ win_window.really_set_foreground!
468
+ end
469
+
470
+ def front?
471
+ win_window.foreground?
472
+ end
473
+
474
+ # Send key events to IE window.
475
+ # See http://www.autoitscript.com/autoit3/docs/appendix/SendKeys.htm
476
+ # for complete documentation on keys supported and syntax.
477
+ def send_keys(key_string)
478
+ assert_exists do
479
+ require 'vapir-ie/autoit'
480
+ bring_to_front
481
+ Vapir.autoit.Send key_string
482
+ end
483
+ end
484
+
485
+ # saves a screenshot of this browser window to the given filename.
486
+ #
487
+ # second argument, optional, specifies what area to take a screenshot of.
488
+ # - :client takes a screenshot of the client area, which excludes the menu bar and other window trimmings.
489
+ # - :window (default) takes a screenshot of the full browser window
490
+ # - :desktop takes a screenshot of the full desktop
491
+ def screen_capture(filename, dc=:window)
492
+ if dc==:desktop
493
+ screenshot_win=WinWindow.desktop_window
494
+ dc=:window
495
+ else
496
+ screenshot_win=win_window
497
+ end
498
+ screenshot_win.capture_to_bmp_file(filename, :dc => dc)
499
+ end
500
+
501
+ def dir
502
+ return File.expand_path(File.dirname(__FILE__))
503
+ end
504
+
505
+ #
506
+ # Document and Document Data
507
+ #
508
+
509
+ # Return the current document
510
+ def document
511
+ assert_exists
512
+ return @ie.document
513
+ end
514
+ alias document_object document
515
+
516
+ def browser
517
+ self
518
+ end
519
+
520
+ # returns the current url, as displayed in the address bar of the browser
521
+ def url
522
+ assert_exists
523
+ return @ie.LocationURL
524
+ end
525
+
526
+ # Error checkers
527
+
528
+ # this method runs the predefined error checks
529
+ def run_error_checks
530
+ assert_exists do
531
+ @error_checkers.each { |e| e.call(self) }
532
+ end
533
+ end
534
+
535
+ # this method is used to add an error checker that gets executed on every page load
536
+ # * checker Proc Object, that contains the code to be run
537
+ def add_checker(checker)
538
+ @error_checkers << checker
539
+ end
540
+
541
+ # this allows a checker to be disabled
542
+ # * checker Proc Object, the checker that is to be disabled
543
+ def disable_checker(checker)
544
+ @error_checkers.delete(checker)
545
+ end
546
+
547
+ # this method shows the name, id etc of the object that is currently active - ie the element that has focus
548
+ # its mostly used in irb when creating a script
549
+ def show_active # TODO/fix: move to common; test
550
+
551
+ current_object = document.activeElement
552
+ current_element = base_class.factory(current_object)
553
+ current_element.to_s
554
+ end
555
+
556
+ # Gives focus to the frame
557
+ def focus
558
+ document.activeElement.blur
559
+ document.focus
560
+ end
561
+
562
+
563
+ # Functions written for using xpath for getting the elements.
564
+ def xmlparser_document_object
565
+ if @xml_parser_doc == nil
566
+ create_xml_parser_doc
567
+ end
568
+ return @xml_parser_doc
569
+ end
570
+
571
+ # Create the Nokogiri object if it is nil. This method is private so can be called only
572
+ # from xmlparser_document_object method.
573
+ def create_xml_parser_doc
574
+ require 'nokogiri'
575
+ if @xml_parser_doc == nil
576
+ htmlSource ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<HTML>\n"
577
+ htmlSource = html_source(document.body,htmlSource," ")
578
+ htmlSource += "\n</HTML>\n"
579
+ # Angrez: Resolving Jira issue WTR-114
580
+ htmlSource = htmlSource.gsub(/&nbsp;/, '&#160;')
581
+ begin
582
+ #@xml_parser_doc = Nokogiri::HTML::Document.new(htmlSource)
583
+ @xml_parser_doc = Nokogiri.parse(htmlSource)
584
+ rescue => e
585
+ output_xml_parser_doc("error.xml", htmlSource)
586
+ raise e
587
+ end
588
+ end
589
+ end
590
+ private :create_xml_parser_doc
591
+
592
+ def output_xml_parser_doc(name, text)
593
+ file = File.open(name,"w")
594
+ file.print(text)
595
+ file.close
596
+ end
597
+ private :output_xml_parser_doc
598
+
599
+ #Function Tokenizes the tag line and returns array of tokens.
600
+ #Token could be either tagName or "=" or attribute name or attribute value
601
+ #Attribute value could be either quoted string or single word
602
+ def tokenize_tagline(outerHtml)
603
+ outerHtml = outerHtml.gsub(/\n|\r/," ")
604
+ #removing "< symbol", opening of current tag
605
+ outerHtml =~ /^\s*<(.*)$/
606
+ outerHtml = $1
607
+ tokens = Array.new
608
+ i = startOffset = 0
609
+ length = outerHtml.length
610
+ #puts outerHtml
611
+ parsingValue = false
612
+ while i < length do
613
+ i +=1 while (i < length && outerHtml[i,1] =~ /\s/)
614
+ next if i == length
615
+ currentToken = outerHtml[i,1]
616
+
617
+ #Either current tag has been closed or user has not closed the tag >
618
+ # and we have received the opening of next element
619
+ break if currentToken =~ /<|>/
620
+
621
+ #parse quoted value
622
+ if(currentToken == "\"" || currentToken == "'")
623
+ parsingValue = false
624
+ quote = currentToken
625
+ startOffset = i
626
+ i += 1
627
+ i += 1 while (i < length && (outerHtml[i,1] != quote || outerHtml[i-1,1] == "\\"))
628
+ if i == length
629
+ tokens.push quote + outerHtml[startOffset..i-1]
630
+ else
631
+ tokens.push outerHtml[startOffset..i]
632
+ end
633
+ elsif currentToken == "="
634
+ tokens.push "="
635
+ parsingValue = true
636
+ else
637
+ startOffset = i
638
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|=|<|>/)) if !parsingValue
639
+ i += 1 while (i < length && !(outerHtml[i,1] =~ /\s|<|>/)) if parsingValue
640
+ parsingValue = false
641
+ i -= 1
642
+ tokens.push outerHtml[startOffset..i]
643
+ end
644
+ i += 1
645
+ end
646
+ return tokens
647
+ end
648
+ private :tokenize_tagline
649
+
650
+ # This function get and clean all the attributes of the tag.
651
+ def all_tag_attributes(outerHtml)
652
+ tokens = tokenize_tagline(outerHtml)
653
+ #puts tokens
654
+ tagLine = ""
655
+ count = 1
656
+ tokensLength = tokens.length
657
+ expectedEqualityOP= false
658
+ while count < tokensLength do
659
+ if expectedEqualityOP == false
660
+ #print Attribute Name
661
+ # If attribute name is valid. Refer: http://www.w3.org/TR/REC-xml/#NT-Name
662
+ if tokens[count] =~ /^(\w|_|:)(.*)$/
663
+ tagLine += " #{tokens[count]}"
664
+ expectedEqualityOP = true
665
+ end
666
+ elsif tokens[count] == "="
667
+ count += 1
668
+ if count == tokensLength
669
+ tagLine += "=\"\""
670
+ elsif(tokens[count][0,1] == "\"" || tokens[count][0,1] == "'")
671
+ tagLine += "=#{tokens[count]}"
672
+ else
673
+ tagLine += "=\"#{tokens[count]}\""
674
+ end
675
+ expectedEqualityOP = false
676
+ else
677
+ #Opps! equality was expected but its not there.
678
+ #Set value same as the attribute name e.g. selected="selected"
679
+ tagLine += "=\"#{tokens[count-1]}\""
680
+ expectedEqualityOP = false
681
+ next
682
+ end
683
+ count += 1
684
+ end
685
+ tagLine += "=\"#{tokens[count-1]}\" " if expectedEqualityOP == true
686
+ #puts tagLine
687
+ return tagLine
688
+ end
689
+ private :all_tag_attributes
690
+
691
+ # This function is used to escape the characters that are not valid XML data.
692
+ def xml_escape(str)
693
+ str = str.gsub(/&/,'&amp;')
694
+ str = str.gsub(/</,'&lt;')
695
+ str = str.gsub(/>/,'&gt;')
696
+ str = str.gsub(/"/, '&quot;')
697
+ str
698
+ end
699
+ private :xml_escape
700
+
701
+ # Returns HTML Source
702
+ # Traverse the DOM tree rooted at body element
703
+ # and generate the HTML source.
704
+ # element: Represent Current element
705
+ # htmlString:HTML Source
706
+ # spaces:(Used for debugging). Helps in indentation
707
+ def html_source(element, htmlString, spaceString)
708
+ begin
709
+ tagLine = ""
710
+ outerHtml = ""
711
+ tagName = ""
712
+ begin
713
+ tagName = element.tagName.downcase
714
+ tagName = EMPTY_TAG_NAME if tagName == ""
715
+ # If tag is a mismatched tag.
716
+ if !(tagName =~ /^(\w|_|:)(.*)$/)
717
+ return htmlString
718
+ end
719
+ rescue
720
+ #handling text nodes
721
+ htmlString += xml_escape(element.toString)
722
+ return htmlString
723
+ end
724
+ #puts tagName
725
+ #Skip comment and script tag
726
+ if tagName =~ /^!/ || tagName== "script" || tagName =="style"
727
+ return htmlString
728
+ end
729
+ #tagLine += spaceString
730
+ outerHtml = all_tag_attributes(element.outerHtml) if tagName != EMPTY_TAG_NAME
731
+ tagLine += "<#{tagName} #{outerHtml}"
732
+
733
+ canHaveChildren = element.canHaveChildren
734
+ if canHaveChildren
735
+ tagLine += ">"
736
+ else
737
+ tagLine += "/>" #self closing tag
738
+ end
739
+ #spaceString += spaceString
740
+ htmlString += tagLine
741
+ childElements = element.childnodes
742
+ childElements.each do |child|
743
+ htmlString = html_source(child,htmlString,spaceString)
744
+ end
745
+ if canHaveChildren
746
+ #tagLine += spaceString
747
+ tagLine ="</" + tagName + ">"
748
+ htmlString += tagLine
749
+ end
750
+ return htmlString
751
+ rescue => e
752
+ puts e.to_s
753
+ end
754
+ return htmlString
755
+ end
756
+ private :html_source
757
+
758
+ public
759
+ # return the first element object (not Element) that matches the xpath
760
+ def element_object_by_xpath(xpath)
761
+ objects= element_objects_by_xpath(xpath)
762
+ return (objects && objects[0])
763
+ end
764
+
765
+ # execute xpath and return an array of elements
766
+ def element_objects_by_xpath(xpath)
767
+ doc = xmlparser_document_object
768
+ modifiedXpath = ""
769
+ selectedElements = Array.new
770
+
771
+ # strip any trailing slash from the xpath expression (as used in watir unit tests)
772
+ xpath.chop! unless (/\/$/ =~ xpath).nil?
773
+
774
+ doc.xpath(xpath).each do |element|
775
+ modifiedXpath = element.path
776
+ temp = element_by_absolute_xpath(modifiedXpath) # temp = a DOM/COM element
777
+ selectedElements << temp if temp != nil
778
+ end
779
+ #puts selectedElements.length
780
+ if selectedElements.length == 0
781
+ return nil
782
+ else
783
+ return selectedElements
784
+ end
785
+ end
786
+
787
+ # Method that iterates over IE DOM object and get the elements for the given
788
+ # xpath.
789
+ def element_by_absolute_xpath(xpath)
790
+ curElem = nil
791
+
792
+ #puts "Hello; Given xpath is : #{xpath}"
793
+ doc = document
794
+ curElem = doc.getElementsByTagName("body").item(0)
795
+ xpath =~ /^.*\/body\[?\d*\]?\/(.*)/
796
+ xpath = $1
797
+
798
+ if xpath == nil
799
+ puts "Function Requires absolute XPath."
800
+ return
801
+ end
802
+
803
+ arr = xpath.split(/\//)
804
+ return nil if arr.length == 0
805
+
806
+ lastTagName = arr[arr.length-1].to_s.upcase
807
+
808
+ # lastTagName is like tagName[number] or just tagName. For the first case we need to
809
+ # separate tagName and number.
810
+ lastTagName =~ /(\w*)\[?\d*\]?/
811
+ lastTagName = $1
812
+ #puts lastTagName
813
+
814
+ for element in arr do
815
+ element =~ /(\w*)\[?(\d*)\]?/
816
+ tagname = $1
817
+ tagname = tagname.upcase
818
+
819
+ if $2 != nil && $2 != ""
820
+ index = $2
821
+ index = "#{index}".to_i - 1
822
+ else
823
+ index = 0
824
+ end
825
+
826
+ #puts "#{element} #{tagname} #{index}"
827
+ allElemns = curElem.childnodes
828
+ if allElemns == nil || allElemns.length == 0
829
+ puts "#{element} is null"
830
+ next # Go to next element
831
+ end
832
+
833
+ #puts "Current element is : #{curElem.tagName}"
834
+ allElemns.each do |child|
835
+ gotIt = false
836
+ begin
837
+ curTag = child.tagName
838
+ curTag = EMPTY_TAG_NAME if curTag == ""
839
+ rescue
840
+ next
841
+ end
842
+ #puts child.tagName
843
+ if curTag == tagname
844
+ index-=1
845
+ if index < 0
846
+ curElem = child
847
+ break
848
+ end
849
+ end
850
+ end
851
+
852
+ #puts "Node selected at index #{index.to_s} : #{curElem.tagName}"
853
+ end
854
+ begin
855
+ if curElem.tagName == lastTagName
856
+ #puts curElem.tagName
857
+ return curElem
858
+ else
859
+ return nil
860
+ end
861
+ rescue
862
+ return nil
863
+ end
864
+ end
865
+ private :element_by_absolute_xpath
866
+
867
+ def attach_command
868
+ "Vapir::IE.attach(:hwnd, #{hwnd})"
869
+ end
870
+
871
+ private
872
+ def base_element_class
873
+ IE::Element
874
+ end
875
+ def browser_class
876
+ IE
877
+ end
878
+
879
+ end # class IE
880
+ end