phantomjs-binaries 1.8.0 → 1.8.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/phantomjs-binaries/version.rb +1 -1
- data/phantomjs-binaries.gemspec +1 -1
- metadata +3 -23
- data/bin/bootstrap.js +0 -342
- data/bin/casperjs +0 -58
- data/bin/usage.txt +0 -11
- data/modules/casper.js +0 -2101
- data/modules/cli.js +0 -138
- data/modules/clientutils.js +0 -781
- data/modules/colorizer.js +0 -130
- data/modules/events.js +0 -259
- data/modules/http.js +0 -70
- data/modules/mouse.js +0 -109
- data/modules/pagestack.js +0 -166
- data/modules/querystring.js +0 -187
- data/modules/tester.js +0 -1161
- data/modules/utils.js +0 -581
- data/modules/vendors/coffee-script.js +0 -8
- data/modules/xunit.js +0 -155
- data/package.json +0 -35
data/modules/cli.js
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
/*!
|
2
|
-
* Casper is a navigation utility for PhantomJS.
|
3
|
-
*
|
4
|
-
* Documentation: http://casperjs.org/
|
5
|
-
* Repository: http://github.com/n1k0/casperjs
|
6
|
-
*
|
7
|
-
* Copyright (c) 2011-2012 Nicolas Perriault
|
8
|
-
*
|
9
|
-
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
10
|
-
*
|
11
|
-
* Permission is hereby granted, free of charge, to any person obtaining a
|
12
|
-
* copy of this software and associated documentation files (the "Software"),
|
13
|
-
* to deal in the Software without restriction, including without limitation
|
14
|
-
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
15
|
-
* and/or sell copies of the Software, and to permit persons to whom the
|
16
|
-
* Software is furnished to do so, subject to the following conditions:
|
17
|
-
*
|
18
|
-
* The above copyright notice and this permission notice shall be included
|
19
|
-
* in all copies or substantial portions of the Software.
|
20
|
-
*
|
21
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
22
|
-
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
24
|
-
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
25
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
26
|
-
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
27
|
-
* DEALINGS IN THE SOFTWARE.
|
28
|
-
*
|
29
|
-
*/
|
30
|
-
|
31
|
-
/*global CasperError console exports phantom require*/
|
32
|
-
|
33
|
-
var system = require('system');
|
34
|
-
var utils = require('utils');
|
35
|
-
|
36
|
-
/**
|
37
|
-
* Extracts, normalize ad organize PhantomJS CLI arguments in a dedicated
|
38
|
-
* Object.
|
39
|
-
*
|
40
|
-
* @param array phantomArgs system.args value
|
41
|
-
* @return Object
|
42
|
-
*/
|
43
|
-
exports.parse = function parse(phantomArgs) {
|
44
|
-
"use strict";
|
45
|
-
var extract = {
|
46
|
-
args: [],
|
47
|
-
options: {},
|
48
|
-
raw: {
|
49
|
-
args: [],
|
50
|
-
options: {}
|
51
|
-
},
|
52
|
-
drop: function drop(what) {
|
53
|
-
if (utils.isNumber(what)) {
|
54
|
-
// deleting an arg by its position
|
55
|
-
this.args = this.args.filter(function _filter(arg, index) {
|
56
|
-
return index !== what;
|
57
|
-
});
|
58
|
-
} else if (utils.isString(what)) {
|
59
|
-
// deleting an arg by its value
|
60
|
-
this.args = this.args.filter(function _filter(arg) {
|
61
|
-
return arg !== what;
|
62
|
-
});
|
63
|
-
// deleting an option by its name (key)
|
64
|
-
var self = this;
|
65
|
-
Object.keys(this.options).forEach(function _forEach(option) {
|
66
|
-
if (option === what) {
|
67
|
-
delete self.options[what];
|
68
|
-
}
|
69
|
-
});
|
70
|
-
} else {
|
71
|
-
throw new CasperError("cannot drop argument of type " + typeof what);
|
72
|
-
}
|
73
|
-
},
|
74
|
-
has: function has(what) {
|
75
|
-
if (utils.isNumber(what)) {
|
76
|
-
return what in this.args;
|
77
|
-
} else if (utils.isString(what)) {
|
78
|
-
return what in this.options;
|
79
|
-
} else {
|
80
|
-
throw new CasperError("Unsupported cli arg tester " + typeof what);
|
81
|
-
}
|
82
|
-
},
|
83
|
-
get: function get(what) {
|
84
|
-
if (utils.isNumber(what)) {
|
85
|
-
return this.args[what];
|
86
|
-
} else if (utils.isString(what)) {
|
87
|
-
return this.options[what];
|
88
|
-
} else {
|
89
|
-
throw new CasperError("Unsupported cli arg getter " + typeof what);
|
90
|
-
}
|
91
|
-
}
|
92
|
-
};
|
93
|
-
phantomArgs.forEach(function _forEach(arg) {
|
94
|
-
if (arg.indexOf('--') === 0) {
|
95
|
-
// named option
|
96
|
-
var optionMatch = arg.match(/^--(.*?)=(.*)/i);
|
97
|
-
if (optionMatch) {
|
98
|
-
extract.options[optionMatch[1]] = castArgument(optionMatch[2]);
|
99
|
-
extract.raw.options[optionMatch[1]] = optionMatch[2];
|
100
|
-
} else {
|
101
|
-
// flag
|
102
|
-
var flagMatch = arg.match(/^--(.*)/);
|
103
|
-
if (flagMatch) {
|
104
|
-
extract.options[flagMatch[1]] = extract.raw.options[flagMatch[1]] = true;
|
105
|
-
}
|
106
|
-
}
|
107
|
-
} else {
|
108
|
-
// positional arg
|
109
|
-
extract.args.push(castArgument(arg));
|
110
|
-
extract.raw.args.push(castArgument(arg));
|
111
|
-
}
|
112
|
-
});
|
113
|
-
extract.raw = utils.mergeObjects(extract.raw, {
|
114
|
-
drop: extract.drop,
|
115
|
-
has: extract.has,
|
116
|
-
get: extract.get
|
117
|
-
});
|
118
|
-
return extract;
|
119
|
-
};
|
120
|
-
|
121
|
-
/**
|
122
|
-
* Cast a string argument to its typed equivalent.
|
123
|
-
*
|
124
|
-
* @param String arg
|
125
|
-
* @return Mixed
|
126
|
-
*/
|
127
|
-
function castArgument(arg) {
|
128
|
-
"use strict";
|
129
|
-
if (arg.match(/^-?\d+$/)) {
|
130
|
-
return parseInt(arg, 10);
|
131
|
-
} else if (arg.match(/^-?\d+\.\d+$/)) {
|
132
|
-
return parseFloat(arg);
|
133
|
-
} else if (arg.match(/^(true|false)$/i)) {
|
134
|
-
return arg.trim().toLowerCase() === "true" ? true : false;
|
135
|
-
} else {
|
136
|
-
return arg;
|
137
|
-
}
|
138
|
-
}
|
data/modules/clientutils.js
DELETED
@@ -1,781 +0,0 @@
|
|
1
|
-
/*!
|
2
|
-
* Casper is a navigation utility for PhantomJS.
|
3
|
-
*
|
4
|
-
* Documentation: http://casperjs.org/
|
5
|
-
* Repository: http://github.com/n1k0/casperjs
|
6
|
-
*
|
7
|
-
* Copyright (c) 2011-2012 Nicolas Perriault
|
8
|
-
*
|
9
|
-
* Part of source code is Copyright Joyent, Inc. and other Node contributors.
|
10
|
-
*
|
11
|
-
* Permission is hereby granted, free of charge, to any person obtaining a
|
12
|
-
* copy of this software and associated documentation files (the "Software"),
|
13
|
-
* to deal in the Software without restriction, including without limitation
|
14
|
-
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
15
|
-
* and/or sell copies of the Software, and to permit persons to whom the
|
16
|
-
* Software is furnished to do so, subject to the following conditions:
|
17
|
-
*
|
18
|
-
* The above copyright notice and this permission notice shall be included
|
19
|
-
* in all copies or substantial portions of the Software.
|
20
|
-
*
|
21
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
22
|
-
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
23
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
24
|
-
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
25
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
26
|
-
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
27
|
-
* DEALINGS IN THE SOFTWARE.
|
28
|
-
*
|
29
|
-
*/
|
30
|
-
|
31
|
-
/*global console escape exports NodeList window*/
|
32
|
-
|
33
|
-
(function(exports) {
|
34
|
-
"use strict";
|
35
|
-
|
36
|
-
exports.create = function create(options) {
|
37
|
-
return new this.ClientUtils(options);
|
38
|
-
};
|
39
|
-
|
40
|
-
/**
|
41
|
-
* Casper client-side helpers.
|
42
|
-
*/
|
43
|
-
exports.ClientUtils = function ClientUtils(options) {
|
44
|
-
/*jshint maxstatements:40*/
|
45
|
-
// private members
|
46
|
-
var BASE64_ENCODE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
47
|
-
var BASE64_DECODE_CHARS = new Array(
|
48
|
-
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
49
|
-
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
50
|
-
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
51
|
-
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
52
|
-
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
53
|
-
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
54
|
-
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
55
|
-
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
|
56
|
-
);
|
57
|
-
var SUPPORTED_SELECTOR_TYPES = ['css', 'xpath'];
|
58
|
-
|
59
|
-
// public members
|
60
|
-
this.options = options || {};
|
61
|
-
this.options.scope = this.options.scope || document;
|
62
|
-
/**
|
63
|
-
* Clicks on the DOM element behind the provided selector.
|
64
|
-
*
|
65
|
-
* @param String selector A CSS3 selector to the element to click
|
66
|
-
* @return Boolean
|
67
|
-
*/
|
68
|
-
this.click = function click(selector) {
|
69
|
-
return this.mouseEvent('click', selector);
|
70
|
-
};
|
71
|
-
|
72
|
-
/**
|
73
|
-
* Decodes a base64 encoded string. Succeeds where window.atob() fails.
|
74
|
-
*
|
75
|
-
* @param String str The base64 encoded contents
|
76
|
-
* @return string
|
77
|
-
*/
|
78
|
-
this.decode = function decode(str) {
|
79
|
-
/*jshint maxstatements:30 maxcomplexity:30 */
|
80
|
-
var c1, c2, c3, c4, i = 0, len = str.length, out = "";
|
81
|
-
while (i < len) {
|
82
|
-
do {
|
83
|
-
c1 = BASE64_DECODE_CHARS[str.charCodeAt(i++) & 0xff];
|
84
|
-
} while (i < len && c1 === -1);
|
85
|
-
if (c1 === -1) {
|
86
|
-
break;
|
87
|
-
}
|
88
|
-
do {
|
89
|
-
c2 = BASE64_DECODE_CHARS[str.charCodeAt(i++) & 0xff];
|
90
|
-
} while (i < len && c2 === -1);
|
91
|
-
if (c2 === -1) {
|
92
|
-
break;
|
93
|
-
}
|
94
|
-
out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
|
95
|
-
do {
|
96
|
-
c3 = str.charCodeAt(i++) & 0xff;
|
97
|
-
if (c3 === 61)
|
98
|
-
return out;
|
99
|
-
c3 = BASE64_DECODE_CHARS[c3];
|
100
|
-
} while (i < len && c3 === -1);
|
101
|
-
if (c3 === -1) {
|
102
|
-
break;
|
103
|
-
}
|
104
|
-
out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
|
105
|
-
do {
|
106
|
-
c4 = str.charCodeAt(i++) & 0xff;
|
107
|
-
if (c4 === 61) {
|
108
|
-
return out;
|
109
|
-
}
|
110
|
-
c4 = BASE64_DECODE_CHARS[c4];
|
111
|
-
} while (i < len && c4 === -1);
|
112
|
-
if (c4 === -1) {
|
113
|
-
break;
|
114
|
-
}
|
115
|
-
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
|
116
|
-
}
|
117
|
-
return out;
|
118
|
-
};
|
119
|
-
|
120
|
-
/**
|
121
|
-
* Echoes something to casper console.
|
122
|
-
*
|
123
|
-
* @param String message
|
124
|
-
* @return
|
125
|
-
*/
|
126
|
-
this.echo = function echo(message) {
|
127
|
-
console.log("[casper.echo] " + message);
|
128
|
-
};
|
129
|
-
|
130
|
-
/**
|
131
|
-
* Base64 encodes a string, even binary ones. Succeeds where
|
132
|
-
* window.btoa() fails.
|
133
|
-
*
|
134
|
-
* @param String str The string content to encode
|
135
|
-
* @return string
|
136
|
-
*/
|
137
|
-
this.encode = function encode(str) {
|
138
|
-
/*jshint maxstatements:30 */
|
139
|
-
var out = "", i = 0, len = str.length, c1, c2, c3;
|
140
|
-
while (i < len) {
|
141
|
-
c1 = str.charCodeAt(i++) & 0xff;
|
142
|
-
if (i === len) {
|
143
|
-
out += BASE64_ENCODE_CHARS.charAt(c1 >> 2);
|
144
|
-
out += BASE64_ENCODE_CHARS.charAt((c1 & 0x3) << 4);
|
145
|
-
out += "==";
|
146
|
-
break;
|
147
|
-
}
|
148
|
-
c2 = str.charCodeAt(i++);
|
149
|
-
if (i === len) {
|
150
|
-
out += BASE64_ENCODE_CHARS.charAt(c1 >> 2);
|
151
|
-
out += BASE64_ENCODE_CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
|
152
|
-
out += BASE64_ENCODE_CHARS.charAt((c2 & 0xF) << 2);
|
153
|
-
out += "=";
|
154
|
-
break;
|
155
|
-
}
|
156
|
-
c3 = str.charCodeAt(i++);
|
157
|
-
out += BASE64_ENCODE_CHARS.charAt(c1 >> 2);
|
158
|
-
out += BASE64_ENCODE_CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
|
159
|
-
out += BASE64_ENCODE_CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
|
160
|
-
out += BASE64_ENCODE_CHARS.charAt(c3 & 0x3F);
|
161
|
-
}
|
162
|
-
return out;
|
163
|
-
};
|
164
|
-
|
165
|
-
/**
|
166
|
-
* Checks if a given DOM element exists in remote page.
|
167
|
-
*
|
168
|
-
* @param String selector CSS3 selector
|
169
|
-
* @return Boolean
|
170
|
-
*/
|
171
|
-
this.exists = function exists(selector) {
|
172
|
-
try {
|
173
|
-
return this.findAll(selector).length > 0;
|
174
|
-
} catch (e) {
|
175
|
-
return false;
|
176
|
-
}
|
177
|
-
};
|
178
|
-
|
179
|
-
/**
|
180
|
-
* Fetches innerText within the element(s) matching a given CSS3
|
181
|
-
* selector.
|
182
|
-
*
|
183
|
-
* @param String selector A CSS3 selector
|
184
|
-
* @return String
|
185
|
-
*/
|
186
|
-
this.fetchText = function fetchText(selector) {
|
187
|
-
var text = '', elements = this.findAll(selector);
|
188
|
-
if (elements && elements.length) {
|
189
|
-
Array.prototype.forEach.call(elements, function _forEach(element) {
|
190
|
-
text += element.textContent || element.innerText;
|
191
|
-
});
|
192
|
-
}
|
193
|
-
return text;
|
194
|
-
};
|
195
|
-
|
196
|
-
/**
|
197
|
-
* Fills a form with provided field values, and optionnaly submits it.
|
198
|
-
*
|
199
|
-
* @param HTMLElement|String form A form element, or a CSS3 selector to a form element
|
200
|
-
* @param Object vals Field values
|
201
|
-
* @return Object An object containing setting result for each field, including file uploads
|
202
|
-
*/
|
203
|
-
this.fill = function fill(form, vals) {
|
204
|
-
/*jshint maxcomplexity:8*/
|
205
|
-
var out = {
|
206
|
-
errors: [],
|
207
|
-
fields: [],
|
208
|
-
files: []
|
209
|
-
};
|
210
|
-
if (!(form instanceof HTMLElement) || typeof form === "string") {
|
211
|
-
this.log("attempting to fetch form element from selector: '" + form + "'", "info");
|
212
|
-
try {
|
213
|
-
form = this.findOne(form);
|
214
|
-
} catch (e) {
|
215
|
-
if (e.name === "SYNTAX_ERR") {
|
216
|
-
out.errors.push("invalid form selector provided: '" + form + "'");
|
217
|
-
return out;
|
218
|
-
}
|
219
|
-
}
|
220
|
-
}
|
221
|
-
if (!form) {
|
222
|
-
out.errors.push("form not found");
|
223
|
-
return out;
|
224
|
-
}
|
225
|
-
for (var name in vals) {
|
226
|
-
if (!vals.hasOwnProperty(name)) {
|
227
|
-
continue;
|
228
|
-
}
|
229
|
-
var field = this.findAll('[name="' + name + '"]', form);
|
230
|
-
var value = vals[name];
|
231
|
-
if (!field || field.length === 0) {
|
232
|
-
out.errors.push('no field named "' + name + '" in form');
|
233
|
-
continue;
|
234
|
-
}
|
235
|
-
try {
|
236
|
-
out.fields[name] = this.setField(field, value);
|
237
|
-
} catch (err) {
|
238
|
-
if (err.name === "FileUploadError") {
|
239
|
-
out.files.push({
|
240
|
-
name: name,
|
241
|
-
path: err.path
|
242
|
-
});
|
243
|
-
} else if(err.name === "FieldNotFound") {
|
244
|
-
out.errors.push('Form field named "' + name + '" was not found.');
|
245
|
-
} else {
|
246
|
-
out.errors.push(err.toString());
|
247
|
-
}
|
248
|
-
}
|
249
|
-
}
|
250
|
-
return out;
|
251
|
-
};
|
252
|
-
|
253
|
-
/**
|
254
|
-
* Finds all DOM elements matching by the provided selector.
|
255
|
-
*
|
256
|
-
* @param String selector CSS3 selector
|
257
|
-
* @param HTMLElement|null scope Element to search child elements within
|
258
|
-
* @return NodeList|undefined
|
259
|
-
*/
|
260
|
-
this.findAll = function findAll(selector, scope) {
|
261
|
-
scope = scope || this.options.scope;
|
262
|
-
try {
|
263
|
-
var pSelector = this.processSelector(selector);
|
264
|
-
if (pSelector.type === 'xpath') {
|
265
|
-
return this.getElementsByXPath(pSelector.path, scope);
|
266
|
-
} else {
|
267
|
-
return scope.querySelectorAll(pSelector.path);
|
268
|
-
}
|
269
|
-
} catch (e) {
|
270
|
-
this.log('findAll(): invalid selector provided "' + selector + '":' + e, "error");
|
271
|
-
}
|
272
|
-
};
|
273
|
-
|
274
|
-
/**
|
275
|
-
* Finds a DOM element by the provided selector.
|
276
|
-
*
|
277
|
-
* @param String selector CSS3 selector
|
278
|
-
* @param HTMLElement|null scope Element to search child elements within
|
279
|
-
* @return HTMLElement|undefined
|
280
|
-
*/
|
281
|
-
this.findOne = function findOne(selector, scope) {
|
282
|
-
scope = scope || this.options.scope;
|
283
|
-
try {
|
284
|
-
var pSelector = this.processSelector(selector);
|
285
|
-
if (pSelector.type === 'xpath') {
|
286
|
-
return this.getElementByXPath(pSelector.path, scope);
|
287
|
-
} else {
|
288
|
-
return scope.querySelector(pSelector.path);
|
289
|
-
}
|
290
|
-
} catch (e) {
|
291
|
-
this.log('findOne(): invalid selector provided "' + selector + '":' + e, "error");
|
292
|
-
}
|
293
|
-
};
|
294
|
-
|
295
|
-
/**
|
296
|
-
* Downloads a resource behind an url and returns its base64-encoded
|
297
|
-
* contents.
|
298
|
-
*
|
299
|
-
* @param String url The resource url
|
300
|
-
* @param String method The request method, optional (default: GET)
|
301
|
-
* @param Object data The request data, optional
|
302
|
-
* @return String Base64 contents string
|
303
|
-
*/
|
304
|
-
this.getBase64 = function getBase64(url, method, data) {
|
305
|
-
return this.encode(this.getBinary(url, method, data));
|
306
|
-
};
|
307
|
-
|
308
|
-
/**
|
309
|
-
* Retrieves string contents from a binary file behind an url. Silently
|
310
|
-
* fails but log errors.
|
311
|
-
*
|
312
|
-
* @param String url Url.
|
313
|
-
* @param String method HTTP method.
|
314
|
-
* @param Object data Request parameters.
|
315
|
-
* @return String
|
316
|
-
*/
|
317
|
-
this.getBinary = function getBinary(url, method, data) {
|
318
|
-
try {
|
319
|
-
return this.sendAJAX(url, method, data, false);
|
320
|
-
} catch (e) {
|
321
|
-
if (e.name === "NETWORK_ERR" && e.code === 101) {
|
322
|
-
this.log("getBinary(): Unfortunately, casperjs cannot make cross domain ajax requests", "warning");
|
323
|
-
}
|
324
|
-
this.log("getBinary(): Error while fetching " + url + ": " + e, "error");
|
325
|
-
return "";
|
326
|
-
}
|
327
|
-
};
|
328
|
-
|
329
|
-
/**
|
330
|
-
* Retrieves total document height.
|
331
|
-
* http://james.padolsey.com/javascript/get-document-height-cross-browser/
|
332
|
-
*
|
333
|
-
* @return {Number}
|
334
|
-
*/
|
335
|
-
this.getDocumentHeight = function getDocumentHeight() {
|
336
|
-
return Math.max(
|
337
|
-
Math.max(document.body.scrollHeight, document.documentElement.scrollHeight),
|
338
|
-
Math.max(document.body.offsetHeight, document.documentElement.offsetHeight),
|
339
|
-
Math.max(document.body.clientHeight, document.documentElement.clientHeight)
|
340
|
-
);
|
341
|
-
};
|
342
|
-
|
343
|
-
/**
|
344
|
-
* Retrieves bounding rect coordinates of the HTML element matching the
|
345
|
-
* provided CSS3 selector in the following form:
|
346
|
-
*
|
347
|
-
* {top: y, left: x, width: w, height:, h}
|
348
|
-
*
|
349
|
-
* @param String selector
|
350
|
-
* @return Object or null
|
351
|
-
*/
|
352
|
-
this.getElementBounds = function getElementBounds(selector) {
|
353
|
-
try {
|
354
|
-
var clipRect = this.findOne(selector).getBoundingClientRect();
|
355
|
-
return {
|
356
|
-
top: clipRect.top,
|
357
|
-
left: clipRect.left,
|
358
|
-
width: clipRect.width,
|
359
|
-
height: clipRect.height
|
360
|
-
};
|
361
|
-
} catch (e) {
|
362
|
-
this.log("Unable to fetch bounds for element " + selector, "warning");
|
363
|
-
}
|
364
|
-
};
|
365
|
-
|
366
|
-
/**
|
367
|
-
* Retrieves the list of bounding rect coordinates for all the HTML elements matching the
|
368
|
-
* provided CSS3 selector, in the following form:
|
369
|
-
*
|
370
|
-
* [{top: y, left: x, width: w, height:, h},
|
371
|
-
* {top: y, left: x, width: w, height:, h},
|
372
|
-
* ...]
|
373
|
-
*
|
374
|
-
* @param String selector
|
375
|
-
* @return Array
|
376
|
-
*/
|
377
|
-
this.getElementsBounds = function getElementsBounds(selector) {
|
378
|
-
var elements = this.findAll(selector);
|
379
|
-
var self = this;
|
380
|
-
try {
|
381
|
-
return Array.prototype.map.call(elements, function(element) {
|
382
|
-
var clipRect = element.getBoundingClientRect();
|
383
|
-
return {
|
384
|
-
top: clipRect.top,
|
385
|
-
left: clipRect.left,
|
386
|
-
width: clipRect.width,
|
387
|
-
height: clipRect.height
|
388
|
-
};
|
389
|
-
});
|
390
|
-
} catch (e) {
|
391
|
-
this.log("Unable to fetch bounds for elements matching " + selector, "warning");
|
392
|
-
}
|
393
|
-
};
|
394
|
-
|
395
|
-
/**
|
396
|
-
* Retrieves information about the node matching the provided selector.
|
397
|
-
*
|
398
|
-
* @param String|Object selector CSS3/XPath selector
|
399
|
-
* @return Object
|
400
|
-
*/
|
401
|
-
this.getElementInfo = function getElementInfo(selector) {
|
402
|
-
var element = this.findOne(selector);
|
403
|
-
var bounds = this.getElementBounds(selector);
|
404
|
-
var attributes = {};
|
405
|
-
[].forEach.call(element.attributes, function(attr) {
|
406
|
-
attributes[attr.name.toLowerCase()] = attr.value;
|
407
|
-
});
|
408
|
-
return {
|
409
|
-
nodeName: element.nodeName.toLowerCase(),
|
410
|
-
attributes: attributes,
|
411
|
-
tag: element.outerHTML,
|
412
|
-
html: element.innerHTML,
|
413
|
-
text: element.innerText,
|
414
|
-
x: bounds.left,
|
415
|
-
y: bounds.top,
|
416
|
-
width: bounds.width,
|
417
|
-
height: bounds.height,
|
418
|
-
visible: this.visible(selector)
|
419
|
-
};
|
420
|
-
};
|
421
|
-
|
422
|
-
/**
|
423
|
-
* Retrieves a single DOM element matching a given XPath expression.
|
424
|
-
*
|
425
|
-
* @param String expression The XPath expression
|
426
|
-
* @param HTMLElement|null scope Element to search child elements within
|
427
|
-
* @return HTMLElement or null
|
428
|
-
*/
|
429
|
-
this.getElementByXPath = function getElementByXPath(expression, scope) {
|
430
|
-
scope = scope || this.options.scope;
|
431
|
-
var a = document.evaluate(expression, scope, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
432
|
-
if (a.snapshotLength > 0) {
|
433
|
-
return a.snapshotItem(0);
|
434
|
-
}
|
435
|
-
};
|
436
|
-
|
437
|
-
/**
|
438
|
-
* Retrieves all DOM elements matching a given XPath expression.
|
439
|
-
*
|
440
|
-
* @param String expression The XPath expression
|
441
|
-
* @param HTMLElement|null scope Element to search child elements within
|
442
|
-
* @return Array
|
443
|
-
*/
|
444
|
-
this.getElementsByXPath = function getElementsByXPath(expression, scope) {
|
445
|
-
scope = scope || this.options.scope;
|
446
|
-
var nodes = [];
|
447
|
-
var a = document.evaluate(expression, scope, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
448
|
-
for (var i = 0; i < a.snapshotLength; i++) {
|
449
|
-
nodes.push(a.snapshotItem(i));
|
450
|
-
}
|
451
|
-
return nodes;
|
452
|
-
};
|
453
|
-
|
454
|
-
/**
|
455
|
-
* Retrieves the value of a form field.
|
456
|
-
*
|
457
|
-
* @param String inputName The for input name attr value
|
458
|
-
* @return Mixed
|
459
|
-
*/
|
460
|
-
this.getFieldValue = function getFieldValue(inputName) {
|
461
|
-
function getSingleValue(input) {
|
462
|
-
try {
|
463
|
-
type = input.getAttribute('type').toLowerCase();
|
464
|
-
} catch (e) {
|
465
|
-
type = 'other';
|
466
|
-
}
|
467
|
-
if (['checkbox', 'radio'].indexOf(type) === -1) {
|
468
|
-
return input.value;
|
469
|
-
}
|
470
|
-
// single checkbox or… radio button (weird, I know)
|
471
|
-
if (input.hasAttribute('value')) {
|
472
|
-
return input.checked ? input.getAttribute('value') : undefined;
|
473
|
-
}
|
474
|
-
return input.checked;
|
475
|
-
}
|
476
|
-
function getMultipleValues(inputs) {
|
477
|
-
type = inputs[0].getAttribute('type').toLowerCase();
|
478
|
-
if (type === 'radio') {
|
479
|
-
var value;
|
480
|
-
[].forEach.call(inputs, function(radio) {
|
481
|
-
value = radio.checked ? radio.value : undefined;
|
482
|
-
});
|
483
|
-
return value;
|
484
|
-
} else if (type === 'checkbox') {
|
485
|
-
var values = [];
|
486
|
-
[].forEach.call(inputs, function(checkbox) {
|
487
|
-
if (checkbox.checked) {
|
488
|
-
values.push(checkbox.value);
|
489
|
-
}
|
490
|
-
});
|
491
|
-
return values;
|
492
|
-
}
|
493
|
-
}
|
494
|
-
var inputs = this.findAll('[name="' + inputName + '"]'), type;
|
495
|
-
switch (inputs.length) {
|
496
|
-
case 0: return null;
|
497
|
-
case 1: return getSingleValue(inputs[0]);
|
498
|
-
default: return getMultipleValues(inputs);
|
499
|
-
}
|
500
|
-
};
|
501
|
-
|
502
|
-
/**
|
503
|
-
* Retrieves a given form all of its field values.
|
504
|
-
*
|
505
|
-
* @param String selector A DOM CSS3/XPath selector
|
506
|
-
* @return Object
|
507
|
-
*/
|
508
|
-
this.getFormValues = function getFormValues(selector) {
|
509
|
-
var form = this.findOne(selector);
|
510
|
-
var values = {};
|
511
|
-
var self = this;
|
512
|
-
[].forEach.call(form.elements, function(element) {
|
513
|
-
var name = element.getAttribute('name');
|
514
|
-
if (name) {
|
515
|
-
values[name] = self.getFieldValue(name);
|
516
|
-
}
|
517
|
-
});
|
518
|
-
return values;
|
519
|
-
};
|
520
|
-
|
521
|
-
/**
|
522
|
-
* Logs a message. Will format the message a way CasperJS will be able
|
523
|
-
* to log phantomjs side.
|
524
|
-
*
|
525
|
-
* @param String message The message to log
|
526
|
-
* @param String level The log level
|
527
|
-
*/
|
528
|
-
this.log = function log(message, level) {
|
529
|
-
console.log("[casper:" + (level || "debug") + "] " + message);
|
530
|
-
};
|
531
|
-
|
532
|
-
/**
|
533
|
-
* Dispatches a mouse event to the DOM element behind the provided selector.
|
534
|
-
*
|
535
|
-
* @param String type Type of event to dispatch
|
536
|
-
* @param String selector A CSS3 selector to the element to click
|
537
|
-
* @return Boolean
|
538
|
-
*/
|
539
|
-
this.mouseEvent = function mouseEvent(type, selector) {
|
540
|
-
var elem = this.findOne(selector);
|
541
|
-
if (!elem) {
|
542
|
-
this.log("mouseEvent(): Couldn't find any element matching '" + selector + "' selector", "error");
|
543
|
-
return false;
|
544
|
-
}
|
545
|
-
try {
|
546
|
-
var evt = document.createEvent("MouseEvents");
|
547
|
-
var center_x = 1, center_y = 1;
|
548
|
-
try {
|
549
|
-
var pos = elem.getBoundingClientRect();
|
550
|
-
center_x = Math.floor((pos.left + pos.right) / 2),
|
551
|
-
center_y = Math.floor((pos.top + pos.bottom) / 2);
|
552
|
-
} catch(e) {}
|
553
|
-
evt.initMouseEvent(type, true, true, window, 1, 1, 1, center_x, center_y, false, false, false, false, 0, elem);
|
554
|
-
// dispatchEvent return value is false if at least one of the event
|
555
|
-
// handlers which handled this event called preventDefault;
|
556
|
-
// so we cannot returns this results as it cannot accurately informs on the status
|
557
|
-
// of the operation
|
558
|
-
// let's assume the event has been sent ok it didn't raise any error
|
559
|
-
elem.dispatchEvent(evt);
|
560
|
-
return true;
|
561
|
-
} catch (e) {
|
562
|
-
this.log("Failed dispatching " + type + "mouse event on " + selector + ": " + e, "error");
|
563
|
-
return false;
|
564
|
-
}
|
565
|
-
};
|
566
|
-
|
567
|
-
/**
|
568
|
-
* Processes a selector input, either as a string or an object.
|
569
|
-
*
|
570
|
-
* If passed an object, if must be of the form:
|
571
|
-
*
|
572
|
-
* selectorObject = {
|
573
|
-
* type: <'css' or 'xpath'>,
|
574
|
-
* path: <a string>
|
575
|
-
* }
|
576
|
-
*
|
577
|
-
* @param String|Object selector The selector string or object
|
578
|
-
*
|
579
|
-
* @return an object containing 'type' and 'path' keys
|
580
|
-
*/
|
581
|
-
this.processSelector = function processSelector(selector) {
|
582
|
-
var selectorObject = {
|
583
|
-
toString: function toString() {
|
584
|
-
return this.type + ' selector: ' + this.path;
|
585
|
-
}
|
586
|
-
};
|
587
|
-
if (typeof selector === "string") {
|
588
|
-
// defaults to CSS selector
|
589
|
-
selectorObject.type = "css";
|
590
|
-
selectorObject.path = selector;
|
591
|
-
return selectorObject;
|
592
|
-
} else if (typeof selector === "object") {
|
593
|
-
// validation
|
594
|
-
if (!selector.hasOwnProperty('type') || !selector.hasOwnProperty('path')) {
|
595
|
-
throw new Error("Incomplete selector object");
|
596
|
-
} else if (SUPPORTED_SELECTOR_TYPES.indexOf(selector.type) === -1) {
|
597
|
-
throw new Error("Unsupported selector type: " + selector.type);
|
598
|
-
}
|
599
|
-
if (!selector.hasOwnProperty('toString')) {
|
600
|
-
selector.toString = selectorObject.toString;
|
601
|
-
}
|
602
|
-
return selector;
|
603
|
-
}
|
604
|
-
throw new Error("Unsupported selector type: " + typeof selector);
|
605
|
-
};
|
606
|
-
|
607
|
-
/**
|
608
|
-
* Removes all DOM elements matching a given XPath expression.
|
609
|
-
*
|
610
|
-
* @param String expression The XPath expression
|
611
|
-
* @return Array
|
612
|
-
*/
|
613
|
-
this.removeElementsByXPath = function removeElementsByXPath(expression) {
|
614
|
-
var a = document.evaluate(expression, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
|
615
|
-
for (var i = 0; i < a.snapshotLength; i++) {
|
616
|
-
a.snapshotItem(i).parentNode.removeChild(a.snapshotItem(i));
|
617
|
-
}
|
618
|
-
};
|
619
|
-
|
620
|
-
/**
|
621
|
-
* Performs an AJAX request.
|
622
|
-
*
|
623
|
-
* @param String url Url.
|
624
|
-
* @param String method HTTP method (default: GET).
|
625
|
-
* @param Object data Request parameters.
|
626
|
-
* @param Boolean async Asynchroneous request? (default: false)
|
627
|
-
* @return String Response text.
|
628
|
-
*/
|
629
|
-
this.sendAJAX = function sendAJAX(url, method, data, async) {
|
630
|
-
var xhr = new XMLHttpRequest(),
|
631
|
-
dataString = "",
|
632
|
-
dataList = [];
|
633
|
-
method = method && method.toUpperCase() || "GET";
|
634
|
-
xhr.open(method, url, !!async);
|
635
|
-
this.log("sendAJAX(): Using HTTP method: '" + method + "'", "debug");
|
636
|
-
xhr.overrideMimeType("text/plain; charset=x-user-defined");
|
637
|
-
if (method === "POST") {
|
638
|
-
if (typeof data === "object") {
|
639
|
-
for (var k in data) {
|
640
|
-
dataList.push(encodeURIComponent(k) + "=" + encodeURIComponent(data[k].toString()));
|
641
|
-
}
|
642
|
-
dataString = dataList.join('&');
|
643
|
-
this.log("sendAJAX(): Using request data: '" + dataString + "'", "debug");
|
644
|
-
} else if (typeof data === "string") {
|
645
|
-
dataString = data;
|
646
|
-
}
|
647
|
-
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
648
|
-
}
|
649
|
-
xhr.send(method === "POST" ? dataString : null);
|
650
|
-
return xhr.responseText;
|
651
|
-
};
|
652
|
-
|
653
|
-
/**
|
654
|
-
* Sets a field (or a set of fields) value. Fails silently, but log
|
655
|
-
* error messages.
|
656
|
-
*
|
657
|
-
* @param HTMLElement|NodeList field One or more element defining a field
|
658
|
-
* @param mixed value The field value to set
|
659
|
-
*/
|
660
|
-
this.setField = function setField(field, value) {
|
661
|
-
/*jshint maxcomplexity:99 */
|
662
|
-
var logValue, fields, out;
|
663
|
-
value = logValue = (value || "");
|
664
|
-
if (field instanceof NodeList) {
|
665
|
-
fields = field;
|
666
|
-
field = fields[0];
|
667
|
-
}
|
668
|
-
if (!(field instanceof HTMLElement)) {
|
669
|
-
var error = new Error('Invalid field type; only HTMLElement and NodeList are supported');
|
670
|
-
error.name = 'FieldNotFound';
|
671
|
-
throw error;
|
672
|
-
}
|
673
|
-
if (this.options && this.options.safeLogs && field.getAttribute('type') === "password") {
|
674
|
-
// obfuscate password value
|
675
|
-
logValue = new Array(value.length + 1).join("*");
|
676
|
-
}
|
677
|
-
this.log('Set "' + field.getAttribute('name') + '" field value to ' + logValue, "debug");
|
678
|
-
try {
|
679
|
-
field.focus();
|
680
|
-
} catch (e) {
|
681
|
-
this.log("Unable to focus() input field " + field.getAttribute('name') + ": " + e, "warning");
|
682
|
-
}
|
683
|
-
var nodeName = field.nodeName.toLowerCase();
|
684
|
-
switch (nodeName) {
|
685
|
-
case "input":
|
686
|
-
var type = field.getAttribute('type') || "text";
|
687
|
-
switch (type.toLowerCase()) {
|
688
|
-
case "color":
|
689
|
-
case "date":
|
690
|
-
case "datetime":
|
691
|
-
case "datetime-local":
|
692
|
-
case "email":
|
693
|
-
case "hidden":
|
694
|
-
case "month":
|
695
|
-
case "number":
|
696
|
-
case "password":
|
697
|
-
case "range":
|
698
|
-
case "search":
|
699
|
-
case "tel":
|
700
|
-
case "text":
|
701
|
-
case "time":
|
702
|
-
case "url":
|
703
|
-
case "week":
|
704
|
-
field.value = value;
|
705
|
-
break;
|
706
|
-
case "checkbox":
|
707
|
-
if (fields.length > 1) {
|
708
|
-
var values = value;
|
709
|
-
if (!Array.isArray(values)) {
|
710
|
-
values = [values];
|
711
|
-
}
|
712
|
-
Array.prototype.forEach.call(fields, function _forEach(f) {
|
713
|
-
f.checked = values.indexOf(f.value) !== -1 ? true : false;
|
714
|
-
});
|
715
|
-
} else {
|
716
|
-
field.checked = value ? true : false;
|
717
|
-
}
|
718
|
-
break;
|
719
|
-
case "file":
|
720
|
-
throw {
|
721
|
-
name: "FileUploadError",
|
722
|
-
message: "File field must be filled using page.uploadFile",
|
723
|
-
path: value
|
724
|
-
};
|
725
|
-
case "radio":
|
726
|
-
if (fields) {
|
727
|
-
Array.prototype.forEach.call(fields, function _forEach(e) {
|
728
|
-
e.checked = (e.value === value);
|
729
|
-
});
|
730
|
-
} else {
|
731
|
-
out = 'Provided radio elements are empty';
|
732
|
-
}
|
733
|
-
break;
|
734
|
-
default:
|
735
|
-
out = "Unsupported input field type: " + type;
|
736
|
-
break;
|
737
|
-
}
|
738
|
-
break;
|
739
|
-
case "select":
|
740
|
-
case "textarea":
|
741
|
-
field.value = value;
|
742
|
-
break;
|
743
|
-
default:
|
744
|
-
out = 'Unsupported field type: ' + nodeName;
|
745
|
-
break;
|
746
|
-
}
|
747
|
-
// firing the `change` event
|
748
|
-
var changeEvent = document.createEvent("HTMLEvents");
|
749
|
-
changeEvent.initEvent('change', true, true);
|
750
|
-
field.dispatchEvent(changeEvent);
|
751
|
-
// blur the field
|
752
|
-
try {
|
753
|
-
field.blur();
|
754
|
-
} catch (err) {
|
755
|
-
this.log("Unable to blur() input field " + field.getAttribute('name') + ": " + err, "warning");
|
756
|
-
}
|
757
|
-
return out;
|
758
|
-
};
|
759
|
-
|
760
|
-
/**
|
761
|
-
* Checks if a given DOM element is visible in remote page.
|
762
|
-
*
|
763
|
-
* @param String selector CSS3 selector
|
764
|
-
* @return Boolean
|
765
|
-
*/
|
766
|
-
this.visible = function visible(selector) {
|
767
|
-
try {
|
768
|
-
var comp,
|
769
|
-
el = this.findOne(selector);
|
770
|
-
|
771
|
-
if (el) {
|
772
|
-
comp = window.getComputedStyle(el, null);
|
773
|
-
return comp.visibility !== 'hidden' && comp.display !== 'none' && el.offsetHeight > 0 && el.offsetWidth > 0;
|
774
|
-
}
|
775
|
-
return false;
|
776
|
-
} catch (e) {
|
777
|
-
return false;
|
778
|
-
}
|
779
|
-
};
|
780
|
-
};
|
781
|
-
})(typeof exports === "object" ? exports : window);
|