added events where applicable in Casper methods
Showing
3 changed files
with
321 additions
and
215 deletions
... | @@ -11,7 +11,7 @@ def resolve(path): | ... | @@ -11,7 +11,7 @@ def resolve(path): |
11 | return path | 11 | return path |
12 | 12 | ||
13 | CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)), '..')) | 13 | CASPER_PATH = os.path.abspath(os.path.join(os.path.dirname(resolve(__file__)), '..')) |
14 | CASPER_ARGS = ['phantomjs', os.path.join(CASPER_PATH, 'bin', 'bootstrap.js'), '--casper-path=%s' % CASPER_PATH, '--cli'] | 14 | CASPER_ARGS = ['phantomjs14', os.path.join(CASPER_PATH, 'bin', 'bootstrap.js'), '--casper-path=%s' % CASPER_PATH, '--cli'] |
15 | CASPER_ARGS.extend(sys.argv[1:]) | 15 | CASPER_ARGS.extend(sys.argv[1:]) |
16 | 16 | ||
17 | try: | 17 | try: | ... | ... |
... | @@ -26,6 +26,7 @@ | ... | @@ -26,6 +26,7 @@ |
26 | * | 26 | * |
27 | */ | 27 | */ |
28 | 28 | ||
29 | var events = require('events'); | ||
29 | var fs = require('fs'); | 30 | var fs = require('fs'); |
30 | var utils = require('utils'); | 31 | var utils = require('utils'); |
31 | 32 | ||
... | @@ -104,24 +105,24 @@ var Casper = function(options) { | ... | @@ -104,24 +105,24 @@ var Casper = function(options) { |
104 | this.test = require('tester').create(this); | 105 | this.test = require('tester').create(this); |
105 | }; | 106 | }; |
106 | 107 | ||
108 | utils.inherits(Casper, events.EventEmitter); | ||
109 | |||
110 | |||
107 | /** | 111 | /** |
108 | * Casper prototype | ||
109 | */ | ||
110 | Casper.prototype = { | ||
111 | /** | ||
112 | * Go a step back in browser's history | 112 | * Go a step back in browser's history |
113 | * | 113 | * |
114 | * @return Casper | 114 | * @return Casper |
115 | */ | 115 | */ |
116 | back: function() { | 116 | Casper.prototype.back = function() { |
117 | return this.then(function(self) { | 117 | return this.then(function() { |
118 | self.evaluate(function() { | 118 | this.emit('casper.back'); |
119 | this.evaluate(function() { | ||
119 | history.back(); | 120 | history.back(); |
120 | }); | 121 | }); |
121 | }); | 122 | }); |
122 | }, | 123 | }; |
123 | 124 | ||
124 | /** | 125 | /** |
125 | * Encodes a resource using the base64 algorithm synchroneously using | 126 | * Encodes a resource using the base64 algorithm synchroneously using |
126 | * client-side XMLHttpRequest. | 127 | * client-side XMLHttpRequest. |
127 | * | 128 | * |
... | @@ -132,13 +133,13 @@ Casper.prototype = { | ... | @@ -132,13 +133,13 @@ Casper.prototype = { |
132 | * @param String data The data to send, optional | 133 | * @param String data The data to send, optional |
133 | * @return string Base64 encoded result | 134 | * @return string Base64 encoded result |
134 | */ | 135 | */ |
135 | base64encode: function(url, method, data) { | 136 | Casper.prototype.base64encode = function(url, method, data) { |
136 | return this.evaluate(function(url, method, data) { | 137 | return this.evaluate(function(url, method, data) { |
137 | return __utils__.getBase64(url, method, data); | 138 | return __utils__.getBase64(url, method, data); |
138 | }, { url: url, method: method, data: data }); | 139 | }, { url: url, method: method, data: data }); |
139 | }, | 140 | }; |
140 | 141 | ||
141 | /** | 142 | /** |
142 | * Proxy method for WebPage#render. Adds a clipRect parameter for | 143 | * Proxy method for WebPage#render. Adds a clipRect parameter for |
143 | * automatically set page clipRect setting values and sets it back once | 144 | * automatically set page clipRect setting values and sets it back once |
144 | * done. If the cliprect parameter is omitted, the full page viewport | 145 | * done. If the cliprect parameter is omitted, the full page viewport |
... | @@ -148,7 +149,7 @@ Casper.prototype = { | ... | @@ -148,7 +149,7 @@ Casper.prototype = { |
148 | * @param mixed clipRect An optional clipRect object (optional) | 149 | * @param mixed clipRect An optional clipRect object (optional) |
149 | * @return Casper | 150 | * @return Casper |
150 | */ | 151 | */ |
151 | capture: function(targetFile, clipRect) { | 152 | Casper.prototype.capture = function(targetFile, clipRect) { |
152 | var previousClipRect; | 153 | var previousClipRect; |
153 | targetFile = fs.absolute(targetFile); | 154 | targetFile = fs.absolute(targetFile); |
154 | if (clipRect) { | 155 | if (clipRect) { |
... | @@ -168,38 +169,38 @@ Casper.prototype = { | ... | @@ -168,38 +169,38 @@ Casper.prototype = { |
168 | this.page.clipRect = previousClipRect; | 169 | this.page.clipRect = previousClipRect; |
169 | } | 170 | } |
170 | return this; | 171 | return this; |
171 | }, | 172 | }; |
172 | 173 | ||
173 | /** | 174 | /** |
174 | * Captures the page area containing the provided selector. | 175 | * Captures the page area containing the provided selector. |
175 | * | 176 | * |
176 | * @param String targetFile Target destination file path. | 177 | * @param String targetFile Target destination file path. |
177 | * @param String selector CSS3 selector | 178 | * @param String selector CSS3 selector |
178 | * @return Casper | 179 | * @return Casper |
179 | */ | 180 | */ |
180 | captureSelector: function(targetFile, selector) { | 181 | Casper.prototype.captureSelector = function(targetFile, selector) { |
181 | return this.capture(targetFile, this.getElementBounds(selector)); | 182 | return this.capture(targetFile, this.getElementBounds(selector)); |
182 | }, | 183 | }; |
183 | 184 | ||
184 | /** | 185 | /** |
185 | * Checks for any further navigation step to process. | 186 | * Checks for any further navigation step to process. |
186 | * | 187 | * |
187 | * @param Casper self A self reference | 188 | * @param Casper self A self reference |
188 | * @param function onComplete An options callback to apply on completion | 189 | * @param function onComplete An options callback to apply on completion |
189 | */ | 190 | */ |
190 | checkStep: function(self, onComplete) { | 191 | Casper.prototype.checkStep = function(self, onComplete) { |
191 | if (self.pendingWait || self.loadInProgress) { | 192 | if (self.pendingWait || self.loadInProgress) { |
192 | return; | 193 | return; |
193 | } | 194 | } |
194 | var step = self.steps[self.step++]; | 195 | var step = self.steps[self.step++]; |
195 | if (utils.isType(step, "function")) { | 196 | if (utils.isFunction(step)) { |
196 | self.runStep(step); | 197 | self.runStep(step); |
197 | } else { | 198 | } else { |
198 | self.result.time = new Date().getTime() - self.startTime; | 199 | self.result.time = new Date().getTime() - self.startTime; |
199 | self.log("Done " + self.steps.length + " steps in " + self.result.time + 'ms.', "info"); | 200 | self.log("Done " + self.steps.length + " steps in " + self.result.time + 'ms.', "info"); |
200 | self.page.content = ''; // avoid having previously loaded DOM contents being still active (refs #34) | 201 | self.page.content = ''; // avoid having previously loaded DOM contents being still active (refs #34) |
201 | clearInterval(self.checker); | 202 | clearInterval(self.checker); |
202 | if (utils.isType(onComplete, "function")) { | 203 | if (utils.isFunction(onComplete)) { |
203 | try { | 204 | try { |
204 | onComplete.call(self, self); | 205 | onComplete.call(self, self); |
205 | } catch (err) { | 206 | } catch (err) { |
... | @@ -210,9 +211,9 @@ Casper.prototype = { | ... | @@ -210,9 +211,9 @@ Casper.prototype = { |
210 | self.exit(); | 211 | self.exit(); |
211 | } | 212 | } |
212 | } | 213 | } |
213 | }, | 214 | }; |
214 | 215 | ||
215 | /** | 216 | /** |
216 | * Emulates a click on the element from the provided selector, if | 217 | * Emulates a click on the element from the provided selector, if |
217 | * possible. In case of success, `true` is returned. | 218 | * possible. In case of success, `true` is returned. |
218 | * | 219 | * |
... | @@ -220,82 +221,85 @@ Casper.prototype = { | ... | @@ -220,82 +221,85 @@ Casper.prototype = { |
220 | * @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true) | 221 | * @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true) |
221 | * @return Boolean | 222 | * @return Boolean |
222 | */ | 223 | */ |
223 | click: function(selector, fallbackToHref) { | 224 | Casper.prototype.click = function(selector, fallbackToHref) { |
224 | fallbackToHref = utils.isType(fallbackToHref, "undefined") ? true : !!fallbackToHref; | 225 | fallbackToHref = utils.isType(fallbackToHref, "undefined") ? true : !!fallbackToHref; |
225 | this.log("Click on selector: " + selector, "debug"); | 226 | this.log("Click on selector: " + selector, "debug"); |
227 | this.emit('casper.click', selector, fallbackToHref); | ||
226 | return this.evaluate(function(selector, fallbackToHref) { | 228 | return this.evaluate(function(selector, fallbackToHref) { |
227 | return __utils__.click(selector, fallbackToHref); | 229 | return __utils__.click(selector, fallbackToHref); |
228 | }, { | 230 | }, { |
229 | selector: selector, | 231 | selector: selector, |
230 | fallbackToHref: fallbackToHref | 232 | fallbackToHref: fallbackToHref |
231 | }); | 233 | }); |
232 | }, | 234 | }; |
233 | 235 | ||
234 | /** | 236 | /** |
235 | * Creates a step definition. | 237 | * Creates a step definition. |
236 | * | 238 | * |
237 | * @param Function fn The step function to call | 239 | * @param Function fn The step function to call |
238 | * @param Object options Step options | 240 | * @param Object options Step options |
239 | * @return Function The final step function | 241 | * @return Function The final step function |
240 | */ | 242 | */ |
241 | createStep: function(fn, options) { | 243 | Casper.prototype.createStep = function(fn, options) { |
242 | if (!utils.isType(fn, "function")) { | 244 | if (!utils.isFunction(fn)) { |
243 | throw new Error("createStep(): a step definition must be a function"); | 245 | throw new Error("createStep(): a step definition must be a function"); |
244 | } | 246 | } |
245 | fn.options = utils.isType(options, "object") ? options : {}; | 247 | fn.options = utils.isObject(options) ? options : {}; |
248 | this.emit('casper.step.created', fn); | ||
246 | return fn; | 249 | return fn; |
247 | }, | 250 | }; |
248 | 251 | ||
249 | /** | 252 | /** |
250 | * Logs the HTML code of the current page. | 253 | * Logs the HTML code of the current page. |
251 | * | 254 | * |
252 | * @return Casper | 255 | * @return Casper |
253 | */ | 256 | */ |
254 | debugHTML: function() { | 257 | Casper.prototype.debugHTML = function() { |
255 | this.echo(this.evaluate(function() { | 258 | this.echo(this.evaluate(function() { |
256 | return document.body.innerHTML; | 259 | return document.body.innerHTML; |
257 | })); | 260 | })); |
258 | return this; | 261 | return this; |
259 | }, | 262 | }; |
260 | 263 | ||
261 | /** | 264 | /** |
262 | * Logs the textual contents of the current page. | 265 | * Logs the textual contents of the current page. |
263 | * | 266 | * |
264 | * @return Casper | 267 | * @return Casper |
265 | */ | 268 | */ |
266 | debugPage: function() { | 269 | Casper.prototype.debugPage = function() { |
267 | this.echo(this.evaluate(function() { | 270 | this.echo(this.evaluate(function() { |
268 | return document.body.innerText; | 271 | return document.body.innerText; |
269 | })); | 272 | })); |
270 | return this; | 273 | return this; |
271 | }, | 274 | }; |
272 | 275 | ||
273 | /** | 276 | /** |
274 | * Exit phantom on failure, with a logged error message. | 277 | * Exit phantom on failure, with a logged error message. |
275 | * | 278 | * |
276 | * @param String message An optional error message | 279 | * @param String message An optional error message |
277 | * @param Number status An optional exit status code (must be > 0) | 280 | * @param Number status An optional exit status code (must be > 0) |
278 | * @return Casper | 281 | * @return Casper |
279 | */ | 282 | */ |
280 | die: function(message, status) { | 283 | Casper.prototype.die = function(message, status) { |
281 | this.result.status = 'error'; | 284 | this.result.status = "error"; |
282 | this.result.time = new Date().getTime() - this.startTime; | 285 | this.result.time = new Date().getTime() - this.startTime; |
283 | message = utils.isType(message, "string") && message.length > 0 ? message : DEFAULT_DIE_MESSAGE; | 286 | message = utils.isString(message) && message.length > 0 ? message : DEFAULT_DIE_MESSAGE; |
284 | this.log(message, "error"); | 287 | this.log(message, "error"); |
285 | if (utils.isType(this.options.onDie, "function")) { | 288 | this.emit('casper.die', message, status); |
289 | if (utils.isFunction(this.options.onDie)) { | ||
286 | this.options.onDie.call(this, this, message, status); | 290 | this.options.onDie.call(this, this, message, status); |
287 | } | 291 | } |
288 | return this.exit(Number(status) > 0 ? Number(status) : 1); | 292 | return this.exit(~~status > 0 ? ~~status : 1); |
289 | }, | 293 | }; |
290 | 294 | ||
291 | /** | 295 | /** |
292 | * Downloads a resource and saves it on the filesystem. | 296 | * Downloads a resource and saves it on the filesystem. |
293 | * | 297 | * |
294 | * @param String url The url of the resource to download | 298 | * @param String url The url of the resource to download |
295 | * @param String targetPath The destination file path | 299 | * @param String targetPath The destination file path |
296 | * @return Casper | 300 | * @return Casper |
297 | */ | 301 | */ |
298 | download: function(url, targetPath) { | 302 | Casper.prototype.download = function(url, targetPath) { |
299 | var cu = require('clientutils').create(); | 303 | var cu = require('clientutils').create(); |
300 | try { | 304 | try { |
301 | fs.write(targetPath, cu.decode(this.base64encode(url)), 'w'); | 305 | fs.write(targetPath, cu.decode(this.base64encode(url)), 'w'); |
... | @@ -303,18 +307,18 @@ Casper.prototype = { | ... | @@ -303,18 +307,18 @@ Casper.prototype = { |
303 | this.log("Error while downloading " + url + " to " + targetPath + ": " + e, "error"); | 307 | this.log("Error while downloading " + url + " to " + targetPath + ": " + e, "error"); |
304 | } | 308 | } |
305 | return this; | 309 | return this; |
306 | }, | 310 | }; |
307 | 311 | ||
308 | /** | 312 | /** |
309 | * Iterates over the values of a provided array and execute a callback | 313 | * Iterates over the values of a provided array and execute a callback |
310 | * for each item. | 314 | * for @ item. |
311 | * | 315 | * |
312 | * @param Array array | 316 | * @param Array array |
313 | * @param Function fn Callback: function(self, item, index) | 317 | * @param Function fn Callback: function(self, item, index) |
314 | * @return Casper | 318 | * @return Casper |
315 | */ | 319 | */ |
316 | each: function(array, fn) { | 320 | Casper.prototype.each = function(array, fn) { |
317 | if (!utils.isType(array, "array")) { | 321 | if (!utils.isArray(array)) { |
318 | this.log("each() only works with arrays", "error"); | 322 | this.log("each() only works with arrays", "error"); |
319 | return this; | 323 | return this; |
320 | } | 324 | } |
... | @@ -324,20 +328,20 @@ Casper.prototype = { | ... | @@ -324,20 +328,20 @@ Casper.prototype = { |
324 | }); | 328 | }); |
325 | })(this); | 329 | })(this); |
326 | return this; | 330 | return this; |
327 | }, | 331 | }; |
328 | 332 | ||
329 | /** | 333 | /** |
330 | * Prints something to stdout. | 334 | * Prints something to stdout. |
331 | * | 335 | * |
332 | * @param String text A string to echo to stdout | 336 | * @param String text A string to echo to stdout |
333 | * @return Casper | 337 | * @return Casper |
334 | */ | 338 | */ |
335 | echo: function(text, style) { | 339 | Casper.prototype.echo = function(text, style) { |
336 | console.log(style ? this.colorizer.colorize(text, style) : text); | 340 | console.log(style ? this.colorizer.colorize(text, style) : text); |
337 | return this; | 341 | return this; |
338 | }, | 342 | }; |
339 | 343 | ||
340 | /** | 344 | /** |
341 | * Evaluates an expression in the page context, a bit like what | 345 | * Evaluates an expression in the page context, a bit like what |
342 | * WebPage#evaluate does, but the passed function can also accept | 346 | * WebPage#evaluate does, but the passed function can also accept |
343 | * parameters if a context Object is also passed: | 347 | * parameters if a context Object is also passed: |
... | @@ -360,13 +364,13 @@ Casper.prototype = { | ... | @@ -360,13 +364,13 @@ Casper.prototype = { |
360 | * @return mixed | 364 | * @return mixed |
361 | * @see WebPage#evaluate | 365 | * @see WebPage#evaluate |
362 | */ | 366 | */ |
363 | evaluate: function(fn, context) { | 367 | Casper.prototype.evaluate = function(fn, context) { |
364 | context = utils.isType(context, "object") ? context : {}; | 368 | context = utils.isObject(context) ? context : {}; |
365 | var newFn = require('injector').create(fn).process(context); | 369 | var newFn = require('injector').create(fn).process(context); |
366 | return this.page.evaluate(newFn); | 370 | return this.page.evaluate(newFn); |
367 | }, | 371 | }; |
368 | 372 | ||
369 | /** | 373 | /** |
370 | * Evaluates an expression within the current page DOM and die() if it | 374 | * Evaluates an expression within the current page DOM and die() if it |
371 | * returns false. | 375 | * returns false. |
372 | * | 376 | * |
... | @@ -374,27 +378,27 @@ Casper.prototype = { | ... | @@ -374,27 +378,27 @@ Casper.prototype = { |
374 | * @param String message The error message to log | 378 | * @param String message The error message to log |
375 | * @return Casper | 379 | * @return Casper |
376 | */ | 380 | */ |
377 | evaluateOrDie: function(fn, message) { | 381 | Casper.prototype.evaluateOrDie = function(fn, message) { |
378 | if (!this.evaluate(fn)) { | 382 | if (!this.evaluate(fn)) { |
379 | return this.die(message); | 383 | return this.die(message); |
380 | } | 384 | } |
381 | return this; | 385 | return this; |
382 | }, | 386 | }; |
383 | 387 | ||
384 | /** | 388 | /** |
385 | * Checks if an element matching the provided CSS3 selector exists in | 389 | * Checks if an element matching the provided CSS3 selector exists in |
386 | * current page DOM. | 390 | * current page DOM. |
387 | * | 391 | * |
388 | * @param String selector A CSS3 selector | 392 | * @param String selector A CSS3 selector |
389 | * @return Boolean | 393 | * @return Boolean |
390 | */ | 394 | */ |
391 | exists: function(selector) { | 395 | Casper.prototype.exists = function(selector) { |
392 | return this.evaluate(function(selector) { | 396 | return this.evaluate(function(selector) { |
393 | return __utils__.exists(selector); | 397 | return __utils__.exists(selector); |
394 | }, { selector: selector }); | 398 | }, { selector: selector }); |
395 | }, | 399 | }; |
396 | 400 | ||
397 | /** | 401 | /** |
398 | * Checks if an element matching the provided CSS3 selector is visible | 402 | * Checks if an element matching the provided CSS3 selector is visible |
399 | * current page DOM by checking that offsetWidth and offsetHeight are | 403 | * current page DOM by checking that offsetWidth and offsetHeight are |
400 | * both non-zero. | 404 | * both non-zero. |
... | @@ -402,51 +406,53 @@ Casper.prototype = { | ... | @@ -402,51 +406,53 @@ Casper.prototype = { |
402 | * @param String selector A CSS3 selector | 406 | * @param String selector A CSS3 selector |
403 | * @return Boolean | 407 | * @return Boolean |
404 | */ | 408 | */ |
405 | visible: function(selector) { | 409 | Casper.prototype.visible = function(selector) { |
406 | return this.evaluate(function(selector) { | 410 | return this.evaluate(function(selector) { |
407 | return __utils__.visible(selector); | 411 | return __utils__.visible(selector); |
408 | }, { selector: selector }); | 412 | }, { selector: selector }); |
409 | }, | 413 | }; |
410 | 414 | ||
411 | /** | 415 | /** |
412 | * Exits phantom. | 416 | * Exits phantom. |
413 | * | 417 | * |
414 | * @param Number status Status | 418 | * @param Number status Status |
415 | * @return Casper | 419 | * @return Casper |
416 | */ | 420 | */ |
417 | exit: function(status) { | 421 | Casper.prototype.exit = function(status) { |
422 | this.emit('casper.exit', status); | ||
418 | phantom.exit(status); | 423 | phantom.exit(status); |
419 | return this; | 424 | return this; |
420 | }, | 425 | }; |
421 | 426 | ||
422 | /** | 427 | /** |
423 | * Fetches innerText within the element(s) matching a given CSS3 | 428 | * Fetches innerText within the element(s) matching a given CSS3 |
424 | * selector. | 429 | * selector. |
425 | * | 430 | * |
426 | * @param String selector A CSS3 selector | 431 | * @param String selector A CSS3 selector |
427 | * @return String | 432 | * @return String |
428 | */ | 433 | */ |
429 | fetchText: function(selector) { | 434 | Casper.prototype.fetchText = function(selector) { |
430 | return this.evaluate(function(selector) { | 435 | return this.evaluate(function(selector) { |
431 | return __utils__.fetchText(selector); | 436 | return __utils__.fetchText(selector); |
432 | }, { selector: selector }); | 437 | }, { selector: selector }); |
433 | }, | 438 | }; |
434 | 439 | ||
435 | /** | 440 | /** |
436 | * Fills a form with provided field values. | 441 | * Fills a form with provided field values. |
437 | * | 442 | * |
438 | * @param String selector A CSS3 selector to the target form to fill | 443 | * @param String selector A CSS3 selector to the target form to fill |
439 | * @param Object vals Field values | 444 | * @param Object vals Field values |
440 | * @param Boolean submit Submit the form? | 445 | * @param Boolean submit Submit the form? |
441 | */ | 446 | */ |
442 | fill: function(selector, vals, submit) { | 447 | Casper.prototype.fill = function(selector, vals, submit) { |
443 | submit = submit === true ? submit : false; | 448 | submit = submit === true ? submit : false; |
444 | if (!utils.isType(selector, "string") || !selector.length) { | 449 | if (!utils.isString(selector) || !selector.length) { |
445 | throw new Error("Form selector must be a non-empty string"); | 450 | throw new Error("Form selector must be a non-empty string"); |
446 | } | 451 | } |
447 | if (!utils.isType(vals, "object")) { | 452 | if (!utils.isObject(vals)) { |
448 | throw new Error("Form values must be provided as an object"); | 453 | throw new Error("Form values must be provided as an object"); |
449 | } | 454 | } |
455 | this.emit('casper.fill', selector, vals, submit); | ||
450 | var fillResults = this.evaluate(function(selector, values) { | 456 | var fillResults = this.evaluate(function(selector, values) { |
451 | return __utils__.fill(selector, values); | 457 | return __utils__.fill(selector, values); |
452 | }, { | 458 | }, { |
... | @@ -485,39 +491,40 @@ Casper.prototype = { | ... | @@ -485,39 +491,40 @@ Casper.prototype = { |
485 | form.submit(); | 491 | form.submit(); |
486 | }, { selector: selector }); | 492 | }, { selector: selector }); |
487 | } | 493 | } |
488 | }, | 494 | }; |
489 | 495 | ||
490 | /** | 496 | /** |
491 | * Go a step forward in browser's history | 497 | * Go a step forward in browser's history |
492 | * | 498 | * |
493 | * @return Casper | 499 | * @return Casper |
494 | */ | 500 | */ |
495 | forward: function(then) { | 501 | Casper.prototype.forward = function(then) { |
496 | return this.then(function(self) { | 502 | return this.then(function() { |
497 | self.evaluate(function() { | 503 | this.emit('casper.forward'); |
504 | this.evaluate(function() { | ||
498 | history.forward(); | 505 | history.forward(); |
499 | }); | 506 | }); |
500 | }); | 507 | }); |
501 | }, | 508 | }; |
502 | 509 | ||
503 | /** | 510 | /** |
504 | * Retrieves current document url. | 511 | * Retrieves current document url. |
505 | * | 512 | * |
506 | * @return String | 513 | * @return String |
507 | */ | 514 | */ |
508 | getCurrentUrl: function() { | 515 | Casper.prototype.getCurrentUrl = function() { |
509 | return decodeURIComponent(this.evaluate(function() { | 516 | return decodeURIComponent(this.evaluate(function() { |
510 | return document.location.href; | 517 | return document.location.href; |
511 | })); | 518 | })); |
512 | }, | 519 | }; |
513 | 520 | ||
514 | /** | 521 | /** |
515 | * Retrieves boundaries for a DOM element matching the provided CSS3 selector. | 522 | * Retrieves boundaries for a DOM element matching the provided CSS3 selector. |
516 | * | 523 | * |
517 | * @param String selector A CSS3 selector | 524 | * @param String selector A CSS3 selector |
518 | * @return Object | 525 | * @return Object |
519 | */ | 526 | */ |
520 | getElementBounds: function(selector) { | 527 | Casper.prototype.getElementBounds = function(selector) { |
521 | if (!this.exists(selector)) { | 528 | if (!this.exists(selector)) { |
522 | throw new Error("No element matching selector found: " + selector); | 529 | throw new Error("No element matching selector found: " + selector); |
523 | } | 530 | } |
... | @@ -528,15 +535,15 @@ Casper.prototype = { | ... | @@ -528,15 +535,15 @@ Casper.prototype = { |
528 | throw new Error('Could not fetch boundaries for element matching selector: ' + selector); | 535 | throw new Error('Could not fetch boundaries for element matching selector: ' + selector); |
529 | } | 536 | } |
530 | return clipRect; | 537 | return clipRect; |
531 | }, | 538 | }; |
532 | 539 | ||
533 | /** | 540 | /** |
534 | * Retrieves global variable. | 541 | * Retrieves global variable. |
535 | * | 542 | * |
536 | * @param String name The name of the global variable to retrieve | 543 | * @param String name The name of the global variable to retrieve |
537 | * @return mixed | 544 | * @return mixed |
538 | */ | 545 | */ |
539 | getGlobal: function(name) { | 546 | Casper.prototype.getGlobal = function(name) { |
540 | var result = this.evaluate(function(name) { | 547 | var result = this.evaluate(function(name) { |
541 | var result = {}; | 548 | var result = {}; |
542 | try { | 549 | try { |
... | @@ -550,25 +557,25 @@ Casper.prototype = { | ... | @@ -550,25 +557,25 @@ Casper.prototype = { |
550 | }, {'name': name}); | 557 | }, {'name': name}); |
551 | if ('error' in result) { | 558 | if ('error' in result) { |
552 | throw new Error(result.error); | 559 | throw new Error(result.error); |
553 | } else if (utils.isType(result.value, "string")) { | 560 | } else if (utils.isString(result.value)) { |
554 | return JSON.parse(result.value); | 561 | return JSON.parse(result.value); |
555 | } else { | 562 | } else { |
556 | return undefined; | 563 | return undefined; |
557 | } | 564 | } |
558 | }, | 565 | }; |
559 | 566 | ||
560 | /** | 567 | /** |
561 | * Retrieves current page title, if any. | 568 | * Retrieves current page title, if any. |
562 | * | 569 | * |
563 | * @return String | 570 | * @return String |
564 | */ | 571 | */ |
565 | getTitle: function() { | 572 | Casper.prototype.getTitle = function() { |
566 | return this.evaluate(function() { | 573 | return this.evaluate(function() { |
567 | return document.title; | 574 | return document.title; |
568 | }); | 575 | }); |
569 | }, | 576 | }; |
570 | 577 | ||
571 | /** | 578 | /** |
572 | * Logs a message. | 579 | * Logs a message. |
573 | * | 580 | * |
574 | * @param String message The message to log | 581 | * @param String message The message to log |
... | @@ -576,10 +583,10 @@ Casper.prototype = { | ... | @@ -576,10 +583,10 @@ Casper.prototype = { |
576 | * @param String space Space from where the logged event occured (default: "phantom") | 583 | * @param String space Space from where the logged event occured (default: "phantom") |
577 | * @return Casper | 584 | * @return Casper |
578 | */ | 585 | */ |
579 | log: function(message, level, space) { | 586 | Casper.prototype.log = function(message, level, space) { |
580 | level = level && this.logLevels.indexOf(level) > -1 ? level : "debug"; | 587 | level = level && this.logLevels.indexOf(level) > -1 ? level : "debug"; |
581 | space = space ? space : "phantom"; | 588 | space = space ? space : "phantom"; |
582 | if (level === "error" && utils.isType(this.options.onError, "function")) { | 589 | if (level === "error" && utils.isFunction(this.options.onError)) { |
583 | this.options.onError.call(this, this, message, space); | 590 | this.options.onError.call(this, this, message, space); |
584 | } | 591 | } |
585 | if (this.logLevels.indexOf(level) < this.logLevels.indexOf(this.options.logLevel)) { | 592 | if (this.logLevels.indexOf(level) < this.logLevels.indexOf(this.options.logLevel)) { |
... | @@ -591,7 +598,7 @@ Casper.prototype = { | ... | @@ -591,7 +598,7 @@ Casper.prototype = { |
591 | message: message, | 598 | message: message, |
592 | date: new Date().toString() | 599 | date: new Date().toString() |
593 | }; | 600 | }; |
594 | if (level in this.logFormats && utils.isType(this.logFormats[level], "function")) { | 601 | if (level in this.logFormats && utils.isFunction(this.logFormats[level])) { |
595 | message = this.logFormats[level](message, level, space); | 602 | message = this.logFormats[level](message, level, space); |
596 | } else { | 603 | } else { |
597 | var levelStr = this.colorizer.colorize('[' + level + ']', this.logStyles[level]); | 604 | var levelStr = this.colorizer.colorize('[' + level + ']', this.logStyles[level]); |
... | @@ -601,22 +608,23 @@ Casper.prototype = { | ... | @@ -601,22 +608,23 @@ Casper.prototype = { |
601 | this.echo(message); // direct output | 608 | this.echo(message); // direct output |
602 | } | 609 | } |
603 | this.result.log.push(entry); | 610 | this.result.log.push(entry); |
611 | this.emit('casper.log', entry); | ||
604 | return this; | 612 | return this; |
605 | }, | 613 | }; |
606 | 614 | ||
607 | /** | 615 | /** |
608 | * Emulates a click on an HTML element matching a given CSS3 selector, | 616 | * Emulates a click on an HTML element matching a given CSS3 selector, |
609 | * using the mouse pointer. | 617 | * using the mouse pointer. |
610 | * | 618 | * |
611 | * @param String selector A DOM CSS3 compatible selector | 619 | * @param String selector A DOM CSS3 compatible selector |
612 | * @return Casper | 620 | * @return Casper |
613 | */ | 621 | */ |
614 | mouseClick: function(selector) { | 622 | Casper.prototype.mouseClick = function(selector) { |
615 | this.mouse.click(selector); | 623 | this.mouse.click(selector); |
616 | return this; | 624 | return this; |
617 | }, | 625 | }; |
618 | 626 | ||
619 | /** | 627 | /** |
620 | * Opens a page. Takes only one argument, the url to open (using the | 628 | * Opens a page. Takes only one argument, the url to open (using the |
621 | * callback argument would defeat the whole purpose of Casper | 629 | * callback argument would defeat the whole purpose of Casper |
622 | * actually). | 630 | * actually). |
... | @@ -624,19 +632,25 @@ Casper.prototype = { | ... | @@ -624,19 +632,25 @@ Casper.prototype = { |
624 | * @param String location The url to open | 632 | * @param String location The url to open |
625 | * @return Casper | 633 | * @return Casper |
626 | */ | 634 | */ |
627 | open: function(location, options) { | 635 | Casper.prototype.open = function(location, options) { |
628 | options = utils.isType(options, "object") ? options : {}; | 636 | options = utils.isObject(options) ? options : {}; |
629 | this.requestUrl = location; | 637 | this.requestUrl = location; |
630 | // http auth | 638 | // http auth |
631 | var httpAuthMatch = location.match(/^https?:\/\/(.+):(.+)@/i); | 639 | var httpAuthMatch = location.match(/^https?:\/\/(.+):(.+)@/i); |
632 | if (httpAuthMatch) { | 640 | if (httpAuthMatch) { |
633 | this.setHttpAuth(httpAuthMatch[1], httpAuthMatch[2]); | 641 | var httpAuth = { |
642 | username: httpAuthMatch[1], | ||
643 | password: httpAuthMatch[2] | ||
644 | }; | ||
645 | this.emit('casper.http.auth', httpAuth); | ||
646 | this.setHttpAuth(httpAuth.username, httpAuth.password); | ||
634 | } | 647 | } |
648 | this.emit('casper.open', location); | ||
635 | this.page.open(location); | 649 | this.page.open(location); |
636 | return this; | 650 | return this; |
637 | }, | 651 | }; |
638 | 652 | ||
639 | /** | 653 | /** |
640 | * Repeats a step a given number of times. | 654 | * Repeats a step a given number of times. |
641 | * | 655 | * |
642 | * @param Number times Number of times to repeat step | 656 | * @param Number times Number of times to repeat step |
... | @@ -644,22 +658,22 @@ Casper.prototype = { | ... | @@ -644,22 +658,22 @@ Casper.prototype = { |
644 | * @return Casper | 658 | * @return Casper |
645 | * @see Casper#then | 659 | * @see Casper#then |
646 | */ | 660 | */ |
647 | repeat: function(times, then) { | 661 | Casper.prototype.repeat = function(times, then) { |
648 | for (var i = 0; i < times; i++) { | 662 | for (var i = 0; i < times; i++) { |
649 | this.then(then); | 663 | this.then(then); |
650 | } | 664 | } |
651 | return this; | 665 | return this; |
652 | }, | 666 | }; |
653 | 667 | ||
654 | /** | 668 | /** |
655 | * Checks if a given resource was loaded by the remote page. | 669 | * Checks if a given resource was loaded by the remote page. |
656 | * | 670 | * |
657 | * @param Function/String test A test function or string. In case a string is passed, url matching will be tested. | 671 | * @param Function/String test A test function or string. In case a string is passed, url matching will be tested. |
658 | * @return Boolean | 672 | * @return Boolean |
659 | */ | 673 | */ |
660 | resourceExists: function(test) { | 674 | Casper.prototype.resourceExists = function(test) { |
661 | var testFn; | 675 | var testFn; |
662 | if (utils.isType(test, "string")) { | 676 | if (utils.isString(test)) { |
663 | testFn = function (res) { | 677 | testFn = function (res) { |
664 | return res.url.search(test) !== -1; | 678 | return res.url.search(test) !== -1; |
665 | }; | 679 | }; |
... | @@ -667,42 +681,44 @@ Casper.prototype = { | ... | @@ -667,42 +681,44 @@ Casper.prototype = { |
667 | testFn = test; | 681 | testFn = test; |
668 | } | 682 | } |
669 | return this.resources.some(testFn); | 683 | return this.resources.some(testFn); |
670 | }, | 684 | }; |
671 | 685 | ||
672 | /** | 686 | /** |
673 | * Runs the whole suite of steps. | 687 | * Runs the whole suite of steps. |
674 | * | 688 | * |
675 | * @param function onComplete an optional callback | 689 | * @param function onComplete an optional callback |
676 | * @param Number time an optional amount of milliseconds for interval checking | 690 | * @param Number time an optional amount of milliseconds for interval checking |
677 | * @return Casper | 691 | * @return Casper |
678 | */ | 692 | */ |
679 | run: function(onComplete, time) { | 693 | Casper.prototype.run = function(onComplete, time) { |
680 | if (!this.steps || this.steps.length < 1) { | 694 | if (!this.steps || this.steps.length < 1) { |
681 | this.log("No steps defined, aborting", "error"); | 695 | this.log("No steps defined, aborting", "error"); |
682 | return this; | 696 | return this; |
683 | } | 697 | } |
684 | this.log("Running suite: " + this.steps.length + " step" + (this.steps.length > 1 ? "s" : ""), "info"); | 698 | this.log("Running suite: " + this.steps.length + " step" + (this.steps.length > 1 ? "s" : ""), "info"); |
699 | this.emit('casper.run'); | ||
685 | this.checker = setInterval(this.checkStep, (time ? time: 250), this, onComplete); | 700 | this.checker = setInterval(this.checkStep, (time ? time: 250), this, onComplete); |
686 | return this; | 701 | return this; |
687 | }, | 702 | }; |
688 | 703 | ||
689 | /** | 704 | /** |
690 | * Runs a step. | 705 | * Runs a step. |
691 | * | 706 | * |
692 | * @param Function step | 707 | * @param Function step |
693 | */ | 708 | */ |
694 | runStep: function(step) { | 709 | Casper.prototype.runStep = function(step) { |
695 | var skipLog = utils.isType(step.options, "object") && step.options.skipLog === true; | 710 | var skipLog = utils.isObject(step.options) && step.options.skipLog === true; |
696 | var stepInfo = "Step " + (this.step) + "/" + this.steps.length; | 711 | var stepInfo = "Step " + (this.step) + "/" + this.steps.length; |
697 | var stepResult; | 712 | var stepResult; |
698 | if (!skipLog) { | 713 | if (!skipLog) { |
699 | this.log(stepInfo + ' ' + this.getCurrentUrl() + ' (HTTP ' + this.currentHTTPStatus + ')', "info"); | 714 | this.log(stepInfo + ' ' + this.getCurrentUrl() + ' (HTTP ' + this.currentHTTPStatus + ')', "info"); |
700 | } | 715 | } |
701 | if (utils.isType(this.options.stepTimeout, "number") && this.options.stepTimeout > 0) { | 716 | if (utils.isNumber(this.options.stepTimeout) && this.options.stepTimeout > 0) { |
702 | var stepTimeoutCheckInterval = setInterval(function(self, start, stepNum) { | 717 | var stepTimeoutCheckInterval = setInterval(function(self, start, stepNum) { |
703 | if (new Date().getTime() - start > self.options.stepTimeout) { | 718 | if (new Date().getTime() - start > self.options.stepTimeout) { |
704 | if (self.step == stepNum) { | 719 | if (self.step == stepNum) { |
705 | if (utils.isType(self.options.onStepTimeout, "function")) { | 720 | self.emit('casper.step.timeout'); |
721 | if (utils.isFunction(self.options.onStepTimeout)) { | ||
706 | self.options.onStepTimeout.call(self, self); | 722 | self.options.onStepTimeout.call(self, self); |
707 | } else { | 723 | } else { |
708 | self.die("Maximum step execution timeout exceeded for step " + stepNum, "error"); | 724 | self.die("Maximum step execution timeout exceeded for step " + stepNum, "error"); |
... | @@ -712,6 +728,7 @@ Casper.prototype = { | ... | @@ -712,6 +728,7 @@ Casper.prototype = { |
712 | } | 728 | } |
713 | }, this.options.stepTimeout, this, new Date().getTime(), this.step); | 729 | }, this.options.stepTimeout, this, new Date().getTime(), this.step); |
714 | } | 730 | } |
731 | this.emit('casper.step.start', step); | ||
715 | try { | 732 | try { |
716 | stepResult = step.call(this, this); | 733 | stepResult = step.call(this, this); |
717 | } catch (e) { | 734 | } catch (e) { |
... | @@ -721,42 +738,44 @@ Casper.prototype = { | ... | @@ -721,42 +738,44 @@ Casper.prototype = { |
721 | throw e; | 738 | throw e; |
722 | } | 739 | } |
723 | } | 740 | } |
724 | if (utils.isType(this.options.onStepComplete, "function")) { | 741 | if (utils.isFunction(this.options.onStepComplete)) { |
725 | this.options.onStepComplete.call(this, this, stepResult); | 742 | this.options.onStepComplete.call(this, this, stepResult); |
726 | } | 743 | } |
727 | if (!skipLog) { | 744 | if (!skipLog) { |
745 | this.emit('casper.step.complete', stepResult); | ||
728 | this.log(stepInfo + ": done in " + (new Date().getTime() - this.startTime) + "ms.", "info"); | 746 | this.log(stepInfo + ": done in " + (new Date().getTime() - this.startTime) + "ms.", "info"); |
729 | } | 747 | } |
730 | }, | 748 | }; |
731 | 749 | ||
732 | /** | 750 | /** |
733 | * Sets HTTP authentication parameters. | 751 | * Sets HTTP authentication parameters. |
734 | * | 752 | * |
735 | * @param String username The HTTP_AUTH_USER value | 753 | * @param String username The HTTP_AUTH_USER value |
736 | * @param String password The HTTP_AUTH_PW value | 754 | * @param String password The HTTP_AUTH_PW value |
737 | * @return Casper | 755 | * @return Casper |
738 | */ | 756 | */ |
739 | setHttpAuth: function(username, password) { | 757 | Casper.prototype.setHttpAuth = function(username, password) { |
740 | if (!this.started) { | 758 | if (!this.started) { |
741 | throw new Error("Casper must be started in order to use the setHttpAuth() method"); | 759 | throw new Error("Casper must be started in order to use the setHttpAuth() method"); |
742 | } | 760 | } |
743 | if (!utils.isType(username, "string") || !utils.isType(password, "string")) { | 761 | if (!utils.isString(username) || !utils.isString(password)) { |
744 | throw new Error("Both username and password must be strings"); | 762 | throw new Error("Both username and password must be strings"); |
745 | } | 763 | } |
746 | this.page.settings.userName = username; | 764 | this.page.settings.userName = username; |
747 | this.page.settings.password = password; | 765 | this.page.settings.password = password; |
748 | this.log("Setting HTTP authentication for user " + username, "info"); | 766 | this.log("Setting HTTP authentication for user " + username, "info"); |
749 | return this; | 767 | return this; |
750 | }, | 768 | }; |
751 | 769 | ||
752 | /** | 770 | /** |
753 | * Configures and starts Casper. | 771 | * Configures and starts Casper. |
754 | * | 772 | * |
755 | * @param String location An optional location to open on start | 773 | * @param String location An optional location to open on start |
756 | * @param function then Next step function to execute on page loaded (optional) | 774 | * @param function then Next step function to execute on page loaded (optional) |
757 | * @return Casper | 775 | * @return Casper |
758 | */ | 776 | */ |
759 | start: function(location, then) { | 777 | Casper.prototype.start = function(location, then) { |
778 | this.emit('casper.starting'); | ||
760 | this.log('Starting...', "info"); | 779 | this.log('Starting...', "info"); |
761 | this.startTime = new Date().getTime(); | 780 | this.startTime = new Date().getTime(); |
762 | this.history = []; | 781 | this.history = []; |
... | @@ -776,46 +795,49 @@ Casper.prototype = { | ... | @@ -776,46 +795,49 @@ Casper.prototype = { |
776 | } | 795 | } |
777 | } | 796 | } |
778 | this.page.settings = utils.mergeObjects(this.page.settings, this.options.pageSettings); | 797 | this.page.settings = utils.mergeObjects(this.page.settings, this.options.pageSettings); |
779 | if (utils.isType(this.options.clipRect, "object")) { | 798 | if (utils.isClipRect(this.options.clipRect)) { |
780 | this.page.clipRect = this.options.clipRect; | 799 | this.page.clipRect = this.options.clipRect; |
781 | } | 800 | } |
782 | if (utils.isType(this.options.viewportSize, "object")) { | 801 | if (utils.isObject(this.options.viewportSize)) { |
783 | this.page.viewportSize = this.options.viewportSize; | 802 | this.page.viewportSize = this.options.viewportSize; |
784 | } | 803 | } |
785 | this.started = true; | 804 | this.started = true; |
786 | if (utils.isType(this.options.timeout, "number") && this.options.timeout > 0) { | 805 | this.emit('casper.started'); |
806 | if (utils.isNumber(this.options.timeout) && this.options.timeout > 0) { | ||
787 | this.log("Execution timeout set to " + this.options.timeout + 'ms', "info"); | 807 | this.log("Execution timeout set to " + this.options.timeout + 'ms', "info"); |
788 | setTimeout(function(self) { | 808 | setTimeout(function(self) { |
789 | if (utils.isType(self.options.onTimeout, "function")) { | 809 | self.emit('casper.timeout'); |
810 | if (utils.isFunction(self.options.onTimeout)) { | ||
790 | self.options.onTimeout.call(self, self); | 811 | self.options.onTimeout.call(self, self); |
791 | } else { | 812 | } else { |
792 | self.die("Timeout of " + self.options.timeout + "ms exceeded, exiting."); | 813 | self.die("Timeout of " + self.options.timeout + "ms exceeded, exiting."); |
793 | } | 814 | } |
794 | }, this.options.timeout, this); | 815 | }, this.options.timeout, this); |
795 | } | 816 | } |
796 | if (utils.isType(this.options.onPageInitialized, "function")) { | 817 | casper.emit('casper.page.initialized'); |
818 | if (utils.isFunction(this.options.onPageInitialized)) { | ||
797 | this.log("Post-configuring WebPage instance", "debug"); | 819 | this.log("Post-configuring WebPage instance", "debug"); |
798 | this.options.onPageInitialized.call(this, this.page); | 820 | this.options.onPageInitialized.call(this, this.page); |
799 | } | 821 | } |
800 | if (utils.isType(location, "string") && location.length > 0) { | 822 | if (utils.isString(location) && location.length > 0) { |
801 | return this.thenOpen(location, utils.isType(then, "function") ? then : this.createStep(function(self) { | 823 | return this.thenOpen(location, utils.isFunction(then) ? then : this.createStep(function(self) { |
802 | self.log("start page is loaded", "debug"); | 824 | self.log("start page is loaded", "debug"); |
803 | })); | 825 | })); |
804 | } | 826 | } |
805 | return this; | 827 | return this; |
806 | }, | 828 | }; |
807 | 829 | ||
808 | /** | 830 | /** |
809 | * Schedules the next step in the navigation process. | 831 | * Schedules the next step in the navigation process. |
810 | * | 832 | * |
811 | * @param function step A function to be called as a step | 833 | * @param function step A function to be called as a step |
812 | * @return Casper | 834 | * @return Casper |
813 | */ | 835 | */ |
814 | then: function(step) { | 836 | Casper.prototype.then = function(step) { |
815 | if (!this.started) { | 837 | if (!this.started) { |
816 | throw new Error("Casper not started; please use Casper#start"); | 838 | throw new Error("Casper not started; please use Casper#start"); |
817 | } | 839 | } |
818 | if (!utils.isType(step, "function")) { | 840 | if (!utils.isFunction(step)) { |
819 | throw new Error("You can only define a step as a function"); | 841 | throw new Error("You can only define a step as a function"); |
820 | } | 842 | } |
821 | // check if casper is running | 843 | // check if casper is running |
... | @@ -836,10 +858,11 @@ Casper.prototype = { | ... | @@ -836,10 +858,11 @@ Casper.prototype = { |
836 | } | 858 | } |
837 | this.steps.splice(insertIndex, 0, step); | 859 | this.steps.splice(insertIndex, 0, step); |
838 | } | 860 | } |
861 | this.emit('casper.step.added', step); | ||
839 | return this; | 862 | return this; |
840 | }, | 863 | }; |
841 | 864 | ||
842 | /** | 865 | /** |
843 | * Adds a new navigation step for clicking on a provided link selector | 866 | * Adds a new navigation step for clicking on a provided link selector |
844 | * and execute an optional next step. | 867 | * and execute an optional next step. |
845 | * | 868 | * |
... | @@ -850,14 +873,14 @@ Casper.prototype = { | ... | @@ -850,14 +873,14 @@ Casper.prototype = { |
850 | * @see Casper#click | 873 | * @see Casper#click |
851 | * @see Casper#then | 874 | * @see Casper#then |
852 | */ | 875 | */ |
853 | thenClick: function(selector, then, fallbackToHref) { | 876 | Casper.prototype.thenClick = function(selector, then, fallbackToHref) { |
854 | this.then(function(self) { | 877 | this.then(function(self) { |
855 | self.click(selector, fallbackToHref); | 878 | self.click(selector, fallbackToHref); |
856 | }); | 879 | }); |
857 | return utils.isType(then, "function") ? this.then(then) : this; | 880 | return utils.isFunction(then) ? this.then(then) : this; |
858 | }, | 881 | }; |
859 | 882 | ||
860 | /** | 883 | /** |
861 | * Adds a new navigation step to perform code evaluation within the | 884 | * Adds a new navigation step to perform code evaluation within the |
862 | * current retrieved page DOM. | 885 | * current retrieved page DOM. |
863 | * | 886 | * |
... | @@ -866,13 +889,13 @@ Casper.prototype = { | ... | @@ -866,13 +889,13 @@ Casper.prototype = { |
866 | * @return Casper | 889 | * @return Casper |
867 | * @see Casper#evaluate | 890 | * @see Casper#evaluate |
868 | */ | 891 | */ |
869 | thenEvaluate: function(fn, context) { | 892 | Casper.prototype.thenEvaluate = function(fn, context) { |
870 | return this.then(function(self) { | 893 | return this.then(function(self) { |
871 | self.evaluate(fn, context); | 894 | self.evaluate(fn, context); |
872 | }); | 895 | }); |
873 | }, | 896 | }; |
874 | 897 | ||
875 | /** | 898 | /** |
876 | * Adds a new navigation step for opening the provided location. | 899 | * Adds a new navigation step for opening the provided location. |
877 | * | 900 | * |
878 | * @param String location The URL to load | 901 | * @param String location The URL to load |
... | @@ -880,16 +903,16 @@ Casper.prototype = { | ... | @@ -880,16 +903,16 @@ Casper.prototype = { |
880 | * @return Casper | 903 | * @return Casper |
881 | * @see Casper#open | 904 | * @see Casper#open |
882 | */ | 905 | */ |
883 | thenOpen: function(location, then) { | 906 | Casper.prototype.thenOpen = function(location, then) { |
884 | this.then(this.createStep(function(self) { | 907 | this.then(this.createStep(function(self) { |
885 | self.open(location); | 908 | self.open(location); |
886 | }, { | 909 | }, { |
887 | skipLog: true | 910 | skipLog: true |
888 | })); | 911 | })); |
889 | return utils.isType(then, "function") ? this.then(then) : this; | 912 | return utils.isFunction(then) ? this.then(then) : this; |
890 | }, | 913 | }; |
891 | 914 | ||
892 | /** | 915 | /** |
893 | * Adds a new navigation step for opening and evaluate an expression | 916 | * Adds a new navigation step for opening and evaluate an expression |
894 | * against the DOM retrieved from the provided location. | 917 | * against the DOM retrieved from the provided location. |
895 | * | 918 | * |
... | @@ -900,32 +923,33 @@ Casper.prototype = { | ... | @@ -900,32 +923,33 @@ Casper.prototype = { |
900 | * @see Casper#evaluate | 923 | * @see Casper#evaluate |
901 | * @see Casper#open | 924 | * @see Casper#open |
902 | */ | 925 | */ |
903 | thenOpenAndEvaluate: function(location, fn, context) { | 926 | Casper.prototype.thenOpenAndEvaluate = function(location, fn, context) { |
904 | return this.thenOpen(location).thenEvaluate(fn, context); | 927 | return this.thenOpen(location).thenEvaluate(fn, context); |
905 | }, | 928 | }; |
906 | 929 | ||
907 | /** | 930 | /** |
908 | * Changes the current viewport size. | 931 | * Changes the current viewport size. |
909 | * | 932 | * |
910 | * @param Number width The viewport width, in pixels | 933 | * @param Number width The viewport width, in pixels |
911 | * @param Number height The viewport height, in pixels | 934 | * @param Number height The viewport height, in pixels |
912 | * @return Casper | 935 | * @return Casper |
913 | */ | 936 | */ |
914 | viewport: function(width, height) { | 937 | Casper.prototype.viewport = function(width, height) { |
915 | if (!this.started) { | 938 | if (!this.started) { |
916 | throw new Error("Casper must be started in order to set viewport at runtime"); | 939 | throw new Error("Casper must be started in order to set viewport at runtime"); |
917 | } | 940 | } |
918 | if (!utils.isType(width, "number") || !utils.isType(height, "number") || width <= 0 || height <= 0) { | 941 | if (!utils.isNumber(width) || !utils.isNumber(height) || width <= 0 || height <= 0) { |
919 | throw new Error("Invalid viewport width/height set: " + width + 'x' + height); | 942 | throw new Error("Invalid viewport width/height set: " + width + 'x' + height); |
920 | } | 943 | } |
921 | this.page.viewportSize = { | 944 | this.page.viewportSize = { |
922 | width: width, | 945 | width: width, |
923 | height: height | 946 | height: height |
924 | }; | 947 | }; |
948 | this.emit('casper.viewport.changed', [width, height]); | ||
925 | return this; | 949 | return this; |
926 | }, | 950 | }; |
927 | 951 | ||
928 | /** | 952 | /** |
929 | * Adds a new step that will wait for a given amount of time (expressed | 953 | * Adds a new step that will wait for a given amount of time (expressed |
930 | * in milliseconds) before processing an optional next one. | 954 | * in milliseconds) before processing an optional next one. |
931 | * | 955 | * |
... | @@ -933,12 +957,12 @@ Casper.prototype = { | ... | @@ -933,12 +957,12 @@ Casper.prototype = { |
933 | * @param Function then Next step to process (optional) | 957 | * @param Function then Next step to process (optional) |
934 | * @return Casper | 958 | * @return Casper |
935 | */ | 959 | */ |
936 | wait: function(timeout, then) { | 960 | Casper.prototype.wait = function(timeout, then) { |
937 | timeout = Number(timeout, 10); | 961 | timeout = Number(timeout, 10); |
938 | if (!utils.isType(timeout, "number") || timeout < 1) { | 962 | if (!utils.isNumber(timeout) || timeout < 1) { |
939 | this.die("wait() only accepts a positive integer > 0 as a timeout value"); | 963 | this.die("wait() only accepts a positive integer > 0 as a timeout value"); |
940 | } | 964 | } |
941 | if (then && !utils.isType(then, "function")) { | 965 | if (then && !utils.isFunction(then)) { |
942 | this.die("wait() a step definition must be a function"); | 966 | this.die("wait() a step definition must be a function"); |
943 | } | 967 | } |
944 | return this.then(function(self) { | 968 | return this.then(function(self) { |
... | @@ -951,17 +975,19 @@ Casper.prototype = { | ... | @@ -951,17 +975,19 @@ Casper.prototype = { |
951 | self.waitDone(); | 975 | self.waitDone(); |
952 | }, timeout); | 976 | }, timeout); |
953 | }); | 977 | }); |
954 | }, | 978 | }; |
955 | 979 | ||
956 | waitStart: function() { | 980 | Casper.prototype.waitStart = function() { |
981 | this.emit('casper.wait.start'); | ||
957 | this.pendingWait = true; | 982 | this.pendingWait = true; |
958 | }, | 983 | }; |
959 | 984 | ||
960 | waitDone: function() { | 985 | Casper.prototype.waitDone = function() { |
986 | this.emit('casper.wait.done'); | ||
961 | this.pendingWait = false; | 987 | this.pendingWait = false; |
962 | }, | 988 | }; |
963 | 989 | ||
964 | /** | 990 | /** |
965 | * Waits until a function returns true to process a next step. | 991 | * Waits until a function returns true to process a next step. |
966 | * | 992 | * |
967 | * @param Function testFx A function to be evaluated for returning condition satisfecit | 993 | * @param Function testFx A function to be evaluated for returning condition satisfecit |
... | @@ -970,12 +996,12 @@ Casper.prototype = { | ... | @@ -970,12 +996,12 @@ Casper.prototype = { |
970 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | 996 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) |
971 | * @return Casper | 997 | * @return Casper |
972 | */ | 998 | */ |
973 | waitFor: function(testFx, then, onTimeout, timeout) { | 999 | Casper.prototype.waitFor = function(testFx, then, onTimeout, timeout) { |
974 | timeout = timeout ? timeout : this.defaultWaitTimeout; | 1000 | timeout = timeout ? timeout : this.defaultWaitTimeout; |
975 | if (!utils.isType(testFx, "function")) { | 1001 | if (!utils.isFunction(testFx)) { |
976 | this.die("waitFor() needs a test function"); | 1002 | this.die("waitFor() needs a test function"); |
977 | } | 1003 | } |
978 | if (then && !utils.isType(then, "function")) { | 1004 | if (then && !utils.isFunction(then)) { |
979 | this.die("waitFor() next step definition must be a function"); | 1005 | this.die("waitFor() next step definition must be a function"); |
980 | } | 1006 | } |
981 | return this.then(function(self) { | 1007 | return this.then(function(self) { |
... | @@ -989,7 +1015,8 @@ Casper.prototype = { | ... | @@ -989,7 +1015,8 @@ Casper.prototype = { |
989 | self.waitDone(); | 1015 | self.waitDone(); |
990 | if (!condition) { | 1016 | if (!condition) { |
991 | self.log("Casper.waitFor() timeout", "warning"); | 1017 | self.log("Casper.waitFor() timeout", "warning"); |
992 | if (utils.isType(onTimeout, "function")) { | 1018 | self.emit('casper.waitFor.timeout'); |
1019 | if (utils.isFunction(onTimeout)) { | ||
993 | onTimeout.call(self, self); | 1020 | onTimeout.call(self, self); |
994 | } else { | 1021 | } else { |
995 | self.die("Timeout of " + timeout + "ms expired, exiting.", "error"); | 1022 | self.die("Timeout of " + timeout + "ms expired, exiting.", "error"); |
... | @@ -1005,9 +1032,9 @@ Casper.prototype = { | ... | @@ -1005,9 +1032,9 @@ Casper.prototype = { |
1005 | } | 1032 | } |
1006 | }, 100, self, testFx, timeout, onTimeout); | 1033 | }, 100, self, testFx, timeout, onTimeout); |
1007 | }); | 1034 | }); |
1008 | }, | 1035 | }; |
1009 | 1036 | ||
1010 | /** | 1037 | /** |
1011 | * Waits until a given resource is loaded | 1038 | * Waits until a given resource is loaded |
1012 | * | 1039 | * |
1013 | * @param String/Function test A function to test if the resource exists. A string will be matched against the resources url. | 1040 | * @param String/Function test A function to test if the resource exists. A string will be matched against the resources url. |
... | @@ -1016,14 +1043,14 @@ Casper.prototype = { | ... | @@ -1016,14 +1043,14 @@ Casper.prototype = { |
1016 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | 1043 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) |
1017 | * @return Casper | 1044 | * @return Casper |
1018 | */ | 1045 | */ |
1019 | waitForResource: function(test, then, onTimeout, timeout) { | 1046 | Casper.prototype.waitForResource = function(test, then, onTimeout, timeout) { |
1020 | timeout = timeout ? timeout : this.defaultWaitTimeout; | 1047 | timeout = timeout ? timeout : this.defaultWaitTimeout; |
1021 | return this.waitFor(function(self) { | 1048 | return this.waitFor(function(self) { |
1022 | return self.resourceExists(test); | 1049 | return self.resourceExists(test); |
1023 | }, then, onTimeout, timeout); | 1050 | }, then, onTimeout, timeout); |
1024 | }, | 1051 | }; |
1025 | 1052 | ||
1026 | /** | 1053 | /** |
1027 | * Waits until an element matching the provided CSS3 selector exists in | 1054 | * Waits until an element matching the provided CSS3 selector exists in |
1028 | * remote DOM to process a next step. | 1055 | * remote DOM to process a next step. |
1029 | * | 1056 | * |
... | @@ -1033,14 +1060,14 @@ Casper.prototype = { | ... | @@ -1033,14 +1060,14 @@ Casper.prototype = { |
1033 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | 1060 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) |
1034 | * @return Casper | 1061 | * @return Casper |
1035 | */ | 1062 | */ |
1036 | waitForSelector: function(selector, then, onTimeout, timeout) { | 1063 | Casper.prototype.waitForSelector = function(selector, then, onTimeout, timeout) { |
1037 | timeout = timeout ? timeout : this.defaultWaitTimeout; | 1064 | timeout = timeout ? timeout : this.defaultWaitTimeout; |
1038 | return this.waitFor(function(self) { | 1065 | return this.waitFor(function(self) { |
1039 | return self.exists(selector); | 1066 | return self.exists(selector); |
1040 | }, then, onTimeout, timeout); | 1067 | }, then, onTimeout, timeout); |
1041 | }, | 1068 | }; |
1042 | 1069 | ||
1043 | /** | 1070 | /** |
1044 | * Waits until an element matching the provided CSS3 selector does not | 1071 | * Waits until an element matching the provided CSS3 selector does not |
1045 | * exist in the remote DOM to process a next step. | 1072 | * exist in the remote DOM to process a next step. |
1046 | * | 1073 | * |
... | @@ -1050,14 +1077,14 @@ Casper.prototype = { | ... | @@ -1050,14 +1077,14 @@ Casper.prototype = { |
1050 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | 1077 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) |
1051 | * @return Casper | 1078 | * @return Casper |
1052 | */ | 1079 | */ |
1053 | waitWhileSelector: function(selector, then, onTimeout, timeout) { | 1080 | Casper.prototype.waitWhileSelector = function(selector, then, onTimeout, timeout) { |
1054 | timeout = timeout ? timeout : this.defaultWaitTimeout; | 1081 | timeout = timeout ? timeout : this.defaultWaitTimeout; |
1055 | return this.waitFor(function(self) { | 1082 | return this.waitFor(function(self) { |
1056 | return !self.exists(selector); | 1083 | return !self.exists(selector); |
1057 | }, then, onTimeout, timeout); | 1084 | }, then, onTimeout, timeout); |
1058 | }, | 1085 | }; |
1059 | 1086 | ||
1060 | /** | 1087 | /** |
1061 | * Waits until an element matching the provided CSS3 selector is | 1088 | * Waits until an element matching the provided CSS3 selector is |
1062 | * visible in the remote DOM to process a next step. | 1089 | * visible in the remote DOM to process a next step. |
1063 | * | 1090 | * |
... | @@ -1067,14 +1094,14 @@ Casper.prototype = { | ... | @@ -1067,14 +1094,14 @@ Casper.prototype = { |
1067 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | 1094 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) |
1068 | * @return Casper | 1095 | * @return Casper |
1069 | */ | 1096 | */ |
1070 | waitUntilVisible: function(selector, then, onTimeout, timeout) { | 1097 | Casper.prototype.waitUntilVisible = function(selector, then, onTimeout, timeout) { |
1071 | timeout = timeout ? timeout : this.defaultWaitTimeout; | 1098 | timeout = timeout ? timeout : this.defaultWaitTimeout; |
1072 | return this.waitFor(function(self) { | 1099 | return this.waitFor(function(self) { |
1073 | return self.visible(selector); | 1100 | return self.visible(selector); |
1074 | }, then, onTimeout, timeout); | 1101 | }, then, onTimeout, timeout); |
1075 | }, | 1102 | }; |
1076 | 1103 | ||
1077 | /** | 1104 | /** |
1078 | * Waits until an element matching the provided CSS3 selector is no | 1105 | * Waits until an element matching the provided CSS3 selector is no |
1079 | * longer visible in remote DOM to process a next step. | 1106 | * longer visible in remote DOM to process a next step. |
1080 | * | 1107 | * |
... | @@ -1084,12 +1111,11 @@ Casper.prototype = { | ... | @@ -1084,12 +1111,11 @@ Casper.prototype = { |
1084 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) | 1111 | * @param Number timeout The max amount of time to wait, in milliseconds (optional) |
1085 | * @return Casper | 1112 | * @return Casper |
1086 | */ | 1113 | */ |
1087 | waitWhileVisible: function(selector, then, onTimeout, timeout) { | 1114 | Casper.prototype.waitWhileVisible = function(selector, then, onTimeout, timeout) { |
1088 | timeout = timeout ? timeout : this.defaultWaitTimeout; | 1115 | timeout = timeout ? timeout : this.defaultWaitTimeout; |
1089 | return this.waitFor(function(self) { | 1116 | return this.waitFor(function(self) { |
1090 | return !self.visible(selector); | 1117 | return !self.visible(selector); |
1091 | }, then, onTimeout, timeout); | 1118 | }, then, onTimeout, timeout); |
1092 | } | ||
1093 | }; | 1119 | }; |
1094 | 1120 | ||
1095 | /** | 1121 | /** |
... | @@ -1098,7 +1124,7 @@ Casper.prototype = { | ... | @@ -1098,7 +1124,7 @@ Casper.prototype = { |
1098 | * @param Object proto Prototype methods to add to Casper | 1124 | * @param Object proto Prototype methods to add to Casper |
1099 | */ | 1125 | */ |
1100 | Casper.extend = function(proto) { | 1126 | Casper.extend = function(proto) { |
1101 | if (!utils.isType(proto, "object")) { | 1127 | if (!utils.isObject(proto)) { |
1102 | throw new Error("extends() only accept objects as prototypes"); | 1128 | throw new Error("extends() only accept objects as prototypes"); |
1103 | } | 1129 | } |
1104 | utils.mergeObjects(Casper.prototype, proto); | 1130 | utils.mergeObjects(Casper.prototype, proto); |
... | @@ -1114,14 +1140,15 @@ exports.Casper = Casper; | ... | @@ -1114,14 +1140,15 @@ exports.Casper = Casper; |
1114 | */ | 1140 | */ |
1115 | function createPage(casper) { | 1141 | function createPage(casper) { |
1116 | var page; | 1142 | var page; |
1117 | if (phantom.version.major <= 1 && phantom.version.minor < 3 && utils.isType(require, "function")) { | 1143 | if (phantom.version.major <= 1 && phantom.version.minor < 3 && utils.isFunction(require)) { |
1118 | page = new WebPage(); | 1144 | page = new WebPage(); |
1119 | } else { | 1145 | } else { |
1120 | page = require('webpage').create(); | 1146 | page = require('webpage').create(); |
1121 | } | 1147 | } |
1122 | page.onAlert = function(message) { | 1148 | page.onAlert = function(message) { |
1123 | casper.log('[alert] ' + message, "info", "remote"); | 1149 | casper.log('[alert] ' + message, "info", "remote"); |
1124 | if (utils.isType(casper.options.onAlert, "function")) { | 1150 | casper.emit('casper.remote.alert', message); |
1151 | if (utils.isFunction(casper.options.onAlert)) { | ||
1125 | casper.options.onAlert.call(casper, casper, message); | 1152 | casper.options.onAlert.call(casper, casper, message); |
1126 | } | 1153 | } |
1127 | }; | 1154 | }; |
... | @@ -1132,25 +1159,32 @@ function createPage(casper) { | ... | @@ -1132,25 +1159,32 @@ function createPage(casper) { |
1132 | msg = test[2]; | 1159 | msg = test[2]; |
1133 | } | 1160 | } |
1134 | casper.log(msg, level, "remote"); | 1161 | casper.log(msg, level, "remote"); |
1162 | casper.emit('casper.remote.message', msg); | ||
1135 | }; | 1163 | }; |
1136 | page.onLoadStarted = function() { | 1164 | page.onLoadStarted = function() { |
1137 | casper.resources = []; | ||
1138 | casper.loadInProgress = true; | 1165 | casper.loadInProgress = true; |
1166 | casper.resources = []; | ||
1167 | casper.emit('casper.load.started'); | ||
1139 | }; | 1168 | }; |
1140 | page.onLoadFinished = function(status) { | 1169 | page.onLoadFinished = function(status) { |
1141 | if (status !== "success") { | 1170 | if (status !== "success") { |
1171 | casper.emit('casper.load.failed', { | ||
1172 | status: status, | ||
1173 | http_status: casper.currentHTTPStatus, | ||
1174 | url: casper.requestUrl | ||
1175 | }); | ||
1142 | var message = 'Loading resource failed with status=' + status; | 1176 | var message = 'Loading resource failed with status=' + status; |
1143 | if (casper.currentHTTPStatus) { | 1177 | if (casper.currentHTTPStatus) { |
1144 | message += ' (HTTP ' + casper.currentHTTPStatus + ')'; | 1178 | message += ' (HTTP ' + casper.currentHTTPStatus + ')'; |
1145 | } | 1179 | } |
1146 | message += ': ' + casper.requestUrl; | 1180 | message += ': ' + casper.requestUrl; |
1147 | casper.log(message, "warning"); | 1181 | casper.log(message, "warning"); |
1148 | if (utils.isType(casper.options.onLoadError, "function")) { | 1182 | if (utils.isFunction(casper.options.onLoadError)) { |
1149 | casper.options.onLoadError.call(casper, casper, casper.requestUrl, status); | 1183 | casper.options.onLoadError.call(casper, casper, casper.requestUrl, status); |
1150 | } | 1184 | } |
1151 | } | 1185 | } |
1152 | if (casper.options.clientScripts) { | 1186 | if (casper.options.clientScripts) { |
1153 | if (!utils.isType(casper.options.clientScripts, "array")) { | 1187 | if (!utils.isArray(casper.options.clientScripts)) { |
1154 | casper.log("The clientScripts option must be an array", "error"); | 1188 | casper.log("The clientScripts option must be an array", "error"); |
1155 | } else { | 1189 | } else { |
1156 | for (var i = 0; i < casper.options.clientScripts.length; i++) { | 1190 | for (var i = 0; i < casper.options.clientScripts.length; i++) { |
... | @@ -1172,10 +1206,12 @@ function createPage(casper) { | ... | @@ -1172,10 +1206,12 @@ function createPage(casper) { |
1172 | } | 1206 | } |
1173 | // history | 1207 | // history |
1174 | casper.history.push(casper.getCurrentUrl()); | 1208 | casper.history.push(casper.getCurrentUrl()); |
1209 | casper.emit('casper.load.finished', status); | ||
1175 | casper.loadInProgress = false; | 1210 | casper.loadInProgress = false; |
1176 | }; | 1211 | }; |
1177 | page.onResourceReceived = function(resource) { | 1212 | page.onResourceReceived = function(resource) { |
1178 | if (utils.isType(casper.options.onResourceReceived, "function")) { | 1213 | casper.emit('casper.resource.received', resource); |
1214 | if (utils.isFunction(casper.options.onResourceReceived)) { | ||
1179 | casper.options.onResourceReceived.call(casper, casper, resource); | 1215 | casper.options.onResourceReceived.call(casper, casper, resource); |
1180 | } | 1216 | } |
1181 | if (resource.stage === "end") { | 1217 | if (resource.stage === "end") { |
... | @@ -1183,18 +1219,21 @@ function createPage(casper) { | ... | @@ -1183,18 +1219,21 @@ function createPage(casper) { |
1183 | } | 1219 | } |
1184 | if (resource.url === casper.requestUrl && resource.stage === "start") { | 1220 | if (resource.url === casper.requestUrl && resource.stage === "start") { |
1185 | casper.currentHTTPStatus = resource.status; | 1221 | casper.currentHTTPStatus = resource.status; |
1186 | if (utils.isType(casper.options.httpStatusHandlers, "object") && | 1222 | casper.emit('casper.http.status.' + resource.status, resource); |
1223 | if (utils.isObject(casper.options.httpStatusHandlers) && | ||
1187 | resource.status in casper.options.httpStatusHandlers && | 1224 | resource.status in casper.options.httpStatusHandlers && |
1188 | utils.isType(casper.options.httpStatusHandlers[resource.status], "function")) { | 1225 | utils.isFunction(casper.options.httpStatusHandlers[resource.status])) { |
1189 | casper.options.httpStatusHandlers[resource.status].call(casper, casper, resource); | 1226 | casper.options.httpStatusHandlers[resource.status].call(casper, casper, resource); |
1190 | } | 1227 | } |
1191 | casper.currentUrl = resource.url; | 1228 | casper.currentUrl = resource.url; |
1192 | } | 1229 | } |
1193 | }; | 1230 | }; |
1194 | page.onResourceRequested = function(request) { | 1231 | page.onResourceRequested = function(request) { |
1195 | if (utils.isType(casper.options.onResourceRequested, "function")) { | 1232 | casper.emit('casper.resource.requested', request); |
1233 | if (utils.isFunction(casper.options.onResourceRequested)) { | ||
1196 | casper.options.onResourceRequested.call(casper, casper, request); | 1234 | casper.options.onResourceRequested.call(casper, casper, request); |
1197 | } | 1235 | } |
1198 | }; | 1236 | }; |
1237 | casper.emit('casper.page.created', page); | ||
1199 | return page; | 1238 | return page; |
1200 | } | 1239 | } | ... | ... |
... | @@ -70,7 +70,7 @@ function fileExt(file) { | ... | @@ -70,7 +70,7 @@ function fileExt(file) { |
70 | exports.fileExt = fileExt; | 70 | exports.fileExt = fileExt; |
71 | 71 | ||
72 | /** | 72 | /** |
73 | * Takes a string and append blank until the pad value is reached. | 73 | * Takes a string and append blanks until the pad value is reached. |
74 | * | 74 | * |
75 | * @param String text | 75 | * @param String text |
76 | * @param Number pad Pad value (optional; default: 80) | 76 | * @param Number pad Pad value (optional; default: 80) |
... | @@ -85,8 +85,14 @@ function fillBlanks(text, pad) { | ... | @@ -85,8 +85,14 @@ function fillBlanks(text, pad) { |
85 | } | 85 | } |
86 | exports.fillBlanks = fillBlanks; | 86 | exports.fillBlanks = fillBlanks; |
87 | 87 | ||
88 | /** | ||
89 | * Checks if value is a javascript Array | ||
90 | * | ||
91 | * @param mixed value | ||
92 | * @return Boolean | ||
93 | */ | ||
88 | function isArray(value) { | 94 | function isArray(value) { |
89 | return isType(value, "array"); | 95 | return Array.isArray(value) || isType(value, "array"); |
90 | } | 96 | } |
91 | exports.isArray = isArray; | 97 | exports.isArray = isArray; |
92 | 98 | ||
... | @@ -101,17 +107,27 @@ function isCasperObject(value) { | ... | @@ -101,17 +107,27 @@ function isCasperObject(value) { |
101 | } | 107 | } |
102 | exports.isCasperObject = isCasperObject; | 108 | exports.isCasperObject = isCasperObject; |
103 | 109 | ||
110 | /** | ||
111 | * Checks if value is a phantomjs clipRect-compatible object | ||
112 | * | ||
113 | * @param mixed value | ||
114 | * @return Boolean | ||
115 | */ | ||
104 | function isClipRect(value) { | 116 | function isClipRect(value) { |
105 | return isType(value, "cliprect") || ( | 117 | return isType(value, "cliprect") || ( |
106 | isType(value, "object") && | 118 | isObject(value) && |
107 | isType(value.top, "number") && | 119 | isNumber(value.top) && isNumber(value.left) && |
108 | isType(value.left, "number") && | 120 | isNumber(value.width) && isNumber(value.height) |
109 | isType(value.width, "number") && | ||
110 | isType(value.height, "number") | ||
111 | ); | 121 | ); |
112 | } | 122 | } |
113 | exports.isClipRect = isClipRect; | 123 | exports.isClipRect = isClipRect; |
114 | 124 | ||
125 | /** | ||
126 | * Checks if value is a javascript Function | ||
127 | * | ||
128 | * @param mixed value | ||
129 | * @return Boolean | ||
130 | */ | ||
115 | function isFunction(value) { | 131 | function isFunction(value) { |
116 | return isType(value, "function"); | 132 | return isType(value, "function"); |
117 | } | 133 | } |
... | @@ -125,15 +141,38 @@ exports.isFunction = isFunction; | ... | @@ -125,15 +141,38 @@ exports.isFunction = isFunction; |
125 | */ | 141 | */ |
126 | function isJsFile(file) { | 142 | function isJsFile(file) { |
127 | var ext = fileExt(file); | 143 | var ext = fileExt(file); |
128 | return isType(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1; | 144 | return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1; |
129 | } | 145 | } |
130 | exports.isJsFile = isJsFile; | 146 | exports.isJsFile = isJsFile; |
131 | 147 | ||
148 | /** | ||
149 | * Checks if value is a javascript Number | ||
150 | * | ||
151 | * @param mixed value | ||
152 | * @return Boolean | ||
153 | */ | ||
154 | function isNumber(value) { | ||
155 | return isType(value, "number"); | ||
156 | } | ||
157 | exports.isNumber = isNumber; | ||
158 | |||
159 | /** | ||
160 | * Checks if value is a javascript Object | ||
161 | * | ||
162 | * @param mixed value | ||
163 | * @return Boolean | ||
164 | */ | ||
132 | function isObject(value) { | 165 | function isObject(value) { |
133 | return isType(value, "object"); | 166 | return isType(value, "object"); |
134 | } | 167 | } |
135 | exports.isObject = isObject; | 168 | exports.isObject = isObject; |
136 | 169 | ||
170 | /** | ||
171 | * Checks if value is a javascript String | ||
172 | * | ||
173 | * @param mixed value | ||
174 | * @return Boolean | ||
175 | */ | ||
137 | function isString(value) { | 176 | function isString(value) { |
138 | return isType(value, "string"); | 177 | return isType(value, "string"); |
139 | } | 178 | } |
... | @@ -148,7 +187,10 @@ exports.isString = isString; | ... | @@ -148,7 +187,10 @@ exports.isString = isString; |
148 | * @return Boolean | 187 | * @return Boolean |
149 | */ | 188 | */ |
150 | function isType(what, typeName) { | 189 | function isType(what, typeName) { |
151 | return betterTypeOf(what) === typeName; | 190 | if (typeof typeName !== "string" || !typeName) { |
191 | throw new Error("You must pass isType() a typeName string"); | ||
192 | } | ||
193 | return betterTypeOf(what).toLowerCase() === typeName.toLowerCase(); | ||
152 | } | 194 | } |
153 | exports.isType = isType; | 195 | exports.isType = isType; |
154 | 196 | ||
... | @@ -159,10 +201,10 @@ exports.isType = isType; | ... | @@ -159,10 +201,10 @@ exports.isType = isType; |
159 | * @return Boolean | 201 | * @return Boolean |
160 | */ | 202 | */ |
161 | function isWebPage(what) { | 203 | function isWebPage(what) { |
162 | if (!what || !isType(what, "object")) { | 204 | if (!what || !isObject(what)) { |
163 | return false; | 205 | return false; |
164 | } | 206 | } |
165 | if (phantom.version.major <= 1 && phantom.version.minor < 3 && isType(require, "function")) { | 207 | if (phantom.version.major <= 1 && phantom.version.minor < 3 && isFunction(require)) { |
166 | return what instanceof WebPage; | 208 | return what instanceof WebPage; |
167 | } else { | 209 | } else { |
168 | return what.toString().indexOf('WebPage(') === 0; | 210 | return what.toString().indexOf('WebPage(') === 0; |
... | @@ -202,9 +244,34 @@ exports.mergeObjects = mergeObjects; | ... | @@ -202,9 +244,34 @@ exports.mergeObjects = mergeObjects; |
202 | function serialize(value) { | 244 | function serialize(value) { |
203 | if (isType(value, "array")) { | 245 | if (isType(value, "array")) { |
204 | value = value.map(function(prop) { | 246 | value = value.map(function(prop) { |
205 | return isType(prop, "function") ? prop.toString().replace(/\s{2,}/, '') : prop; | 247 | return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop; |
206 | }); | 248 | }); |
207 | } | 249 | } |
208 | return JSON.stringify(value, null, 4); | 250 | return JSON.stringify(value, null, 4); |
209 | } | 251 | } |
210 | exports.serialize = serialize; | 252 | exports.serialize = serialize; |
253 | |||
254 | /** | ||
255 | * Inherit the prototype methods from one constructor into another. | ||
256 | * | ||
257 | * The Function.prototype.inherits from lang.js rewritten as a standalone | ||
258 | * function (not on Function.prototype). NOTE: If this file is to be loaded | ||
259 | * during bootstrapping this function needs to be revritten using some native | ||
260 | * functions as prototype setup using normal JavaScript does not work as | ||
261 | * expected during bootstrapping (see mirror.js in r114903). | ||
262 | * | ||
263 | * @param {function} ctor Constructor function which needs to inherit the | ||
264 | * prototype. | ||
265 | * @param {function} superCtor Constructor function to inherit prototype from. | ||
266 | */ | ||
267 | exports.inherits = function(ctor, superCtor) { | ||
268 | ctor.super_ = superCtor; | ||
269 | ctor.prototype = Object.create(superCtor.prototype, { | ||
270 | constructor: { | ||
271 | value: ctor, | ||
272 | enumerable: false, | ||
273 | writable: true, | ||
274 | configurable: true | ||
275 | } | ||
276 | }); | ||
277 | }; | ... | ... |
-
Please register or sign in to post a comment