eyes_selenium 3.14.3 → 3.14.4
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.
- checksums.yaml +5 -5
- data/lib/applitools/selenium/border_aware_element_content_location_provider.rb +2 -0
- data/lib/applitools/selenium/browser.rb +66 -56
- data/lib/applitools/selenium/context_based_scale_provider.rb +2 -0
- data/lib/applitools/selenium/css_transform/css_transform.rb +21 -0
- data/lib/applitools/selenium/css_translate_element_position_provider.rb +48 -0
- data/lib/applitools/selenium/css_translate_position_provider.rb +29 -10
- data/lib/applitools/selenium/dom_capture/dom_capture.rb +113 -0
- data/lib/applitools/selenium/dom_capture/dom_capture_script.rb +103 -0
- data/lib/applitools/selenium/driver.rb +9 -2
- data/lib/applitools/selenium/element.rb +2 -0
- data/lib/applitools/selenium/element_position_provider.rb +3 -1
- data/lib/applitools/selenium/entire_element_screenshot.rb +20 -0
- data/lib/applitools/selenium/eyes.rb +203 -396
- data/lib/applitools/selenium/eyes_screenshot.rb +60 -0
- data/lib/applitools/selenium/eyes_target_locator.rb +12 -24
- data/lib/applitools/selenium/fixed_cut_provider.rb +48 -0
- data/lib/applitools/selenium/frame.rb +11 -1
- data/lib/applitools/selenium/frame_chain.rb +9 -1
- data/lib/applitools/selenium/full_page_capture_algorithm.rb +85 -50
- data/lib/applitools/selenium/fullpage_screenshot.rb +37 -0
- data/lib/applitools/selenium/keyboard.rb +2 -0
- data/lib/applitools/selenium/mouse.rb +2 -0
- data/lib/applitools/selenium/move_to_region_visibility_strategy.rb +2 -0
- data/lib/applitools/selenium/nop_region_visibility_strategy.rb +2 -0
- data/lib/applitools/selenium/region_provider.rb +91 -0
- data/lib/applitools/selenium/sauce/driver.rb +2 -0
- data/lib/applitools/selenium/scroll_position_provider.rb +13 -2
- data/lib/applitools/selenium/takes_screenshot_image_provider.rb +5 -3
- data/lib/applitools/selenium/target.rb +32 -6
- data/lib/applitools/selenium/viewport_screenshot.rb +46 -0
- data/lib/applitools/selenium/viewport_size.rb +2 -0
- data/lib/applitools/version.rb +3 -1
- data/lib/eyes_selenium.rb +4 -9
- metadata +16 -27
- data/lib/applitools/capybara.rb +0 -8
- data/lib/applitools/poltergeist/applitools_compatible.rb +0 -32
- data/lib/applitools/poltergeist/driver.rb +0 -11
- data/lib/applitools/selenium/capybara/capybara_settings.rb +0 -23
- data/lib/applitools/selenium/capybara/driver.rb +0 -37
- data/lib/applitools/selenium/eyes_full_page_screenshot.rb +0 -47
- data/lib/applitools/selenium/eyes_web_driver_screenshot.rb +0 -341
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eed17b1ab4d0917d2cb7f0048435f2b8b5b5a41c29607bfc82caa0da521e071e
|
4
|
+
data.tar.gz: d8398e29b30ba60e8d033608d0e70193413866e5509c057206357840b58ca0e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 214c9dd3f2085254d46241980ca5a416a4b4f70cda46f54a65adf36b85198d53db267fb9d8275c5d98f1f9543e06343dcbe36938d80eab7c1b1fe2e4e013bc20
|
7
|
+
data.tar.gz: 824ea45b22311de40906cd2a2e19b8652d7eff6b778e6f43379acc7f600397e7d5ad0a49ee55909e29303d585241200c16771e976e346546dbe8c51f8e680064
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
1
3
|
module Applitools::Selenium
|
2
4
|
# @!visibility private
|
3
5
|
class Browser
|
@@ -69,7 +71,15 @@ module Applitools::Selenium
|
|
69
71
|
end
|
70
72
|
|
71
73
|
def chrome?
|
72
|
-
|
74
|
+
running_browser_name == :chrome
|
75
|
+
end
|
76
|
+
|
77
|
+
def firefox?
|
78
|
+
running_browser_name == :firefox
|
79
|
+
end
|
80
|
+
|
81
|
+
def running_browser_name
|
82
|
+
@driver.__getobj__.browser
|
73
83
|
end
|
74
84
|
|
75
85
|
def user_agent
|
@@ -149,62 +159,62 @@ module Applitools::Selenium
|
|
149
159
|
set_transform("translate(-#{point.left}px, -#{point.top}px)")
|
150
160
|
end
|
151
161
|
|
152
|
-
# Takes a full page screenshot.
|
162
|
+
# # Takes a full page screenshot.
|
163
|
+
# #
|
164
|
+
# # @return [ChunkyPNG::Canvas] image The result of the screenshot.
|
165
|
+
# def fullpage_screenshot
|
166
|
+
# # Scroll to the top/left corner of the screen.
|
167
|
+
# original_scroll_position = current_scroll_position
|
168
|
+
# scroll_to(Applitools::Base::Point::TOP_LEFT)
|
169
|
+
# if current_scroll_position != Applitools::Base::Point::TOP_LEFT
|
170
|
+
# raise 'Could not scroll to the top/left corner of the screen!'
|
171
|
+
# end
|
153
172
|
#
|
154
|
-
#
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
process_screenshot_part(screenshot_part, size_factor)
|
201
|
-
end
|
202
|
-
screenshot = Applitools::Utils::ImageUtils.stitch_images(page_size, parts)
|
203
|
-
end
|
204
|
-
set_transform(original_transform) if @eyes.use_css_transition
|
205
|
-
scroll_to(original_scroll_position)
|
206
|
-
screenshot
|
207
|
-
end
|
173
|
+
# # Translate to top/left of the page (notice this is different from JavaScript scrolling).
|
174
|
+
# if @eyes.use_css_transition
|
175
|
+
# original_transform = current_transform
|
176
|
+
# translate_to(Applitools::Base::Point::TOP_LEFT)
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# # Take screenshot of the (0,0) tile.
|
180
|
+
# screenshot = @driver.visible_screenshot
|
181
|
+
#
|
182
|
+
# # Normalize screenshot width/height.
|
183
|
+
# size_factor = 1
|
184
|
+
# page_size = entire_page_size
|
185
|
+
# factor = image_normalization_factor(screenshot)
|
186
|
+
# if factor == 0.5
|
187
|
+
# size_factor = 2
|
188
|
+
# page_size.width *= size_factor
|
189
|
+
# page_size.height *= size_factor
|
190
|
+
# page_size.width = [page_size.width, screenshot.width].max
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# # NOTE: this is required! Since when calculating the screenshot parts for full size, we use a screenshot size
|
194
|
+
# # which is a bit smaller (see comment below).
|
195
|
+
# if screenshot.width < page_size.width || screenshot.height < page_size.height
|
196
|
+
# # We use a smaller size than the actual screenshot size in order to eliminate duplication of bottom scroll bars,
|
197
|
+
# # as well as footer-like elements with fixed position.
|
198
|
+
# max_scrollbar_size = @eyes.use_css_transition ? 0 : MAX_SCROLLBAR_SIZE
|
199
|
+
# height = [screenshot.height - (max_scrollbar_size * size_factor), MIN_SCREENSHOT_PART_HEIGHT * size_factor].max
|
200
|
+
# screenshot_part_size = Applitools::Base::Dimension.new(screenshot.width, height)
|
201
|
+
#
|
202
|
+
# sub_regions = Applitools::Base::Region.new(0, 0, page_size.width,
|
203
|
+
# page_size.height).subregions(screenshot_part_size)
|
204
|
+
# parts = sub_regions.map do |screenshot_part|
|
205
|
+
# # Skip (0,0), as we already got the screenshot.
|
206
|
+
# if screenshot_part.left.zero? && screenshot_part.top.zero?
|
207
|
+
# next Applitools::Base::ImagePosition.new(screenshot, Applitools::Base::Point::TOP_LEFT)
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# process_screenshot_part(screenshot_part, size_factor)
|
211
|
+
# end
|
212
|
+
# screenshot = Applitools::Utils::ImageUtils.stitch_images(page_size, parts)
|
213
|
+
# end
|
214
|
+
# set_transform(original_transform) if @eyes.use_css_transition
|
215
|
+
# scroll_to(original_scroll_position)
|
216
|
+
# screenshot
|
217
|
+
# end
|
208
218
|
|
209
219
|
private
|
210
220
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Applitools
|
4
|
+
module CssTransform
|
5
|
+
private
|
6
|
+
|
7
|
+
def get_position_from_transform(transform)
|
8
|
+
regexp = /^translate\(\s*(\-?)([\d, \.]+)px,\s*(\-?)([\d, \.]+)px\s*\)/
|
9
|
+
data = regexp.match(transform)
|
10
|
+
|
11
|
+
raise Applitools::EyesError.new "Can't parse CSS transition: #{transform}!" unless data
|
12
|
+
x = data[2].to_f.round
|
13
|
+
y = data[4].to_f.round
|
14
|
+
|
15
|
+
x *= -1 unless data[1].empty?
|
16
|
+
y *= -1 unless data[3].empty?
|
17
|
+
|
18
|
+
Applitools::Location.new(x, y)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'element_position_provider'
|
4
|
+
require_relative 'css_transform/css_transform'
|
5
|
+
|
6
|
+
module Applitools::Selenium
|
7
|
+
# @!visibility private
|
8
|
+
class CssTranslateElementPositionProvider < ElementPositionProvider
|
9
|
+
include Applitools::CssTransform
|
10
|
+
|
11
|
+
# Gets the current position.
|
12
|
+
#
|
13
|
+
# @return [Applitools::Location] The location.
|
14
|
+
def current_position
|
15
|
+
position = super.offset_negative transforms_offset
|
16
|
+
logger.info "Current position is #{position}"
|
17
|
+
position
|
18
|
+
end
|
19
|
+
|
20
|
+
def position=(location)
|
21
|
+
super
|
22
|
+
out_of_eyes = location.dup.offset_negative(current_position)
|
23
|
+
return if out_of_eyes == Applitools::Location::TOP_LEFT
|
24
|
+
logger.info "Moving element by #{out_of_eyes} to fit in the eyes region"
|
25
|
+
|
26
|
+
Applitools::Utils::EyesSeleniumUtils.element_translate_to(
|
27
|
+
driver,
|
28
|
+
element,
|
29
|
+
transforms_offset.offset_negative(out_of_eyes)
|
30
|
+
)
|
31
|
+
logger.info 'Done scrolling element!'
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def transforms_offset
|
37
|
+
logger.info 'Getting element transforms...'
|
38
|
+
transforms = Applitools::Utils::EyesSeleniumUtils.current_element_transforms(driver, element)
|
39
|
+
logger.info "Current transforms: #{transforms}"
|
40
|
+
transform_positions = transforms.values.compact.select { |s| !s.empty? }
|
41
|
+
.map { |s| get_position_from_transform(s) }
|
42
|
+
transform_positions.each do |p|
|
43
|
+
raise Applitools::EyesError.new 'Got different css positions!' unless p == transform_positions[0]
|
44
|
+
end
|
45
|
+
transform_positions[0] || Applitools::Location::TOP_LEFT.dup
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'css_transform/css_transform'
|
4
|
+
|
1
5
|
module Applitools::Selenium
|
2
6
|
class CssTranslatePositionProvider
|
3
7
|
extend Forwardable
|
8
|
+
include Applitools::CssTransform
|
4
9
|
|
5
10
|
def_delegators 'Applitools::EyesLogger', :logger, :log_handler, :log_handler=
|
6
11
|
|
@@ -33,7 +38,7 @@ module Applitools::Selenium
|
|
33
38
|
#
|
34
39
|
# @param [Applitools::Location] value The location.
|
35
40
|
def restore_state(value)
|
36
|
-
transforms = value.values.select { |el| !el.empty? }
|
41
|
+
transforms = value.values.compact.select { |el| !el.empty? }
|
37
42
|
Applitools::Utils::EyesSeleniumUtils.set_transforms(executor, value)
|
38
43
|
if transforms.empty?
|
39
44
|
self.last_state_position = Applitools::Location::TOP_LEFT
|
@@ -62,7 +67,7 @@ module Applitools::Selenium
|
|
62
67
|
# Gets the entire size of the frame.
|
63
68
|
#
|
64
69
|
# @return [Applitools::RectangleSize] The entire size of the frame.
|
65
|
-
def entire_size
|
70
|
+
def entire_size(_image_width, _image_height)
|
66
71
|
viewport_size = Applitools::Utils::EyesSeleniumUtils.extract_viewport_size(executor)
|
67
72
|
result = Applitools::Utils::EyesSeleniumUtils.current_frame_content_entire_size(executor)
|
68
73
|
logger.info "Entire size: #{result}"
|
@@ -73,19 +78,33 @@ module Applitools::Selenium
|
|
73
78
|
result.height = [viewport_size.height, result.height].min if disable_vertical
|
74
79
|
logger.info "Actual size to scroll: #{result}"
|
75
80
|
result
|
81
|
+
|
82
|
+
# return result unless executor.frame_chain.empty?
|
83
|
+
|
84
|
+
# spp = Applitools::Selenium::ScrollPositionProvider.new(executor)
|
85
|
+
# original_scroll_position = spp.current_position
|
86
|
+
# spp.scroll_to_bottom_right
|
87
|
+
# bottom_right_position = spp.current_position
|
88
|
+
# spp.restore_state(original_scroll_position)
|
89
|
+
# Applitools::RectangleSize.new(bottom_right_position.x + image_width, bottom_right_position.y + image_height)
|
76
90
|
end
|
77
91
|
|
78
92
|
private
|
79
93
|
|
80
94
|
attr_accessor :executor, :disable_horizontal, :disable_vertical, :max_width, :max_height
|
81
95
|
|
82
|
-
def get_position_from_transform(transform)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
96
|
+
# def get_position_from_transform(transform)
|
97
|
+
# regexp = /^translate\(\s*(\-?)([\d, \.]+)px,\s*(\-?)([\d, \.]+)px\s*\)/
|
98
|
+
# data = regexp.match(transform)
|
99
|
+
#
|
100
|
+
# raise Applitools::EyesError.new "Can't parse CSS transition: #{transform}!" unless data
|
101
|
+
# x = data[2].to_f.round
|
102
|
+
# y = data[4].to_f.round
|
103
|
+
#
|
104
|
+
# x *= (-1) unless data[1].empty?
|
105
|
+
# y *= (-1) unless data[3].empty?
|
106
|
+
#
|
107
|
+
# Applitools::Location.new(x, y)
|
108
|
+
# end
|
90
109
|
end
|
91
110
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
require 'css_parser'
|
7
|
+
include Benchmark
|
8
|
+
module Applitools::Selenium
|
9
|
+
module DomCapture
|
10
|
+
CSS_DOWNLOAD_TIMEOUT = 2 # 2 seconds
|
11
|
+
DOM_CAPTURE_TIMEOUT = 10 # 10 seconds
|
12
|
+
|
13
|
+
extend self
|
14
|
+
|
15
|
+
def get_window_dom(driver, logger)
|
16
|
+
args_obj = {
|
17
|
+
'styleProps' => %w(
|
18
|
+
background-color background-image background-size color border-width
|
19
|
+
border-color border-style padding margin
|
20
|
+
),
|
21
|
+
'attributeProps' => nil,
|
22
|
+
'rectProps' => %w(width height top left),
|
23
|
+
'ignoredTagNames' => %w(HEAD SCRIPT)
|
24
|
+
}
|
25
|
+
dom_tree = ''
|
26
|
+
if Timeout.respond_to?(:timeout)
|
27
|
+
Timeout.timeout(DOM_CAPTURE_TIMEOUT) do
|
28
|
+
dom_tree = driver.execute_script(Applitools::Selenium::DomCapture::DOM_CAPTURE_SCRIPT, args_obj)
|
29
|
+
get_frame_dom(driver, { 'childNodes' => [dom_tree], 'tagName' => 'OUTER_HTML' }, logger)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
timeout(DOM_CAPTURE_TIMEOUT) do
|
33
|
+
dom_tree = driver.execute_script(Applitools::Selenium::DomCapture::DOM_CAPTURE_SCRIPT, args_obj)
|
34
|
+
get_frame_dom(driver, { 'childNodes' => [dom_tree], 'tagName' => 'OUTER_HTML' }, logger)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
dom_tree
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def get_frame_dom(driver, dom_tree, logger)
|
43
|
+
tag_name = dom_tree['tagName']
|
44
|
+
return unless tag_name
|
45
|
+
frame_index = 0
|
46
|
+
loop(driver, dom_tree, logger) do |dom_sub_tree|
|
47
|
+
# this block is called if IFRAME found
|
48
|
+
driver.switch_to.frame(index: frame_index)
|
49
|
+
get_frame_dom(driver, dom_sub_tree, logger)
|
50
|
+
driver.switch_to.parent_frame
|
51
|
+
frame_index += 1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def loop(driver, dom_tree, logger)
|
56
|
+
child_nodes = dom_tree['childNodes']
|
57
|
+
return unless child_nodes
|
58
|
+
iterate_child_nodes = proc do |node_childs|
|
59
|
+
node_childs.each do |node|
|
60
|
+
if node['tagName'].casecmp('IFRAME') == 0
|
61
|
+
yield(node) if block_given?
|
62
|
+
else
|
63
|
+
node['css'] = get_frame_bundled_css(driver, logger) if node['tagName'].casecmp('HTML') == 0
|
64
|
+
iterate_child_nodes.call(node['childNodes']) unless node['childNodes'].nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
iterate_child_nodes.call(child_nodes)
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_frame_bundled_css(driver, logger)
|
72
|
+
base_url = URI.parse(driver.current_url)
|
73
|
+
parser = CssParser::Parser.new import: true, absolute_paths: true
|
74
|
+
css_threads = []
|
75
|
+
css_items = []
|
76
|
+
driver.execute_script(Applitools::Selenium::DomCapture::CSS_CAPTURE_SCRIPT).each_with_index do |item, i|
|
77
|
+
if (v = item['text'])
|
78
|
+
css_items[i] = [v]
|
79
|
+
elsif (v = item['href'])
|
80
|
+
target_url = URI.parse(v)
|
81
|
+
url_to_load = target_url.absolute? ? target_url : base_url.merge(target_url)
|
82
|
+
css_threads << Thread.new(url_to_load) do |url|
|
83
|
+
if Timeout.respond_to?(:timeout)
|
84
|
+
Timeout.timeout(CSS_DOWNLOAD_TIMEOUT) do
|
85
|
+
css_string, = parser.send(:read_remote_file, url)
|
86
|
+
css_items[i] = [css_string, { base_uri: url }]
|
87
|
+
end
|
88
|
+
else
|
89
|
+
timeout(CSS_DOWNLOAD_TIMEOUT) do
|
90
|
+
css_string, = parser.send(:read_remote_file, url)
|
91
|
+
css_items[i] = [css_string, { base_uri: url }]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
begin
|
98
|
+
css_threads.each(&:join)
|
99
|
+
rescue CssParser::CircularReferenceError => e
|
100
|
+
logger.respond_to?(:error) && logger.error("Got a circular reference error! #{e.message}")
|
101
|
+
rescue CssParser::RemoteFileError => e
|
102
|
+
logger.respond_to?(:error) && logger.error("File download error - #{e.message}")
|
103
|
+
rescue StandardError => e
|
104
|
+
logger.respond_to?(:error) && logger.error("#{e.class} - #{e.message}")
|
105
|
+
end
|
106
|
+
|
107
|
+
css_items.each { |css| parser.add_block!(*css) if css && css[0] }
|
108
|
+
css_result = ''
|
109
|
+
parser.each_rule_set { |s| css_result.concat(s.to_s) }
|
110
|
+
css_result
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
module Applitools::Selenium
|
4
|
+
module DomCapture
|
5
|
+
CSS_CAPTURE_SCRIPT = <<-SCRIPT
|
6
|
+
function extractCssResources() {
|
7
|
+
return Array.from(document.querySelectorAll('link[rel="stylesheet"],style')).map(el => {
|
8
|
+
if (el.tagName.toUpperCase() === 'LINK') {
|
9
|
+
return {"href": el.getAttribute('href')};
|
10
|
+
} else {
|
11
|
+
return {"text": el.textContent};
|
12
|
+
}
|
13
|
+
});
|
14
|
+
}
|
15
|
+
return extractCssResources();
|
16
|
+
SCRIPT
|
17
|
+
DOM_CAPTURE_SCRIPT = <<-SCRIPT
|
18
|
+
function captureFrame({ styleProps, attributeProps, rectProps, ignoredTagNames }) {
|
19
|
+
const NODE_TYPES = {
|
20
|
+
ELEMENT: 1,
|
21
|
+
TEXT: 3,
|
22
|
+
};
|
23
|
+
function filter(x) {
|
24
|
+
return !!x;
|
25
|
+
}
|
26
|
+
function notEmptyObj(obj) {
|
27
|
+
return Object.keys(obj).length ? obj : undefined;
|
28
|
+
}
|
29
|
+
function iframeToJSON(el) {
|
30
|
+
const obj = elementToJSON(el);
|
31
|
+
try {
|
32
|
+
if (el.contentDocument) {
|
33
|
+
obj.childNodes = [captureNode(el.contentDocument.documentElement)];
|
34
|
+
}
|
35
|
+
} catch (ex) {
|
36
|
+
} finally {
|
37
|
+
return obj;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
function elementToJSON(el) {
|
41
|
+
const tagName = el.tagName.toUpperCase();
|
42
|
+
if (ignoredTagNames.indexOf(tagName) > -1) return null;
|
43
|
+
const computedStyle = window.getComputedStyle(el);
|
44
|
+
const boundingClientRect = el.getBoundingClientRect();
|
45
|
+
const style = {};
|
46
|
+
for (const p of styleProps) style[p] = computedStyle.getPropertyValue(p);
|
47
|
+
const rect = {};
|
48
|
+
for (const p of rectProps) rect[p] = boundingClientRect[p];
|
49
|
+
const attributes = {};
|
50
|
+
if (!attributeProps) {
|
51
|
+
if (el.hasAttributes()) {
|
52
|
+
var attrs = el.attributes;
|
53
|
+
for (const p of attrs) {
|
54
|
+
attributes[p.name] = p.value;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
else {
|
59
|
+
if (attributeProps.all) {
|
60
|
+
for (const p of attributeProps.all) {
|
61
|
+
if (el.hasAttribute(p)) attributes[p] = el.getAttribute(p);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
if (attributeProps[tagName]) {
|
65
|
+
for (const p of attributeProps[tagName]) {
|
66
|
+
if (el.hasAttribute(p)) attributes[p] = el.getAttribute(p);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
return {
|
71
|
+
tagName,
|
72
|
+
style: notEmptyObj(style),
|
73
|
+
rect: notEmptyObj(rect),
|
74
|
+
attributes: notEmptyObj(attributes),
|
75
|
+
childNodes: Array.prototype.map.call(el.childNodes, captureNode).filter(filter),
|
76
|
+
};
|
77
|
+
}
|
78
|
+
function captureTextNode(node) {
|
79
|
+
return {
|
80
|
+
tagName: '#text',
|
81
|
+
text: node.textContent,
|
82
|
+
};
|
83
|
+
}
|
84
|
+
function captureNode(node) {
|
85
|
+
switch (node.nodeType) {
|
86
|
+
case NODE_TYPES.TEXT:
|
87
|
+
return captureTextNode(node);
|
88
|
+
case NODE_TYPES.ELEMENT:
|
89
|
+
if (node.tagName.toUpperCase() === 'IFRAME') {
|
90
|
+
return iframeToJSON(node);
|
91
|
+
} else {
|
92
|
+
return elementToJSON(node);
|
93
|
+
}
|
94
|
+
default:
|
95
|
+
return null;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
return captureNode(document.documentElement);
|
99
|
+
}
|
100
|
+
return captureFrame(arguments[0]);
|
101
|
+
SCRIPT
|
102
|
+
end
|
103
|
+
end
|