poltergeist 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +77 -40
- data/lib/capybara/poltergeist.rb +11 -11
- data/lib/capybara/poltergeist/browser.rb +35 -12
- data/lib/capybara/poltergeist/client.rb +9 -12
- data/lib/capybara/poltergeist/client/agent.coffee +99 -6
- data/lib/capybara/poltergeist/client/browser.coffee +57 -79
- data/lib/capybara/poltergeist/client/compiled/agent.js +179 -19
- data/lib/capybara/poltergeist/client/compiled/browser.js +109 -81
- data/lib/capybara/poltergeist/client/compiled/connection.js +8 -1
- data/lib/capybara/poltergeist/client/compiled/main.js +75 -34
- data/lib/capybara/poltergeist/client/compiled/node.js +32 -39
- data/lib/capybara/poltergeist/client/compiled/web_page.js +118 -41
- data/lib/capybara/poltergeist/client/main.coffee +11 -23
- data/lib/capybara/poltergeist/client/node.coffee +16 -33
- data/lib/capybara/poltergeist/client/web_page.coffee +57 -26
- data/lib/capybara/poltergeist/driver.rb +36 -6
- data/lib/capybara/poltergeist/errors.rb +4 -15
- data/lib/capybara/poltergeist/json.rb +25 -0
- data/lib/capybara/poltergeist/network_traffic.rb +6 -0
- data/lib/capybara/poltergeist/network_traffic/request.rb +26 -0
- data/lib/capybara/poltergeist/network_traffic/response.rb +40 -0
- data/lib/capybara/poltergeist/node.rb +10 -6
- data/lib/capybara/poltergeist/version.rb +1 -1
- data/lib/capybara/poltergeist/web_socket_server.rb +3 -0
- metadata +112 -23
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Poltergeist - A PhantomJS driver for Capybara #
|
2
2
|
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
|
5
5
|
[![Build Status](https://secure.travis-ci.org/jonleighton/poltergeist.png)](http://travis-ci.org/jonleighton/poltergeist)
|
6
6
|
[![Dependency Status](https://gemnasium.com/jonleighton/poltergeist.png)](https://gemnasium.com/jonleighton/poltergeist)
|
@@ -18,47 +18,31 @@ require 'capybara/poltergeist'
|
|
18
18
|
Capybara.javascript_driver = :poltergeist
|
19
19
|
```
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
`Transfer-Encoding: chunked`
|
26
|
-
([commit](https://github.com/rack/rack/commit/50cdd0bf000a9ffb3eb3760fda2ff3e1ad18f3a7)).
|
27
|
-
This has been observed to cause problems,
|
28
|
-
probably due to race conditions in Qt's HTTP handling code, so you are
|
29
|
-
recommended to avoid this by specifying your own server setup for
|
30
|
-
Capybara:
|
31
|
-
|
32
|
-
``` ruby
|
33
|
-
Capybara.server do |app, port|
|
34
|
-
require 'rack/handler/thin'
|
35
|
-
Thin::Logging.silent = true
|
36
|
-
Thin::Server.new('0.0.0.0', port, app).start
|
37
|
-
end
|
38
|
-
```
|
39
|
-
|
40
|
-
If you're using Rails 3.0, this affects you. If you're using Rails 3.1+,
|
41
|
-
this doesn't affect you.
|
21
|
+
If you were previously using the `:rack_test` driver, be aware that
|
22
|
+
your app will now run in a separate thread and this can have
|
23
|
+
consequences for transactional tests. [See the Capybara README for more
|
24
|
+
detail](https://github.com/jnicklas/capybara/blob/master/README.md#transactions-and-database-setup).
|
42
25
|
|
43
26
|
## Installing PhantomJS ##
|
44
27
|
|
45
|
-
You need PhantomJS 1.
|
46
|
-
need Qt, or
|
28
|
+
You need at least PhantomJS 1.6.0, but 1.6.1 is recommended as there some issues with the former.
|
29
|
+
There are *no other external dependencies* (you don't need Qt, or a running X
|
30
|
+
server, etc.)
|
47
31
|
|
48
32
|
### Mac ###
|
49
33
|
|
50
|
-
* *
|
51
|
-
* *
|
34
|
+
* *Manual install*: [Download this](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.6.1-macosx-static.zip&can=2&q=)
|
35
|
+
* *Homebrew*: `brew install phantomjs`
|
52
36
|
|
53
37
|
### Linux ###
|
54
38
|
|
55
39
|
* Download the [32
|
56
|
-
bit](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.
|
40
|
+
bit](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.6.1-linux-i686-dynamic.tar.bz2&can=2&q=)
|
57
41
|
or [64
|
58
|
-
bit](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.
|
42
|
+
bit](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.6.1-linux-x86_64-dynamic.tar.bz2&can=2&q=)
|
59
43
|
binary.
|
60
|
-
* Extract it: `sudo tar
|
61
|
-
* Link it: `sudo ln -s /usr/local/phantomjs
|
44
|
+
* Extract it: `sudo tar xvjf phantomjs-1.6.1-linux-*-dynamic.tar.gz -C /usr/local`
|
45
|
+
* Link it: `sudo ln -s /usr/local/phantomjs-1.6.1-linux*/bin/phantomjs /usr/local/bin/phantomjs`
|
62
46
|
|
63
47
|
(Note that you cannot copy the `/usr/local/phantomjs/bin/phantomjs`
|
64
48
|
binary elsewhere on its own as it dynamically links with other files in
|
@@ -69,7 +53,7 @@ binary elsewhere on its own as it dynamically links with other files in
|
|
69
53
|
Do this as a last resort if the binaries don't work for you. It will
|
70
54
|
take quite a long time as it has to build WebKit.
|
71
55
|
|
72
|
-
* Download [the source tarball](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.
|
56
|
+
* Download [the source tarball](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.6.1-source.zip&can=2&q=)
|
73
57
|
* Extract and cd in
|
74
58
|
* `./build.sh`
|
75
59
|
|
@@ -79,7 +63,7 @@ Supported: MRI 1.8.7, MRI 1.9.2, MRI 1.9.3, JRuby 1.8, JRuby 1.9.
|
|
79
63
|
|
80
64
|
Not supported:
|
81
65
|
|
82
|
-
* Rubinius
|
66
|
+
* Rubinius
|
83
67
|
* Windows
|
84
68
|
|
85
69
|
Contributions are welcome in order to move 'unsupported'
|
@@ -90,11 +74,11 @@ items into the 'supported' list.
|
|
90
74
|
There are no special steps to take. You don't need Xvfb or any running X
|
91
75
|
server at all.
|
92
76
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
77
|
+
Depending on your tests, one thing that you may need is some fonts. If
|
78
|
+
you're getting errors on a CI that don't occur during development then
|
79
|
+
try taking some screenshots - it may well be missing fonts throwing
|
80
|
+
things off kilter. Your distro will have various font packages available
|
81
|
+
to install.
|
98
82
|
|
99
83
|
## What's supported? ##
|
100
84
|
|
@@ -126,9 +110,28 @@ When this option is enabled, you can insert `page.driver.debug` into
|
|
126
110
|
your tests to pause the test and launch a browser which gives you the
|
127
111
|
WebKit inspector to view your test run with.
|
128
112
|
|
129
|
-
|
130
|
-
|
131
|
-
|
113
|
+
[Read more
|
114
|
+
here](http://jonathanleighton.com/articles/2012/poltergeist-0-6-0/)
|
115
|
+
|
116
|
+
### Setting request headers ###
|
117
|
+
|
118
|
+
Additional HTTP request headers can be set like so:
|
119
|
+
|
120
|
+
``` ruby
|
121
|
+
page.driver.headers = {
|
122
|
+
"Cookie" => "foo=bar",
|
123
|
+
"Host" => "foo.com"
|
124
|
+
}
|
125
|
+
```
|
126
|
+
|
127
|
+
They will be cleared between tests, so you do not have to do this manually.
|
128
|
+
|
129
|
+
### Inspecting network traffic ###
|
130
|
+
|
131
|
+
You can inspect the network traffic (i.e. what resources have been
|
132
|
+
loaded) on the current page by calling `page.driver.network_traffic`.
|
133
|
+
This returns an array of request objects. A request object has a
|
134
|
+
`response_parts` method containing data about the response chunks.
|
132
135
|
|
133
136
|
## Customization ##
|
134
137
|
|
@@ -150,6 +153,11 @@ end
|
|
150
153
|
when communicating with PhantomJS. `nil` means wait forever. Default
|
151
154
|
is 30.
|
152
155
|
* `:inspector` (Boolean, String) - See 'Remote Debugging', above.
|
156
|
+
* `:js_errors` (Boolean) - When false, Javascript errors do not get re-raised in Ruby.
|
157
|
+
* `:window_size` (Array) - The dimensions of the browser window in which to test, expressed
|
158
|
+
as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
|
159
|
+
* `:phantomjs_options` (Array) - Additional [command line options](http://code.google.com/p/phantomjs/wiki/Interface#Command-line_Options)
|
160
|
+
to be passed to PhantomJS, e.g. `['--load-images=no', '--ignore-ssl-errors=yes']`
|
153
161
|
|
154
162
|
## Bugs ##
|
155
163
|
|
@@ -173,6 +181,35 @@ makes debugging easier). Running `rake autocompile` will watch the
|
|
173
181
|
|
174
182
|
## Changes ##
|
175
183
|
|
184
|
+
### 0.7.0 ###
|
185
|
+
|
186
|
+
#### Features ####
|
187
|
+
|
188
|
+
* Added an option `:js_errors`, allowing poltergeist to continue
|
189
|
+
running after JS errors. (John Griffin & Tom Stuart) [Issue #62] [Issue #69]
|
190
|
+
* Added an option `:window_size`, allowing users to specify
|
191
|
+
dimensions to which the browser window will be resized.
|
192
|
+
(Tom Stuart) [Issue #53]
|
193
|
+
* Capybara 1.0 is no longer supported. Capybara ~> 1.1 is required.
|
194
|
+
* Added ability to set arbitrary http request headers
|
195
|
+
* Inspect network traffic on the page via
|
196
|
+
`page.driver.network_traffic` (Doug McInnes) [Issue #77]
|
197
|
+
* Added an option `:phantomjs_options`, allowing users to specify
|
198
|
+
additional command-line options passed to phantomjs executable.
|
199
|
+
(wynst) [Issue #97]
|
200
|
+
* Scroll element into viewport if needed on click (Gabriel Sobrinho)
|
201
|
+
[Issue #83]
|
202
|
+
* Added status code support. (Dmitriy Nesteryuk and Jon Leighton) [Issue #37]
|
203
|
+
|
204
|
+
#### Bug fixes ###
|
205
|
+
|
206
|
+
* Fix issue with `ClickFailed` exception happening with a negative
|
207
|
+
co-ordinate (which should be impossible). (Jon Leighton, Gabriel
|
208
|
+
Sobrinho, Tom Stuart) [Issue #60]
|
209
|
+
* Fix issue with `undefined method map for "[]":String`, which
|
210
|
+
happened when dealing with pages that include JS rewriting
|
211
|
+
Array.prototype.toJSON. (Tom Stuart) [Issue #63]
|
212
|
+
|
176
213
|
### 0.6.0 ###
|
177
214
|
|
178
215
|
#### Features ####
|
data/lib/capybara/poltergeist.rb
CHANGED
@@ -2,17 +2,17 @@ require 'capybara'
|
|
2
2
|
|
3
3
|
module Capybara
|
4
4
|
module Poltergeist
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
require 'capybara/poltergeist/driver'
|
6
|
+
require 'capybara/poltergeist/browser'
|
7
|
+
require 'capybara/poltergeist/node'
|
8
|
+
require 'capybara/poltergeist/server'
|
9
|
+
require 'capybara/poltergeist/web_socket_server'
|
10
|
+
require 'capybara/poltergeist/client'
|
11
|
+
require 'capybara/poltergeist/util'
|
12
|
+
require 'capybara/poltergeist/inspector'
|
13
|
+
require 'capybara/poltergeist/spawn'
|
14
|
+
require 'capybara/poltergeist/json'
|
15
|
+
require 'capybara/poltergeist/network_traffic'
|
16
16
|
require 'capybara/poltergeist/errors'
|
17
17
|
end
|
18
18
|
end
|
@@ -2,12 +2,13 @@ require 'multi_json'
|
|
2
2
|
|
3
3
|
module Capybara::Poltergeist
|
4
4
|
class Browser
|
5
|
-
attr_reader :server, :client, :logger
|
5
|
+
attr_reader :server, :client, :logger, :js_errors
|
6
6
|
|
7
|
-
def initialize(server, client, logger = nil)
|
8
|
-
@server
|
9
|
-
@client
|
10
|
-
@logger
|
7
|
+
def initialize(server, client, logger = nil, js_errors = true)
|
8
|
+
@server = server
|
9
|
+
@client = client
|
10
|
+
@logger = logger
|
11
|
+
@js_errors = js_errors
|
11
12
|
end
|
12
13
|
|
13
14
|
def restart
|
@@ -15,14 +16,18 @@ module Capybara::Poltergeist
|
|
15
16
|
client.restart
|
16
17
|
end
|
17
18
|
|
18
|
-
def visit(url)
|
19
|
-
command 'visit', url
|
19
|
+
def visit(url, headers)
|
20
|
+
command 'visit', url, headers
|
20
21
|
end
|
21
22
|
|
22
23
|
def current_url
|
23
24
|
command 'current_url'
|
24
25
|
end
|
25
26
|
|
27
|
+
def status_code
|
28
|
+
command 'status_code'
|
29
|
+
end
|
30
|
+
|
26
31
|
def body
|
27
32
|
command 'body'
|
28
33
|
end
|
@@ -79,6 +84,7 @@ module Capybara::Poltergeist
|
|
79
84
|
def within_frame(id, &block)
|
80
85
|
command 'push_frame', id
|
81
86
|
yield
|
87
|
+
ensure
|
82
88
|
command 'pop_frame'
|
83
89
|
end
|
84
90
|
|
@@ -103,29 +109,46 @@ module Capybara::Poltergeist
|
|
103
109
|
end
|
104
110
|
|
105
111
|
def render(path, options = {})
|
106
|
-
command 'render', path, !!options[:full]
|
112
|
+
command 'render', path.to_s, !!options[:full]
|
107
113
|
end
|
108
114
|
|
109
115
|
def resize(width, height)
|
110
116
|
command 'resize', width, height
|
111
117
|
end
|
112
118
|
|
119
|
+
def network_traffic
|
120
|
+
command('network_traffic').values.map do |event|
|
121
|
+
NetworkTraffic::Request.new(
|
122
|
+
event['request'],
|
123
|
+
event['responseParts'].map { |response| NetworkTraffic::Response.new(response) }
|
124
|
+
)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def equals(page_id, id, other_id)
|
129
|
+
command('equals', page_id, id, other_id)
|
130
|
+
end
|
131
|
+
|
113
132
|
def command(name, *args)
|
114
133
|
message = { 'name' => name, 'args' => args }
|
115
134
|
log message.inspect
|
116
135
|
|
117
|
-
json =
|
136
|
+
json = JSON.load(server.send(JSON.dump(message)))
|
118
137
|
log json.inspect
|
119
138
|
|
120
139
|
if json['error']
|
121
140
|
if json['error']['name'] == 'Poltergeist.JavascriptError'
|
122
|
-
|
141
|
+
error = JavascriptError.new(json['error'])
|
142
|
+
if js_errors
|
143
|
+
raise error
|
144
|
+
else
|
145
|
+
log error
|
146
|
+
end
|
123
147
|
else
|
124
148
|
raise BrowserError.new(json['error'])
|
125
149
|
end
|
126
|
-
else
|
127
|
-
json['response']
|
128
150
|
end
|
151
|
+
json['response']
|
129
152
|
|
130
153
|
rescue DeadClient
|
131
154
|
restart
|
@@ -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.6.0'
|
5
5
|
PHANTOMJS_NAME = 'phantomjs'
|
6
6
|
|
7
7
|
def self.start(*args)
|
@@ -10,12 +10,13 @@ module Capybara::Poltergeist
|
|
10
10
|
client
|
11
11
|
end
|
12
12
|
|
13
|
-
attr_reader :pid, :port, :path, :
|
13
|
+
attr_reader :pid, :port, :path, :window_size, :phantomjs_options
|
14
14
|
|
15
|
-
def initialize(port,
|
16
|
-
@port
|
17
|
-
@
|
18
|
-
@
|
15
|
+
def initialize(port, options = {})
|
16
|
+
@port = port
|
17
|
+
@path = options[:path] || PHANTOMJS_NAME
|
18
|
+
@window_size = options[:window_size] || [1024, 768]
|
19
|
+
@phantomjs_options = options[:phantomjs_options] || []
|
19
20
|
|
20
21
|
pid = Process.pid
|
21
22
|
at_exit { stop if Process.pid == pid }
|
@@ -47,14 +48,10 @@ module Capybara::Poltergeist
|
|
47
48
|
def command
|
48
49
|
@command ||= begin
|
49
50
|
parts = [path]
|
50
|
-
|
51
|
-
if inspector
|
52
|
-
parts << "--remote-debugger-port=#{inspector.port}"
|
53
|
-
parts << "--remote-debugger-autorun=yes"
|
54
|
-
end
|
55
|
-
|
51
|
+
parts.concat phantomjs_options
|
56
52
|
parts << PHANTOMJS_SCRIPT
|
57
53
|
parts << port
|
54
|
+
parts.concat window_size
|
58
55
|
parts
|
59
56
|
end
|
60
57
|
end
|
@@ -7,8 +7,18 @@ class PoltergeistAgent
|
|
7
7
|
@windows = []
|
8
8
|
this.pushWindow(window)
|
9
9
|
|
10
|
-
externalCall: (name,
|
11
|
-
|
10
|
+
externalCall: (name, args) ->
|
11
|
+
try
|
12
|
+
{ value: this[name].apply(this, args) }
|
13
|
+
catch error
|
14
|
+
{ error: { message: error.toString(), stack: error.stack } }
|
15
|
+
|
16
|
+
@stringify: (object) ->
|
17
|
+
JSON.stringify object, (key, value) ->
|
18
|
+
if Array.isArray(this[key])
|
19
|
+
return this[key]
|
20
|
+
else
|
21
|
+
return value
|
12
22
|
|
13
23
|
pushWindow: (new_window) ->
|
14
24
|
@windows.push(new_window)
|
@@ -55,10 +65,10 @@ class PoltergeistAgent
|
|
55
65
|
get: (id) ->
|
56
66
|
@nodes[id] or= new PoltergeistAgent.Node(this, @elements[id])
|
57
67
|
|
58
|
-
nodeCall: (id, name,
|
68
|
+
nodeCall: (id, name, args) ->
|
59
69
|
node = this.get(id)
|
60
70
|
throw new PoltergeistAgent.ObsoleteNode if node.isObsolete()
|
61
|
-
node[name].apply(node,
|
71
|
+
node[name].apply(node, args)
|
62
72
|
|
63
73
|
class PoltergeistAgent.ObsoleteNode
|
64
74
|
toString: -> "PoltergeistAgent.ObsoleteNode"
|
@@ -91,7 +101,33 @@ class PoltergeistAgent.Node
|
|
91
101
|
|
92
102
|
changed: ->
|
93
103
|
event = document.createEvent('HTMLEvents')
|
94
|
-
event.initEvent(
|
104
|
+
event.initEvent('change', true, false)
|
105
|
+
@element.dispatchEvent(event)
|
106
|
+
|
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
|
95
131
|
@element.dispatchEvent(event)
|
96
132
|
|
97
133
|
insideBody: ->
|
@@ -120,6 +156,9 @@ class PoltergeistAgent.Node
|
|
120
156
|
else
|
121
157
|
@element.getAttribute(name)
|
122
158
|
|
159
|
+
scrollIntoView: ->
|
160
|
+
@element.scrollIntoViewIfNeeded()
|
161
|
+
|
123
162
|
value: ->
|
124
163
|
if @element.tagName == 'SELECT' && @element.multiple
|
125
164
|
option.value for option in @element.children when option.selected
|
@@ -130,8 +169,20 @@ class PoltergeistAgent.Node
|
|
130
169
|
if (@element.maxLength >= 0)
|
131
170
|
value = value.substr(0, @element.maxLength)
|
132
171
|
|
133
|
-
@element.value =
|
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
|
+
|
134
183
|
this.changed()
|
184
|
+
this.input()
|
185
|
+
this.trigger('blur')
|
135
186
|
|
136
187
|
isMultiple: ->
|
137
188
|
@element.multiple
|
@@ -209,6 +260,48 @@ class PoltergeistAgent.Node
|
|
209
260
|
selector += ".#{className}"
|
210
261
|
selector
|
211
262
|
|
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
|
+
isDOMEqual: (other_id) ->
|
303
|
+
@element == @agent.get(other_id).element
|
304
|
+
|
212
305
|
window.__poltergeist = new PoltergeistAgent
|
213
306
|
|
214
307
|
document.addEventListener(
|