poltergeist 0.7.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +201 -62
- data/lib/capybara/poltergeist.rb +1 -1
- data/lib/capybara/poltergeist/browser.rb +36 -4
- data/lib/capybara/poltergeist/client.rb +4 -4
- data/lib/capybara/poltergeist/client/agent.coffee +30 -134
- data/lib/capybara/poltergeist/client/browser.coffee +97 -19
- data/lib/capybara/poltergeist/client/compiled/agent.js +36 -146
- data/lib/capybara/poltergeist/client/compiled/browser.js +114 -19
- data/lib/capybara/poltergeist/client/compiled/node.js +11 -5
- data/lib/capybara/poltergeist/client/compiled/web_page.js +80 -16
- data/lib/capybara/poltergeist/client/node.coffee +11 -6
- data/lib/capybara/poltergeist/client/web_page.coffee +62 -12
- data/lib/capybara/poltergeist/cookie.rb +35 -0
- data/lib/capybara/poltergeist/driver.rb +39 -7
- data/lib/capybara/poltergeist/inspector.rb +18 -7
- data/lib/capybara/poltergeist/server.rb +2 -2
- data/lib/capybara/poltergeist/version.rb +1 -1
- metadata +21 -5
- data/lib/capybara/poltergeist/util.rb +0 -12
@@ -1,7 +1,7 @@
|
|
1
1
|
module Capybara::Poltergeist
|
2
2
|
class Client
|
3
3
|
PHANTOMJS_SCRIPT = File.expand_path('../client/compiled/main.js', __FILE__)
|
4
|
-
PHANTOMJS_VERSION = '1.
|
4
|
+
PHANTOMJS_VERSION = '1.7.0'
|
5
5
|
PHANTOMJS_NAME = 'phantomjs'
|
6
6
|
|
7
7
|
def self.start(*args)
|
@@ -61,11 +61,11 @@ module Capybara::Poltergeist
|
|
61
61
|
def check_phantomjs_version
|
62
62
|
return if @phantomjs_version_checked
|
63
63
|
|
64
|
-
version = `#{path} --version
|
64
|
+
version = `#{path} --version` rescue nil
|
65
65
|
|
66
|
-
if $? != 0
|
66
|
+
if version.nil? || $? != 0
|
67
67
|
raise PhantomJSFailed.new($?)
|
68
|
-
elsif version < PHANTOMJS_VERSION
|
68
|
+
elsif version.chomp < PHANTOMJS_VERSION
|
69
69
|
raise PhantomJSTooOld.new(version)
|
70
70
|
end
|
71
71
|
|
@@ -4,8 +4,6 @@ class PoltergeistAgent
|
|
4
4
|
constructor: ->
|
5
5
|
@elements = []
|
6
6
|
@nodes = {}
|
7
|
-
@windows = []
|
8
|
-
this.pushWindow(window)
|
9
7
|
|
10
8
|
externalCall: (name, args) ->
|
11
9
|
try
|
@@ -14,39 +12,23 @@ class PoltergeistAgent
|
|
14
12
|
{ error: { message: error.toString(), stack: error.stack } }
|
15
13
|
|
16
14
|
@stringify: (object) ->
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
try
|
16
|
+
JSON.stringify object, (key, value) ->
|
17
|
+
if Array.isArray(this[key])
|
18
|
+
return this[key]
|
19
|
+
else
|
20
|
+
return value
|
21
|
+
catch error
|
22
|
+
if error instanceof TypeError
|
23
|
+
'"(cyclic structure)"'
|
20
24
|
else
|
21
|
-
|
22
|
-
|
23
|
-
pushWindow: (new_window) ->
|
24
|
-
@windows.push(new_window)
|
25
|
-
|
26
|
-
@window = new_window
|
27
|
-
@document = @window.document
|
28
|
-
|
29
|
-
null
|
30
|
-
|
31
|
-
popWindow: ->
|
32
|
-
@windows.pop()
|
33
|
-
|
34
|
-
@window = @windows[@windows.length - 1]
|
35
|
-
@document = @window.document
|
36
|
-
|
37
|
-
null
|
38
|
-
|
39
|
-
pushFrame: (id) ->
|
40
|
-
this.pushWindow @document.getElementById(id).contentWindow
|
41
|
-
|
42
|
-
popFrame: ->
|
43
|
-
this.popWindow()
|
25
|
+
throw error
|
44
26
|
|
45
27
|
currentUrl: ->
|
46
28
|
window.location.toString()
|
47
29
|
|
48
|
-
find: (selector, within =
|
49
|
-
results =
|
30
|
+
find: (selector, within = document) ->
|
31
|
+
results = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
|
50
32
|
ids = []
|
51
33
|
|
52
34
|
for i in [0...results.snapshotLength]
|
@@ -59,8 +41,8 @@ class PoltergeistAgent
|
|
59
41
|
@elements.length - 1
|
60
42
|
|
61
43
|
documentSize: ->
|
62
|
-
height:
|
63
|
-
width:
|
44
|
+
height: document.documentElement.scrollHeight,
|
45
|
+
width: document.documentElement.scrollWidth
|
64
46
|
|
65
47
|
get: (id) ->
|
66
48
|
@nodes[id] or= new PoltergeistAgent.Node(this, @elements[id])
|
@@ -91,7 +73,7 @@ class PoltergeistAgent.Node
|
|
91
73
|
isObsolete: ->
|
92
74
|
obsolete = (element) =>
|
93
75
|
if element.parentNode?
|
94
|
-
if element.parentNode ==
|
76
|
+
if element.parentNode == document
|
95
77
|
false
|
96
78
|
else
|
97
79
|
obsolete element.parentNode
|
@@ -104,51 +86,15 @@ class PoltergeistAgent.Node
|
|
104
86
|
event.initEvent('change', true, false)
|
105
87
|
@element.dispatchEvent(event)
|
106
88
|
|
107
|
-
input: ->
|
108
|
-
event = document.createEvent('HTMLEvents')
|
109
|
-
event.initEvent('input', true, false)
|
110
|
-
@element.dispatchEvent(event)
|
111
|
-
|
112
|
-
keyupdowned: (eventName, keyCode) ->
|
113
|
-
event = document.createEvent('UIEvents')
|
114
|
-
event.initEvent(eventName, true, true)
|
115
|
-
event.keyCode = keyCode
|
116
|
-
event.which = keyCode
|
117
|
-
event.charCode = 0
|
118
|
-
@element.dispatchEvent(event)
|
119
|
-
|
120
|
-
keypressed: (altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) ->
|
121
|
-
event = document.createEvent('UIEvents')
|
122
|
-
event.initEvent('keypress', true, true)
|
123
|
-
event.window = @agent.window
|
124
|
-
event.altKey = altKey
|
125
|
-
event.ctrlKey = ctrlKey
|
126
|
-
event.shiftKey = shiftKey
|
127
|
-
event.metaKey = metaKey
|
128
|
-
event.keyCode = keyCode
|
129
|
-
event.charCode = charCode
|
130
|
-
event.which = keyCode
|
131
|
-
@element.dispatchEvent(event)
|
132
|
-
|
133
89
|
insideBody: ->
|
134
|
-
@element ==
|
135
|
-
|
90
|
+
@element == document.body ||
|
91
|
+
document.evaluate('ancestor::body', @element, null, XPathResult.BOOLEAN_TYPE, null).booleanValue
|
136
92
|
|
137
93
|
text: ->
|
138
|
-
|
139
|
-
|
140
|
-
if this.insideBody()
|
141
|
-
el = @element
|
94
|
+
if @element.tagName == 'TEXTAREA'
|
95
|
+
@element.textContent
|
142
96
|
else
|
143
|
-
|
144
|
-
|
145
|
-
results = @agent.document.evaluate('.//text()[not(ancestor::script)]', el, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
|
146
|
-
text = ''
|
147
|
-
|
148
|
-
for i in [0...results.snapshotLength]
|
149
|
-
node = results.snapshotItem(i)
|
150
|
-
text += node.textContent if this.isVisible(node.parentNode)
|
151
|
-
text
|
97
|
+
@element.innerText
|
152
98
|
|
153
99
|
getAttribute: (name) ->
|
154
100
|
if name == 'checked' || name == 'selected'
|
@@ -165,25 +111,6 @@ class PoltergeistAgent.Node
|
|
165
111
|
else
|
166
112
|
@element.value
|
167
113
|
|
168
|
-
set: (value) ->
|
169
|
-
if (@element.maxLength >= 0)
|
170
|
-
value = value.substr(0, @element.maxLength)
|
171
|
-
|
172
|
-
@element.value = ''
|
173
|
-
this.trigger('focus')
|
174
|
-
|
175
|
-
for char in value
|
176
|
-
@element.value += char
|
177
|
-
|
178
|
-
keyCode = this.characterToKeyCode(char)
|
179
|
-
this.keyupdowned('keydown', keyCode)
|
180
|
-
this.keypressed(false, false, false, false, char.charCodeAt(0), char.charCodeAt(0))
|
181
|
-
this.keyupdowned('keyup', keyCode)
|
182
|
-
|
183
|
-
this.changed()
|
184
|
-
this.input()
|
185
|
-
this.trigger('blur')
|
186
|
-
|
187
114
|
isMultiple: ->
|
188
115
|
@element.multiple
|
189
116
|
|
@@ -207,7 +134,7 @@ class PoltergeistAgent.Node
|
|
207
134
|
isVisible: (element) ->
|
208
135
|
element = @element unless element
|
209
136
|
|
210
|
-
if
|
137
|
+
if window.getComputedStyle(element).display == 'none'
|
211
138
|
false
|
212
139
|
else if element.parentElement
|
213
140
|
this.isVisible element.parentElement
|
@@ -216,6 +143,7 @@ class PoltergeistAgent.Node
|
|
216
143
|
|
217
144
|
position: ->
|
218
145
|
rect = @element.getClientRects()[0]
|
146
|
+
throw new PoltergeistAgent.ObsoleteNode unless rect
|
219
147
|
|
220
148
|
{
|
221
149
|
top: rect.top,
|
@@ -230,7 +158,7 @@ class PoltergeistAgent.Node
|
|
230
158
|
if Node.EVENTS.MOUSE.indexOf(name) != -1
|
231
159
|
event = document.createEvent('MouseEvent')
|
232
160
|
event.initMouseEvent(
|
233
|
-
name, true, true,
|
161
|
+
name, true, true, window, 0, 0, 0, 0, 0,
|
234
162
|
false, false, false, false, 0, null
|
235
163
|
)
|
236
164
|
else if Node.EVENTS.FOCUS.indexOf(name) != -1
|
@@ -241,6 +169,13 @@ class PoltergeistAgent.Node
|
|
241
169
|
|
242
170
|
@element.dispatchEvent(event)
|
243
171
|
|
172
|
+
focusAndHighlight: ->
|
173
|
+
@element.focus()
|
174
|
+
@element.select()
|
175
|
+
|
176
|
+
blur: ->
|
177
|
+
@element.blur()
|
178
|
+
|
244
179
|
clickTest: (x, y) ->
|
245
180
|
el = origEl = document.elementFromPoint(x, y)
|
246
181
|
|
@@ -260,45 +195,6 @@ class PoltergeistAgent.Node
|
|
260
195
|
selector += ".#{className}"
|
261
196
|
selector
|
262
197
|
|
263
|
-
characterToKeyCode: (character) ->
|
264
|
-
code = character.toUpperCase().charCodeAt(0)
|
265
|
-
specialKeys =
|
266
|
-
96: 192 #`
|
267
|
-
45: 189 #-
|
268
|
-
61: 187 #=
|
269
|
-
91: 219 #[
|
270
|
-
93: 221 #]
|
271
|
-
92: 220 #\
|
272
|
-
59: 186 #;
|
273
|
-
39: 222 #'
|
274
|
-
44: 188 #,
|
275
|
-
46: 190 #.
|
276
|
-
47: 191 #/
|
277
|
-
127: 46 #delete
|
278
|
-
126: 192 #~
|
279
|
-
33: 49 #!
|
280
|
-
64: 50 #@
|
281
|
-
35: 51 ##
|
282
|
-
36: 52 #$
|
283
|
-
37: 53 #%
|
284
|
-
94: 54 #^
|
285
|
-
38: 55 #&
|
286
|
-
42: 56 #*
|
287
|
-
40: 57 #(
|
288
|
-
41: 48 #)
|
289
|
-
95: 189 #_
|
290
|
-
43: 187 #+
|
291
|
-
123: 219 #{
|
292
|
-
125: 221 #}
|
293
|
-
124: 220 #|
|
294
|
-
58: 186 #:
|
295
|
-
34: 222 #"
|
296
|
-
60: 188 #<
|
297
|
-
62: 190 #>
|
298
|
-
63: 191 #?
|
299
|
-
|
300
|
-
specialKeys[code] || code
|
301
|
-
|
302
198
|
isDOMEqual: (other_id) ->
|
303
199
|
@element == @agent.get(other_id).element
|
304
200
|
|
@@ -1,15 +1,20 @@
|
|
1
1
|
class Poltergeist.Browser
|
2
2
|
constructor: (@owner, width, height) ->
|
3
|
-
@width
|
4
|
-
@height
|
5
|
-
@state
|
6
|
-
@
|
3
|
+
@width = width || 1024
|
4
|
+
@height = height || 768
|
5
|
+
@state = 'default'
|
6
|
+
@page_stack = []
|
7
|
+
@page_id = 0
|
7
8
|
|
8
9
|
this.resetPage()
|
9
10
|
|
10
11
|
resetPage: ->
|
11
|
-
|
12
|
-
|
12
|
+
if @page?
|
13
|
+
@page.release()
|
14
|
+
phantom.clearCookies()
|
15
|
+
|
16
|
+
@page = new Poltergeist.WebPage
|
17
|
+
@page.setViewportSize(width: @width, height: @height)
|
13
18
|
|
14
19
|
@page.onLoadStarted = =>
|
15
20
|
@state = 'loading' if @state == 'clicked'
|
@@ -19,18 +24,32 @@ class Poltergeist.Browser
|
|
19
24
|
|
20
25
|
@page.onLoadFinished = (status) =>
|
21
26
|
if @state == 'loading'
|
22
|
-
this.sendResponse(status)
|
27
|
+
this.sendResponse(status: status, click: @last_click)
|
28
|
+
@state = 'default'
|
29
|
+
else if @state == 'awaiting_frame_load'
|
30
|
+
this.sendResponse(true)
|
23
31
|
@state = 'default'
|
24
32
|
|
25
33
|
@page.onInitialized = =>
|
26
34
|
@page_id += 1
|
27
35
|
|
36
|
+
@page.onPageCreated = (sub_page) =>
|
37
|
+
if @state == 'awaiting_sub_page'
|
38
|
+
name = @page_name
|
39
|
+
@state = 'default'
|
40
|
+
@page_name = null
|
41
|
+
|
42
|
+
# At this point subpage isn't fully initialized, so we can't check
|
43
|
+
# its name. Instead, we just schedule another attempt to push the
|
44
|
+
# window.
|
45
|
+
setTimeout((=> this.push_window(name)), 0)
|
46
|
+
|
28
47
|
sendResponse: (response) ->
|
29
48
|
errors = @page.errors()
|
30
49
|
|
31
50
|
if errors.length > 0
|
32
51
|
@page.clearErrors()
|
33
|
-
|
52
|
+
@owner.sendError(new Poltergeist.JavascriptError(errors))
|
34
53
|
else
|
35
54
|
@owner.sendResponse(response)
|
36
55
|
|
@@ -40,9 +59,16 @@ class Poltergeist.Browser
|
|
40
59
|
else
|
41
60
|
throw new Poltergeist.ObsoleteNode
|
42
61
|
|
43
|
-
visit: (url
|
44
|
-
@state
|
45
|
-
@page.
|
62
|
+
visit: (url) ->
|
63
|
+
@state = 'loading'
|
64
|
+
prev_url = @page.currentUrl()
|
65
|
+
|
66
|
+
@page.open(url)
|
67
|
+
|
68
|
+
if /#/.test(url) && prev_url.split('#')[0] == url.split('#')[0]
|
69
|
+
# hashchange occurred, so there will be no onLoadFinished
|
70
|
+
@state = 'default'
|
71
|
+
this.sendResponse 'success'
|
46
72
|
|
47
73
|
current_url: ->
|
48
74
|
this.sendResponse @page.currentUrl()
|
@@ -103,12 +129,40 @@ class Poltergeist.Browser
|
|
103
129
|
@page.execute("function() { #{script} }")
|
104
130
|
this.sendResponse(true)
|
105
131
|
|
106
|
-
push_frame: (
|
107
|
-
@page.pushFrame(
|
108
|
-
|
132
|
+
push_frame: (name) ->
|
133
|
+
if @page.pushFrame(name)
|
134
|
+
if @page.currentUrl() == 'about:blank'
|
135
|
+
@state = 'awaiting_frame_load'
|
136
|
+
else
|
137
|
+
this.sendResponse(true)
|
138
|
+
else
|
139
|
+
# There's currently no PhantomJS callback available for frame creation,
|
140
|
+
# so we have to poll
|
141
|
+
setTimeout((=> this.push_frame(name)), 50)
|
109
142
|
|
110
143
|
pop_frame: ->
|
111
|
-
@page.popFrame()
|
144
|
+
this.sendResponse(@page.popFrame())
|
145
|
+
|
146
|
+
push_window: (name) ->
|
147
|
+
sub_page = @page.getPage(name)
|
148
|
+
|
149
|
+
if sub_page
|
150
|
+
if sub_page.currentUrl() == 'about:blank'
|
151
|
+
sub_page.onLoadFinished = =>
|
152
|
+
sub_page.onLoadFinished = null
|
153
|
+
this.push_window(name)
|
154
|
+
else
|
155
|
+
@page_stack.push(@page)
|
156
|
+
@page = sub_page
|
157
|
+
@page_id += 1
|
158
|
+
this.sendResponse(true)
|
159
|
+
else
|
160
|
+
@page_name = name
|
161
|
+
@state = 'awaiting_sub_page'
|
162
|
+
|
163
|
+
pop_window: ->
|
164
|
+
prev_page = @page_stack.pop()
|
165
|
+
@page = prev_page if prev_page
|
112
166
|
this.sendResponse(true)
|
113
167
|
|
114
168
|
click: (page_id, id) ->
|
@@ -119,11 +173,13 @@ class Poltergeist.Browser
|
|
119
173
|
# state and wait for onLoadFinished before sending a response.
|
120
174
|
@state = 'clicked'
|
121
175
|
|
122
|
-
node.click()
|
176
|
+
@last_click = node.click()
|
123
177
|
|
124
|
-
|
125
|
-
@state
|
126
|
-
|
178
|
+
setTimeout =>
|
179
|
+
if @state != 'loading'
|
180
|
+
@state = 'default'
|
181
|
+
this.sendResponse(@last_click)
|
182
|
+
, 5
|
127
183
|
|
128
184
|
drag: (page_id, id, other_id) ->
|
129
185
|
this.node(page_id, id).dragTo this.node(page_id, other_id)
|
@@ -163,6 +219,28 @@ class Poltergeist.Browser
|
|
163
219
|
network_traffic: ->
|
164
220
|
this.sendResponse(@page.networkTraffic())
|
165
221
|
|
222
|
+
set_headers: (headers) ->
|
223
|
+
# Workaround for https://code.google.com/p/phantomjs/issues/detail?id=745
|
224
|
+
@page.setUserAgent(headers['User-Agent']) if headers['User-Agent']
|
225
|
+
@page.setCustomHeaders(headers)
|
226
|
+
this.sendResponse(true)
|
227
|
+
|
228
|
+
response_headers: ->
|
229
|
+
this.sendResponse(@page.responseHeaders())
|
230
|
+
|
231
|
+
cookies: ->
|
232
|
+
this.sendResponse(@page.cookies())
|
233
|
+
|
234
|
+
# We're using phantom.addCookie so that cookies can be set
|
235
|
+
# before the first page load has taken place.
|
236
|
+
set_cookie: (cookie) ->
|
237
|
+
phantom.addCookie(cookie)
|
238
|
+
this.sendResponse(true)
|
239
|
+
|
240
|
+
remove_cookie: (name) ->
|
241
|
+
@page.deleteCookie(name)
|
242
|
+
this.sendResponse(true)
|
243
|
+
|
166
244
|
exit: ->
|
167
245
|
phantom.exit()
|
168
246
|
|
@@ -5,8 +5,6 @@ PoltergeistAgent = (function() {
|
|
5
5
|
function PoltergeistAgent() {
|
6
6
|
this.elements = [];
|
7
7
|
this.nodes = {};
|
8
|
-
this.windows = [];
|
9
|
-
this.pushWindow(window);
|
10
8
|
}
|
11
9
|
|
12
10
|
PoltergeistAgent.prototype.externalCall = function(name, args) {
|
@@ -25,35 +23,21 @@ PoltergeistAgent = (function() {
|
|
25
23
|
};
|
26
24
|
|
27
25
|
PoltergeistAgent.stringify = function(object) {
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
try {
|
27
|
+
return JSON.stringify(object, function(key, value) {
|
28
|
+
if (Array.isArray(this[key])) {
|
29
|
+
return this[key];
|
30
|
+
} else {
|
31
|
+
return value;
|
32
|
+
}
|
33
|
+
});
|
34
|
+
} catch (error) {
|
35
|
+
if (error instanceof TypeError) {
|
36
|
+
return '"(cyclic structure)"';
|
31
37
|
} else {
|
32
|
-
|
38
|
+
throw error;
|
33
39
|
}
|
34
|
-
}
|
35
|
-
};
|
36
|
-
|
37
|
-
PoltergeistAgent.prototype.pushWindow = function(new_window) {
|
38
|
-
this.windows.push(new_window);
|
39
|
-
this.window = new_window;
|
40
|
-
this.document = this.window.document;
|
41
|
-
return null;
|
42
|
-
};
|
43
|
-
|
44
|
-
PoltergeistAgent.prototype.popWindow = function() {
|
45
|
-
this.windows.pop();
|
46
|
-
this.window = this.windows[this.windows.length - 1];
|
47
|
-
this.document = this.window.document;
|
48
|
-
return null;
|
49
|
-
};
|
50
|
-
|
51
|
-
PoltergeistAgent.prototype.pushFrame = function(id) {
|
52
|
-
return this.pushWindow(this.document.getElementById(id).contentWindow);
|
53
|
-
};
|
54
|
-
|
55
|
-
PoltergeistAgent.prototype.popFrame = function() {
|
56
|
-
return this.popWindow();
|
40
|
+
}
|
57
41
|
};
|
58
42
|
|
59
43
|
PoltergeistAgent.prototype.currentUrl = function() {
|
@@ -63,9 +47,9 @@ PoltergeistAgent = (function() {
|
|
63
47
|
PoltergeistAgent.prototype.find = function(selector, within) {
|
64
48
|
var i, ids, results, _i, _ref;
|
65
49
|
if (within == null) {
|
66
|
-
within =
|
50
|
+
within = document;
|
67
51
|
}
|
68
|
-
results =
|
52
|
+
results = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
69
53
|
ids = [];
|
70
54
|
for (i = _i = 0, _ref = results.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
71
55
|
ids.push(this.register(results.snapshotItem(i)));
|
@@ -80,8 +64,8 @@ PoltergeistAgent = (function() {
|
|
80
64
|
|
81
65
|
PoltergeistAgent.prototype.documentSize = function() {
|
82
66
|
return {
|
83
|
-
height:
|
84
|
-
width:
|
67
|
+
height: document.documentElement.scrollHeight,
|
68
|
+
width: document.documentElement.scrollWidth
|
85
69
|
};
|
86
70
|
};
|
87
71
|
|
@@ -140,7 +124,7 @@ PoltergeistAgent.Node = (function() {
|
|
140
124
|
_this = this;
|
141
125
|
obsolete = function(element) {
|
142
126
|
if (element.parentNode != null) {
|
143
|
-
if (element.parentNode ===
|
127
|
+
if (element.parentNode === document) {
|
144
128
|
return false;
|
145
129
|
} else {
|
146
130
|
return obsolete(element.parentNode);
|
@@ -159,61 +143,16 @@ PoltergeistAgent.Node = (function() {
|
|
159
143
|
return this.element.dispatchEvent(event);
|
160
144
|
};
|
161
145
|
|
162
|
-
Node.prototype.input = function() {
|
163
|
-
var event;
|
164
|
-
event = document.createEvent('HTMLEvents');
|
165
|
-
event.initEvent('input', true, false);
|
166
|
-
return this.element.dispatchEvent(event);
|
167
|
-
};
|
168
|
-
|
169
|
-
Node.prototype.keyupdowned = function(eventName, keyCode) {
|
170
|
-
var event;
|
171
|
-
event = document.createEvent('UIEvents');
|
172
|
-
event.initEvent(eventName, true, true);
|
173
|
-
event.keyCode = keyCode;
|
174
|
-
event.which = keyCode;
|
175
|
-
event.charCode = 0;
|
176
|
-
return this.element.dispatchEvent(event);
|
177
|
-
};
|
178
|
-
|
179
|
-
Node.prototype.keypressed = function(altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
|
180
|
-
var event;
|
181
|
-
event = document.createEvent('UIEvents');
|
182
|
-
event.initEvent('keypress', true, true);
|
183
|
-
event.window = this.agent.window;
|
184
|
-
event.altKey = altKey;
|
185
|
-
event.ctrlKey = ctrlKey;
|
186
|
-
event.shiftKey = shiftKey;
|
187
|
-
event.metaKey = metaKey;
|
188
|
-
event.keyCode = keyCode;
|
189
|
-
event.charCode = charCode;
|
190
|
-
event.which = keyCode;
|
191
|
-
return this.element.dispatchEvent(event);
|
192
|
-
};
|
193
|
-
|
194
146
|
Node.prototype.insideBody = function() {
|
195
|
-
return this.element ===
|
147
|
+
return this.element === document.body || document.evaluate('ancestor::body', this.element, null, XPathResult.BOOLEAN_TYPE, null).booleanValue;
|
196
148
|
};
|
197
149
|
|
198
150
|
Node.prototype.text = function() {
|
199
|
-
|
200
|
-
|
201
|
-
return '';
|
202
|
-
}
|
203
|
-
if (this.insideBody()) {
|
204
|
-
el = this.element;
|
151
|
+
if (this.element.tagName === 'TEXTAREA') {
|
152
|
+
return this.element.textContent;
|
205
153
|
} else {
|
206
|
-
|
154
|
+
return this.element.innerText;
|
207
155
|
}
|
208
|
-
results = this.agent.document.evaluate('.//text()[not(ancestor::script)]', el, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
209
|
-
text = '';
|
210
|
-
for (i = _i = 0, _ref = results.snapshotLength; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
211
|
-
node = results.snapshotItem(i);
|
212
|
-
if (this.isVisible(node.parentNode)) {
|
213
|
-
text += node.textContent;
|
214
|
-
}
|
215
|
-
}
|
216
|
-
return text;
|
217
156
|
};
|
218
157
|
|
219
158
|
Node.prototype.getAttribute = function(name) {
|
@@ -245,26 +184,6 @@ PoltergeistAgent.Node = (function() {
|
|
245
184
|
}
|
246
185
|
};
|
247
186
|
|
248
|
-
Node.prototype.set = function(value) {
|
249
|
-
var char, keyCode, _i, _len;
|
250
|
-
if (this.element.maxLength >= 0) {
|
251
|
-
value = value.substr(0, this.element.maxLength);
|
252
|
-
}
|
253
|
-
this.element.value = '';
|
254
|
-
this.trigger('focus');
|
255
|
-
for (_i = 0, _len = value.length; _i < _len; _i++) {
|
256
|
-
char = value[_i];
|
257
|
-
this.element.value += char;
|
258
|
-
keyCode = this.characterToKeyCode(char);
|
259
|
-
this.keyupdowned('keydown', keyCode);
|
260
|
-
this.keypressed(false, false, false, false, char.charCodeAt(0), char.charCodeAt(0));
|
261
|
-
this.keyupdowned('keyup', keyCode);
|
262
|
-
}
|
263
|
-
this.changed();
|
264
|
-
this.input();
|
265
|
-
return this.trigger('blur');
|
266
|
-
};
|
267
|
-
|
268
187
|
Node.prototype.isMultiple = function() {
|
269
188
|
return this.element.multiple;
|
270
189
|
};
|
@@ -295,7 +214,7 @@ PoltergeistAgent.Node = (function() {
|
|
295
214
|
if (!element) {
|
296
215
|
element = this.element;
|
297
216
|
}
|
298
|
-
if (
|
217
|
+
if (window.getComputedStyle(element).display === 'none') {
|
299
218
|
return false;
|
300
219
|
} else if (element.parentElement) {
|
301
220
|
return this.isVisible(element.parentElement);
|
@@ -307,6 +226,9 @@ PoltergeistAgent.Node = (function() {
|
|
307
226
|
Node.prototype.position = function() {
|
308
227
|
var rect;
|
309
228
|
rect = this.element.getClientRects()[0];
|
229
|
+
if (!rect) {
|
230
|
+
throw new PoltergeistAgent.ObsoleteNode;
|
231
|
+
}
|
310
232
|
return {
|
311
233
|
top: rect.top,
|
312
234
|
right: rect.right,
|
@@ -321,7 +243,7 @@ PoltergeistAgent.Node = (function() {
|
|
321
243
|
var event;
|
322
244
|
if (Node.EVENTS.MOUSE.indexOf(name) !== -1) {
|
323
245
|
event = document.createEvent('MouseEvent');
|
324
|
-
event.initMouseEvent(name, true, true,
|
246
|
+
event.initMouseEvent(name, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
325
247
|
} else if (Node.EVENTS.FOCUS.indexOf(name) !== -1) {
|
326
248
|
event = document.createEvent('HTMLEvents');
|
327
249
|
event.initEvent(name, true, true);
|
@@ -331,6 +253,15 @@ PoltergeistAgent.Node = (function() {
|
|
331
253
|
return this.element.dispatchEvent(event);
|
332
254
|
};
|
333
255
|
|
256
|
+
Node.prototype.focusAndHighlight = function() {
|
257
|
+
this.element.focus();
|
258
|
+
return this.element.select();
|
259
|
+
};
|
260
|
+
|
261
|
+
Node.prototype.blur = function() {
|
262
|
+
return this.element.blur();
|
263
|
+
};
|
264
|
+
|
334
265
|
Node.prototype.clickTest = function(x, y) {
|
335
266
|
var el, origEl;
|
336
267
|
el = origEl = document.elementFromPoint(x, y);
|
@@ -364,47 +295,6 @@ PoltergeistAgent.Node = (function() {
|
|
364
295
|
return selector;
|
365
296
|
};
|
366
297
|
|
367
|
-
Node.prototype.characterToKeyCode = function(character) {
|
368
|
-
var code, specialKeys;
|
369
|
-
code = character.toUpperCase().charCodeAt(0);
|
370
|
-
specialKeys = {
|
371
|
-
96: 192,
|
372
|
-
45: 189,
|
373
|
-
61: 187,
|
374
|
-
91: 219,
|
375
|
-
93: 221,
|
376
|
-
92: 220,
|
377
|
-
59: 186,
|
378
|
-
39: 222,
|
379
|
-
44: 188,
|
380
|
-
46: 190,
|
381
|
-
47: 191,
|
382
|
-
127: 46,
|
383
|
-
126: 192,
|
384
|
-
33: 49,
|
385
|
-
64: 50,
|
386
|
-
35: 51,
|
387
|
-
36: 52,
|
388
|
-
37: 53,
|
389
|
-
94: 54,
|
390
|
-
38: 55,
|
391
|
-
42: 56,
|
392
|
-
40: 57,
|
393
|
-
41: 48,
|
394
|
-
95: 189,
|
395
|
-
43: 187,
|
396
|
-
123: 219,
|
397
|
-
125: 221,
|
398
|
-
124: 220,
|
399
|
-
58: 186,
|
400
|
-
34: 222,
|
401
|
-
60: 188,
|
402
|
-
62: 190,
|
403
|
-
63: 191
|
404
|
-
};
|
405
|
-
return specialKeys[code] || code;
|
406
|
-
};
|
407
|
-
|
408
298
|
Node.prototype.isDOMEqual = function(other_id) {
|
409
299
|
return this.element === this.agent.get(other_id).element;
|
410
300
|
};
|