poltergeist 0.1.0
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.
- data/CHANGELOG.md +0 -0
- data/LICENSE +20 -0
- data/README.md +85 -0
- data/lib/capybara/poltergeist/browser.rb +130 -0
- data/lib/capybara/poltergeist/client/agent.coffee +175 -0
- data/lib/capybara/poltergeist/client/browser.coffee +121 -0
- data/lib/capybara/poltergeist/client/compiled/agent.js +198 -0
- data/lib/capybara/poltergeist/client/compiled/browser.js +117 -0
- data/lib/capybara/poltergeist/client/compiled/connection.js +20 -0
- data/lib/capybara/poltergeist/client/compiled/main.js +38 -0
- data/lib/capybara/poltergeist/client/compiled/node.js +74 -0
- data/lib/capybara/poltergeist/client/compiled/web_page.js +136 -0
- data/lib/capybara/poltergeist/client/connection.coffee +11 -0
- data/lib/capybara/poltergeist/client/main.coffee +27 -0
- data/lib/capybara/poltergeist/client/node.coffee +56 -0
- data/lib/capybara/poltergeist/client/web_page.coffee +102 -0
- data/lib/capybara/poltergeist/client.rb +57 -0
- data/lib/capybara/poltergeist/driver.rb +85 -0
- data/lib/capybara/poltergeist/errors.rb +26 -0
- data/lib/capybara/poltergeist/node.rb +92 -0
- data/lib/capybara/poltergeist/server.rb +35 -0
- data/lib/capybara/poltergeist/server_manager.rb +118 -0
- data/lib/capybara/poltergeist/version.rb +5 -0
- data/lib/capybara/poltergeist.rb +20 -0
- metadata +102 -0
@@ -0,0 +1,198 @@
|
|
1
|
+
var PoltergeistAgent;
|
2
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
3
|
+
PoltergeistAgent = (function() {
|
4
|
+
function PoltergeistAgent() {
|
5
|
+
this.elements = [];
|
6
|
+
this.nodes = {};
|
7
|
+
this.windows = [];
|
8
|
+
this.pushWindow(window);
|
9
|
+
}
|
10
|
+
PoltergeistAgent.prototype.pushWindow = function(new_window) {
|
11
|
+
this.windows.push(new_window);
|
12
|
+
this.window = new_window;
|
13
|
+
return this.document = this.window.document;
|
14
|
+
};
|
15
|
+
PoltergeistAgent.prototype.popWindow = function() {
|
16
|
+
this.windows.pop();
|
17
|
+
this.window = this.windows[this.windows.length - 1];
|
18
|
+
return this.document = this.window.document;
|
19
|
+
};
|
20
|
+
PoltergeistAgent.prototype.pushFrame = function(id) {
|
21
|
+
return this.pushWindow(this.document.getElementById(id).contentWindow);
|
22
|
+
};
|
23
|
+
PoltergeistAgent.prototype.popFrame = function() {
|
24
|
+
return this.popWindow();
|
25
|
+
};
|
26
|
+
PoltergeistAgent.prototype.currentUrl = function() {
|
27
|
+
return window.location.toString();
|
28
|
+
};
|
29
|
+
PoltergeistAgent.prototype.find = function(selector, id) {
|
30
|
+
var context, i, ids, results, _ref;
|
31
|
+
context = id != null ? this.elements[id] : this.document;
|
32
|
+
results = this.document.evaluate(selector, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
33
|
+
ids = [];
|
34
|
+
for (i = 0, _ref = results.snapshotLength; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) {
|
35
|
+
ids.push(this.register(results.snapshotItem(i)));
|
36
|
+
}
|
37
|
+
return ids;
|
38
|
+
};
|
39
|
+
PoltergeistAgent.prototype.register = function(element) {
|
40
|
+
this.elements.push(element);
|
41
|
+
return this.elements.length - 1;
|
42
|
+
};
|
43
|
+
PoltergeistAgent.prototype.documentSize = function() {
|
44
|
+
return {
|
45
|
+
height: this.document.documentElement.scrollHeight,
|
46
|
+
width: this.document.documentElement.scrollWidth
|
47
|
+
};
|
48
|
+
};
|
49
|
+
PoltergeistAgent.prototype.get = function(id) {
|
50
|
+
var _base;
|
51
|
+
return (_base = this.nodes)[id] || (_base[id] = new PoltergeistAgent.Node(this, this.elements[id]));
|
52
|
+
};
|
53
|
+
PoltergeistAgent.prototype.nodeCall = function(id, name, arguments) {
|
54
|
+
var node;
|
55
|
+
node = this.get(id);
|
56
|
+
return node[name].apply(node, arguments);
|
57
|
+
};
|
58
|
+
return PoltergeistAgent;
|
59
|
+
})();
|
60
|
+
PoltergeistAgent.Node = (function() {
|
61
|
+
Node.EVENTS = {
|
62
|
+
FOCUS: ['blur', 'focus', 'focusin', 'focusout'],
|
63
|
+
MOUSE: ['click', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseover', 'mouseout', 'mouseup']
|
64
|
+
};
|
65
|
+
function Node(agent, element) {
|
66
|
+
this.agent = agent;
|
67
|
+
this.element = element;
|
68
|
+
}
|
69
|
+
Node.prototype.parentId = function() {
|
70
|
+
return this.agent.register(this.element.parentNode);
|
71
|
+
};
|
72
|
+
Node.prototype.isObsolete = function() {
|
73
|
+
var obsolete;
|
74
|
+
obsolete = __bind(function(element) {
|
75
|
+
if (element.parentNode != null) {
|
76
|
+
if (element.parentNode === this.agent.document) {
|
77
|
+
return false;
|
78
|
+
} else {
|
79
|
+
return obsolete(element.parentNode);
|
80
|
+
}
|
81
|
+
} else {
|
82
|
+
return true;
|
83
|
+
}
|
84
|
+
}, this);
|
85
|
+
return obsolete(this.element);
|
86
|
+
};
|
87
|
+
Node.prototype.changed = function() {
|
88
|
+
var event;
|
89
|
+
event = document.createEvent('HTMLEvents');
|
90
|
+
event.initEvent("change", true, false);
|
91
|
+
return this.element.dispatchEvent(event);
|
92
|
+
};
|
93
|
+
Node.prototype.text = function() {
|
94
|
+
return this.element.textContent;
|
95
|
+
};
|
96
|
+
Node.prototype.getAttribute = function(name) {
|
97
|
+
if (name === 'checked' || name === 'selected') {
|
98
|
+
return this.element[name];
|
99
|
+
} else {
|
100
|
+
return this.element.getAttribute(name);
|
101
|
+
}
|
102
|
+
};
|
103
|
+
Node.prototype.value = function() {
|
104
|
+
var option, _i, _len, _ref, _results;
|
105
|
+
if (this.element.tagName === 'SELECT' && this.element.multiple) {
|
106
|
+
_ref = this.element.children;
|
107
|
+
_results = [];
|
108
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
109
|
+
option = _ref[_i];
|
110
|
+
if (option.selected) {
|
111
|
+
_results.push(option.value);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
return _results;
|
115
|
+
} else {
|
116
|
+
return this.element.value;
|
117
|
+
}
|
118
|
+
};
|
119
|
+
Node.prototype.set = function(value) {
|
120
|
+
if (this.element.maxLength >= 0) {
|
121
|
+
value = value.substr(0, this.element.maxLength);
|
122
|
+
}
|
123
|
+
this.element.value = value;
|
124
|
+
return this.changed();
|
125
|
+
};
|
126
|
+
Node.prototype.isMultiple = function() {
|
127
|
+
return this.element.multiple;
|
128
|
+
};
|
129
|
+
Node.prototype.setAttribute = function(name, value) {
|
130
|
+
return this.element.setAttribute(name, value);
|
131
|
+
};
|
132
|
+
Node.prototype.removeAttribute = function(name) {
|
133
|
+
return this.element.removeAttribute(name);
|
134
|
+
};
|
135
|
+
Node.prototype.select = function(value) {
|
136
|
+
if (value === false && !this.element.parentNode.multiple) {
|
137
|
+
return false;
|
138
|
+
} else {
|
139
|
+
this.element.selected = value;
|
140
|
+
this.changed();
|
141
|
+
return true;
|
142
|
+
}
|
143
|
+
};
|
144
|
+
Node.prototype.tagName = function() {
|
145
|
+
return this.element.tagName;
|
146
|
+
};
|
147
|
+
Node.prototype.elementVisible = function(element) {};
|
148
|
+
Node.prototype.isVisible = function(id) {
|
149
|
+
var visible;
|
150
|
+
visible = function(element) {
|
151
|
+
if (this.window.getComputedStyle(element).display === 'none') {
|
152
|
+
return false;
|
153
|
+
} else if (element.parentElement) {
|
154
|
+
return visible(element.parentElement);
|
155
|
+
} else {
|
156
|
+
return true;
|
157
|
+
}
|
158
|
+
};
|
159
|
+
return visible(this.element);
|
160
|
+
};
|
161
|
+
Node.prototype.position = function(id) {
|
162
|
+
var pos;
|
163
|
+
pos = function(element) {
|
164
|
+
var parentPos, x, y;
|
165
|
+
x = element.offsetLeft;
|
166
|
+
y = element.offsetTop;
|
167
|
+
if (element.offsetParent) {
|
168
|
+
parentPos = pos(element.offsetParent);
|
169
|
+
x += parentPos.x;
|
170
|
+
y += parentPos.y;
|
171
|
+
}
|
172
|
+
return {
|
173
|
+
x: x,
|
174
|
+
y: y
|
175
|
+
};
|
176
|
+
};
|
177
|
+
return pos(this.element);
|
178
|
+
};
|
179
|
+
Node.prototype.trigger = function(name) {
|
180
|
+
var event;
|
181
|
+
if (Node.EVENTS.MOUSE.indexOf(name) !== -1) {
|
182
|
+
event = document.createEvent('MouseEvent');
|
183
|
+
event.initMouseEvent(name, true, true, this.agent.window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
184
|
+
} else if (Node.EVENTS.FOCUS.indexOf(name) !== -1) {
|
185
|
+
event = document.createEvent('HTMLEvents');
|
186
|
+
event.initEvent(name, true, true);
|
187
|
+
} else {
|
188
|
+
throw "Unknown event";
|
189
|
+
}
|
190
|
+
return this.element.dispatchEvent(event);
|
191
|
+
};
|
192
|
+
return Node;
|
193
|
+
})();
|
194
|
+
window.__poltergeist = new PoltergeistAgent;
|
195
|
+
document.addEventListener('DOMContentLoaded', function() {
|
196
|
+
return console.log('__DOMContentLoaded');
|
197
|
+
});
|
198
|
+
true;
|
@@ -0,0 +1,117 @@
|
|
1
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
2
|
+
Poltergeist.Browser = (function() {
|
3
|
+
function Browser(owner) {
|
4
|
+
this.owner = owner;
|
5
|
+
this.awaiting_response = false;
|
6
|
+
this.resetPage();
|
7
|
+
}
|
8
|
+
Browser.prototype.resetPage = function() {
|
9
|
+
if (this.page != null) {
|
10
|
+
this.page.release();
|
11
|
+
}
|
12
|
+
this.page = new Poltergeist.WebPage;
|
13
|
+
return this.page.onLoadFinished = __bind(function(status) {
|
14
|
+
if (this.awaiting_response) {
|
15
|
+
this.owner.sendResponse(status);
|
16
|
+
return this.awaiting_response = false;
|
17
|
+
}
|
18
|
+
}, this);
|
19
|
+
};
|
20
|
+
Browser.prototype.visit = function(url) {
|
21
|
+
this.awaiting_response = true;
|
22
|
+
return this.page.open(url);
|
23
|
+
};
|
24
|
+
Browser.prototype.current_url = function() {
|
25
|
+
return this.owner.sendResponse(this.page.currentUrl());
|
26
|
+
};
|
27
|
+
Browser.prototype.body = function() {
|
28
|
+
return this.owner.sendResponse(this.page.content());
|
29
|
+
};
|
30
|
+
Browser.prototype.source = function() {
|
31
|
+
return this.owner.sendResponse(this.page.source());
|
32
|
+
};
|
33
|
+
Browser.prototype.find = function(selector, id) {
|
34
|
+
return this.owner.sendResponse(this.page.find(selector, id));
|
35
|
+
};
|
36
|
+
Browser.prototype.text = function(id) {
|
37
|
+
return this.owner.sendResponse(this.page.get(id).text());
|
38
|
+
};
|
39
|
+
Browser.prototype.attribute = function(id, name) {
|
40
|
+
return this.owner.sendResponse(this.page.get(id).getAttribute(name));
|
41
|
+
};
|
42
|
+
Browser.prototype.value = function(id) {
|
43
|
+
return this.owner.sendResponse(this.page.get(id).value());
|
44
|
+
};
|
45
|
+
Browser.prototype.set = function(id, value) {
|
46
|
+
this.page.get(id).set(value);
|
47
|
+
return this.owner.sendResponse(true);
|
48
|
+
};
|
49
|
+
Browser.prototype.select_file = function(id, value) {
|
50
|
+
var element, multiple;
|
51
|
+
element = this.page.get(id);
|
52
|
+
multiple = element.isMultiple();
|
53
|
+
if (multiple) {
|
54
|
+
element.removeAttribute('multiple');
|
55
|
+
}
|
56
|
+
element.setAttribute('_poltergeist_selected', '');
|
57
|
+
this.page.uploadFile('[_poltergeist_selected]', value);
|
58
|
+
element.removeAttribute('_poltergeist_selected');
|
59
|
+
if (multiple) {
|
60
|
+
element.setAttribute('multiple', 'multiple');
|
61
|
+
}
|
62
|
+
return this.owner.sendResponse(true);
|
63
|
+
};
|
64
|
+
Browser.prototype.select = function(id, value) {
|
65
|
+
return this.owner.sendResponse(this.page.get(id).select(value));
|
66
|
+
};
|
67
|
+
Browser.prototype.tag_name = function(id) {
|
68
|
+
return this.owner.sendResponse(this.page.get(id).tagName());
|
69
|
+
};
|
70
|
+
Browser.prototype.visible = function(id) {
|
71
|
+
return this.owner.sendResponse(this.page.get(id).isVisible());
|
72
|
+
};
|
73
|
+
Browser.prototype.evaluate = function(script) {
|
74
|
+
return this.owner.sendResponse(this.page.evaluate("function() { return " + script + " }"));
|
75
|
+
};
|
76
|
+
Browser.prototype.execute = function(script) {
|
77
|
+
this.page.execute("function() { return " + script + " }");
|
78
|
+
return this.owner.sendResponse(true);
|
79
|
+
};
|
80
|
+
Browser.prototype.push_frame = function(id) {
|
81
|
+
this.page.pushFrame(id);
|
82
|
+
return this.owner.sendResponse(true);
|
83
|
+
};
|
84
|
+
Browser.prototype.pop_frame = function() {
|
85
|
+
this.page.popFrame();
|
86
|
+
return this.owner.sendResponse(true);
|
87
|
+
};
|
88
|
+
Browser.prototype.click = function(id) {
|
89
|
+
this.page.onLoadStarted = __bind(function() {
|
90
|
+
return this.awaiting_response = true;
|
91
|
+
}, this);
|
92
|
+
this.page.get(id).click();
|
93
|
+
return setTimeout(__bind(function() {
|
94
|
+
this.page.onLoadStarted = null;
|
95
|
+
if (!this.awaiting_response) {
|
96
|
+
return this.owner.sendResponse(true);
|
97
|
+
}
|
98
|
+
}, this), 0);
|
99
|
+
};
|
100
|
+
Browser.prototype.drag = function(id, other_id) {
|
101
|
+
this.page.get(id).dragTo(this.page.get(other_id));
|
102
|
+
return this.owner.sendResponse(true);
|
103
|
+
};
|
104
|
+
Browser.prototype.trigger = function(id, event) {
|
105
|
+
this.page.get(id).trigger(event);
|
106
|
+
return this.owner.sendResponse(event);
|
107
|
+
};
|
108
|
+
Browser.prototype.reset = function() {
|
109
|
+
this.resetPage();
|
110
|
+
return this.owner.sendResponse(true);
|
111
|
+
};
|
112
|
+
Browser.prototype.render = function(path) {
|
113
|
+
this.page.render(path);
|
114
|
+
return this.owner.sendResponse(true);
|
115
|
+
};
|
116
|
+
return Browser;
|
117
|
+
})();
|
@@ -0,0 +1,20 @@
|
|
1
|
+
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
2
|
+
Poltergeist.Connection = (function() {
|
3
|
+
function Connection(owner, port) {
|
4
|
+
this.owner = owner;
|
5
|
+
this.port = port;
|
6
|
+
this.commandReceived = __bind(this.commandReceived, this);
|
7
|
+
this.socket = new WebSocket("ws://127.0.0.1:" + this.port + "/");
|
8
|
+
this.socket.onmessage = this.commandReceived;
|
9
|
+
this.socket.onclose = function() {
|
10
|
+
return phantom.exit();
|
11
|
+
};
|
12
|
+
}
|
13
|
+
Connection.prototype.commandReceived = function(message) {
|
14
|
+
return this.owner.runCommand(JSON.parse(message.data));
|
15
|
+
};
|
16
|
+
Connection.prototype.send = function(message) {
|
17
|
+
return this.socket.send(JSON.stringify(message));
|
18
|
+
};
|
19
|
+
return Connection;
|
20
|
+
})();
|
@@ -0,0 +1,38 @@
|
|
1
|
+
var Poltergeist;
|
2
|
+
if (phantom.version.major < 1 || phantom.version.minor < 3) {
|
3
|
+
console.log("Poltergeist requires a PhantomJS version of at least 1.3");
|
4
|
+
phantom.exit(1);
|
5
|
+
}
|
6
|
+
Poltergeist = (function() {
|
7
|
+
function Poltergeist(port) {
|
8
|
+
this.browser = new Poltergeist.Browser(this);
|
9
|
+
this.connection = new Poltergeist.Connection(this, port);
|
10
|
+
}
|
11
|
+
Poltergeist.prototype.runCommand = function(command) {
|
12
|
+
try {
|
13
|
+
return this.browser[command.name].apply(this.browser, command.args);
|
14
|
+
} catch (error) {
|
15
|
+
return this.connection.send({
|
16
|
+
error: error.toString()
|
17
|
+
});
|
18
|
+
}
|
19
|
+
};
|
20
|
+
Poltergeist.prototype.sendResponse = function(response) {
|
21
|
+
return this.connection.send({
|
22
|
+
response: response
|
23
|
+
});
|
24
|
+
};
|
25
|
+
return Poltergeist;
|
26
|
+
})();
|
27
|
+
Poltergeist.ObsoleteNode = (function() {
|
28
|
+
function ObsoleteNode() {}
|
29
|
+
ObsoleteNode.prototype.toString = function() {
|
30
|
+
return "Poltergeist.ObsoleteNode";
|
31
|
+
};
|
32
|
+
return ObsoleteNode;
|
33
|
+
})();
|
34
|
+
phantom.injectJs('web_page.js');
|
35
|
+
phantom.injectJs('node.js');
|
36
|
+
phantom.injectJs('connection.js');
|
37
|
+
phantom.injectJs('browser.js');
|
38
|
+
new Poltergeist(phantom.args[0]);
|
@@ -0,0 +1,74 @@
|
|
1
|
+
var __slice = Array.prototype.slice, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
2
|
+
Poltergeist.Node = (function() {
|
3
|
+
var name, _fn, _i, _len, _ref;
|
4
|
+
Node.DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'isVisible', 'position', 'trigger', 'parentId'];
|
5
|
+
function Node(page, id) {
|
6
|
+
this.page = page;
|
7
|
+
this.id = id;
|
8
|
+
}
|
9
|
+
Node.prototype.parent = function() {
|
10
|
+
return new Poltergeist.Node(this.page, this.parentId());
|
11
|
+
};
|
12
|
+
Node.prototype.isObsolete = function() {
|
13
|
+
return this.page.nodeCall(this.id, 'isObsolete');
|
14
|
+
};
|
15
|
+
_ref = Node.DELEGATES;
|
16
|
+
_fn = __bind(function(name) {
|
17
|
+
return this.prototype[name] = function() {
|
18
|
+
var arguments, _ref2;
|
19
|
+
_ref2 = arguments, arguments = 1 <= _ref2.length ? __slice.call(_ref2, 0) : [];
|
20
|
+
if (this.isObsolete()) {
|
21
|
+
throw new Poltergeist.ObsoleteNode;
|
22
|
+
} else {
|
23
|
+
return this.page.nodeCall(this.id, name, arguments);
|
24
|
+
}
|
25
|
+
};
|
26
|
+
}, Node);
|
27
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
28
|
+
name = _ref[_i];
|
29
|
+
_fn(name);
|
30
|
+
}
|
31
|
+
Node.prototype.scrollIntoView = function() {
|
32
|
+
var pos, scroll, size, viewport, _ref2, _ref3;
|
33
|
+
viewport = this.page.viewport();
|
34
|
+
size = this.page.documentSize();
|
35
|
+
pos = this.position();
|
36
|
+
scroll = {
|
37
|
+
left: viewport.left,
|
38
|
+
top: viewport.top
|
39
|
+
};
|
40
|
+
if (!((viewport.left <= (_ref2 = pos.x) && _ref2 < viewport.right))) {
|
41
|
+
scroll.left = Math.min(pos.x, size.width - viewport.width);
|
42
|
+
}
|
43
|
+
if (!((viewport.top <= (_ref3 = pos.y) && _ref3 < viewport.bottom))) {
|
44
|
+
scroll.top = Math.min(pos.y, size.height - viewport.height);
|
45
|
+
}
|
46
|
+
if (scroll.left !== viewport.left || scroll.top !== viewport.top) {
|
47
|
+
this.page.setScrollPosition(scroll);
|
48
|
+
}
|
49
|
+
return {
|
50
|
+
position: this.relativePosition(pos, scroll),
|
51
|
+
scroll: scroll
|
52
|
+
};
|
53
|
+
};
|
54
|
+
Node.prototype.relativePosition = function(position, scroll) {
|
55
|
+
return {
|
56
|
+
x: position.x - scroll.left,
|
57
|
+
y: position.y - scroll.top
|
58
|
+
};
|
59
|
+
};
|
60
|
+
Node.prototype.click = function() {
|
61
|
+
var position;
|
62
|
+
position = this.scrollIntoView().position;
|
63
|
+
return this.page.sendEvent('click', position.x, position.y);
|
64
|
+
};
|
65
|
+
Node.prototype.dragTo = function(other) {
|
66
|
+
var otherPosition, position, scroll, _ref2;
|
67
|
+
_ref2 = this.scrollIntoView(), position = _ref2.position, scroll = _ref2.scroll;
|
68
|
+
otherPosition = this.relativePosition(other.position(), scroll);
|
69
|
+
this.page.sendEvent('mousedown', position.x, position.y);
|
70
|
+
this.page.sendEvent('mousemove', otherPosition.x, otherPosition.y);
|
71
|
+
return this.page.sendEvent('mouseup', otherPosition.x, otherPosition.y);
|
72
|
+
};
|
73
|
+
return Node;
|
74
|
+
}).call(this);
|
@@ -0,0 +1,136 @@
|
|
1
|
+
var __slice = Array.prototype.slice, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
2
|
+
Poltergeist.WebPage = (function() {
|
3
|
+
var command, delegate, _fn, _fn2, _i, _j, _len, _len2, _ref, _ref2;
|
4
|
+
WebPage.CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived'];
|
5
|
+
WebPage.DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render'];
|
6
|
+
WebPage.COMMANDS = ['currentUrl', 'find', 'nodeCall', 'pushFrame', 'popFrame', 'documentSize'];
|
7
|
+
function WebPage() {
|
8
|
+
var callback, _i, _len, _ref;
|
9
|
+
this["native"] = require('webpage').create();
|
10
|
+
this.nodes = {};
|
11
|
+
this._source = "";
|
12
|
+
_ref = WebPage.CALLBACKS;
|
13
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
14
|
+
callback = _ref[_i];
|
15
|
+
this.bindCallback(callback);
|
16
|
+
}
|
17
|
+
this.injectAgent();
|
18
|
+
}
|
19
|
+
_ref = WebPage.COMMANDS;
|
20
|
+
_fn = __bind(function(command) {
|
21
|
+
return this.prototype[command] = function() {
|
22
|
+
var arguments, _ref2;
|
23
|
+
_ref2 = arguments, arguments = 1 <= _ref2.length ? __slice.call(_ref2, 0) : [];
|
24
|
+
return this.runCommand(command, arguments);
|
25
|
+
};
|
26
|
+
}, WebPage);
|
27
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
28
|
+
command = _ref[_i];
|
29
|
+
_fn(command);
|
30
|
+
}
|
31
|
+
_ref2 = WebPage.DELEGATES;
|
32
|
+
_fn2 = __bind(function(delegate) {
|
33
|
+
return this.prototype[delegate] = function() {
|
34
|
+
return this["native"][delegate].apply(this["native"], arguments);
|
35
|
+
};
|
36
|
+
}, WebPage);
|
37
|
+
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
|
38
|
+
delegate = _ref2[_j];
|
39
|
+
_fn2(delegate);
|
40
|
+
}
|
41
|
+
WebPage.prototype.onInitializedNative = function() {
|
42
|
+
this._source = null;
|
43
|
+
this.injectAgent();
|
44
|
+
return this.setScrollPosition({
|
45
|
+
left: 0,
|
46
|
+
top: 0
|
47
|
+
});
|
48
|
+
};
|
49
|
+
WebPage.prototype.injectAgent = function() {
|
50
|
+
if (this.evaluate(function() {
|
51
|
+
return typeof __poltergeist;
|
52
|
+
}) === "undefined") {
|
53
|
+
return this["native"].injectJs('agent.js');
|
54
|
+
}
|
55
|
+
};
|
56
|
+
WebPage.prototype.onConsoleMessageNative = function(message) {
|
57
|
+
if (message === '__DOMContentLoaded') {
|
58
|
+
this._source = this["native"].content;
|
59
|
+
return false;
|
60
|
+
}
|
61
|
+
};
|
62
|
+
WebPage.prototype.onLoadFinishedNative = function() {
|
63
|
+
return this._source || (this._source = this["native"].content);
|
64
|
+
};
|
65
|
+
WebPage.prototype.onConsoleMessage = function(message) {
|
66
|
+
return console.log(message);
|
67
|
+
};
|
68
|
+
WebPage.prototype.content = function() {
|
69
|
+
return this["native"].content;
|
70
|
+
};
|
71
|
+
WebPage.prototype.source = function() {
|
72
|
+
return this._source;
|
73
|
+
};
|
74
|
+
WebPage.prototype.viewportSize = function() {
|
75
|
+
return this["native"].viewportSize;
|
76
|
+
};
|
77
|
+
WebPage.prototype.scrollPosition = function() {
|
78
|
+
return this["native"].scrollPosition;
|
79
|
+
};
|
80
|
+
WebPage.prototype.setScrollPosition = function(pos) {
|
81
|
+
return this["native"].scrollPosition = pos;
|
82
|
+
};
|
83
|
+
WebPage.prototype.viewport = function() {
|
84
|
+
var scroll, size;
|
85
|
+
scroll = this.scrollPosition();
|
86
|
+
size = this.viewportSize();
|
87
|
+
return {
|
88
|
+
top: scroll.top,
|
89
|
+
bottom: scroll.top + size.height,
|
90
|
+
left: scroll.left,
|
91
|
+
right: scroll.left + size.width,
|
92
|
+
width: size.width,
|
93
|
+
height: size.height
|
94
|
+
};
|
95
|
+
};
|
96
|
+
WebPage.prototype.get = function(id) {
|
97
|
+
var _base;
|
98
|
+
return (_base = this.nodes)[id] || (_base[id] = new Poltergeist.Node(this, id));
|
99
|
+
};
|
100
|
+
WebPage.prototype.evaluate = function() {
|
101
|
+
var args, fn;
|
102
|
+
fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
103
|
+
return this["native"].evaluate("function() { return " + (this.stringifyCall(fn, args)) + " }");
|
104
|
+
};
|
105
|
+
WebPage.prototype.execute = function() {
|
106
|
+
var args, fn;
|
107
|
+
fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
108
|
+
return this["native"].evaluate("function() { " + (this.stringifyCall(fn, args)) + " }");
|
109
|
+
};
|
110
|
+
WebPage.prototype.stringifyCall = function(fn, args) {
|
111
|
+
if (args.length === 0) {
|
112
|
+
return "(" + (fn.toString()) + ")()";
|
113
|
+
} else {
|
114
|
+
return "(" + (fn.toString()) + ").apply(this, JSON.parse(" + (JSON.stringify(JSON.stringify(args))) + "))";
|
115
|
+
}
|
116
|
+
};
|
117
|
+
WebPage.prototype.bindCallback = function(name) {
|
118
|
+
var that;
|
119
|
+
that = this;
|
120
|
+
return this["native"][name] = function() {
|
121
|
+
var result;
|
122
|
+
if (that[name + 'Native'] != null) {
|
123
|
+
result = that[name + 'Native'].apply(that, arguments);
|
124
|
+
}
|
125
|
+
if (result !== false && (that[name] != null)) {
|
126
|
+
return that[name].apply(that, arguments);
|
127
|
+
}
|
128
|
+
};
|
129
|
+
};
|
130
|
+
WebPage.prototype.runCommand = function(name, arguments) {
|
131
|
+
return this.evaluate(function(name, arguments) {
|
132
|
+
return __poltergeist[name].apply(__poltergeist, arguments);
|
133
|
+
}, name, arguments);
|
134
|
+
};
|
135
|
+
return WebPage;
|
136
|
+
}).call(this);
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class Poltergeist.Connection
|
2
|
+
constructor: (@owner, @port) ->
|
3
|
+
@socket = new WebSocket "ws://127.0.0.1:#{@port}/"
|
4
|
+
@socket.onmessage = this.commandReceived
|
5
|
+
@socket.onclose = -> phantom.exit()
|
6
|
+
|
7
|
+
commandReceived: (message) =>
|
8
|
+
@owner.runCommand(JSON.parse(message.data))
|
9
|
+
|
10
|
+
send: (message) ->
|
11
|
+
@socket.send(JSON.stringify(message))
|
@@ -0,0 +1,27 @@
|
|
1
|
+
if phantom.version.major < 1 || phantom.version.minor < 3
|
2
|
+
console.log "Poltergeist requires a PhantomJS version of at least 1.3"
|
3
|
+
phantom.exit(1)
|
4
|
+
|
5
|
+
class Poltergeist
|
6
|
+
constructor: (port) ->
|
7
|
+
@browser = new Poltergeist.Browser(this)
|
8
|
+
@connection = new Poltergeist.Connection(this, port)
|
9
|
+
|
10
|
+
runCommand: (command) ->
|
11
|
+
try
|
12
|
+
@browser[command.name].apply(@browser, command.args)
|
13
|
+
catch error
|
14
|
+
@connection.send({ error: error.toString() })
|
15
|
+
|
16
|
+
sendResponse: (response) ->
|
17
|
+
@connection.send({ response: response })
|
18
|
+
|
19
|
+
class Poltergeist.ObsoleteNode
|
20
|
+
toString: -> "Poltergeist.ObsoleteNode"
|
21
|
+
|
22
|
+
phantom.injectJs('web_page.js')
|
23
|
+
phantom.injectJs('node.js')
|
24
|
+
phantom.injectJs('connection.js')
|
25
|
+
phantom.injectJs('browser.js')
|
26
|
+
|
27
|
+
new Poltergeist(phantom.args[0])
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Proxy object for forwarding method calls to the node object inside the page.
|
2
|
+
|
3
|
+
class Poltergeist.Node
|
4
|
+
@DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', 'removeAttribute',
|
5
|
+
'isMultiple', 'select', 'tagName', 'isVisible', 'position', 'trigger', 'parentId']
|
6
|
+
|
7
|
+
constructor: (@page, @id) ->
|
8
|
+
|
9
|
+
parent: ->
|
10
|
+
new Poltergeist.Node(@page, this.parentId())
|
11
|
+
|
12
|
+
isObsolete: ->
|
13
|
+
@page.nodeCall(@id, 'isObsolete')
|
14
|
+
|
15
|
+
for name in @DELEGATES
|
16
|
+
do (name) =>
|
17
|
+
this.prototype[name] = (arguments...) ->
|
18
|
+
if this.isObsolete()
|
19
|
+
throw new Poltergeist.ObsoleteNode
|
20
|
+
else
|
21
|
+
@page.nodeCall(@id, name, arguments)
|
22
|
+
|
23
|
+
scrollIntoView: ->
|
24
|
+
viewport = @page.viewport()
|
25
|
+
size = @page.documentSize()
|
26
|
+
pos = this.position()
|
27
|
+
|
28
|
+
scroll = { left: viewport.left, top: viewport.top }
|
29
|
+
|
30
|
+
unless viewport.left <= pos.x < viewport.right
|
31
|
+
scroll.left = Math.min(pos.x, size.width - viewport.width)
|
32
|
+
|
33
|
+
unless viewport.top <= pos.y < viewport.bottom
|
34
|
+
scroll.top = Math.min(pos.y, size.height - viewport.height)
|
35
|
+
|
36
|
+
if scroll.left != viewport.left || scroll.top != viewport.top
|
37
|
+
@page.setScrollPosition(scroll)
|
38
|
+
|
39
|
+
position: this.relativePosition(pos, scroll),
|
40
|
+
scroll: scroll
|
41
|
+
|
42
|
+
relativePosition: (position, scroll) ->
|
43
|
+
x: position.x - scroll.left
|
44
|
+
y: position.y - scroll.top
|
45
|
+
|
46
|
+
click: ->
|
47
|
+
position = this.scrollIntoView().position
|
48
|
+
@page.sendEvent('click', position.x, position.y)
|
49
|
+
|
50
|
+
dragTo: (other) ->
|
51
|
+
{ position, scroll } = this.scrollIntoView()
|
52
|
+
otherPosition = this.relativePosition(other.position(), scroll)
|
53
|
+
|
54
|
+
@page.sendEvent('mousedown', position.x, position.y)
|
55
|
+
@page.sendEvent('mousemove', otherPosition.x, otherPosition.y)
|
56
|
+
@page.sendEvent('mouseup', otherPosition.x, otherPosition.y)
|