phantom-proxy
Version:
a lightweight proxy that lets you to drive phantomjs from node.
319 lines (293 loc) • 15.8 kB
JavaScript
(function (module) {
'use strict';
var util = require('util'),
events = require('events'),
_ = require('underscore'),
request = require('./request/main');
var WebPage = function (options) {
var self = this,
executeMethod = function () {
var self = this,
callbackFn = arguments[1],
methodName = arguments[0],
args = (arguments.length > 2) ? Array.prototype.slice.call(arguments, 2, arguments.length) :
[
],
url = this.options.hostAndPort + methodName;
this.options.debug && console.log('calling execute method for %s and with %d params: %s'.grey, url, args.length, JSON.stringify(args));
request.post(url, {form:{ args:JSON.stringify(args)}},
function (error, response, body) {
error && console.error(error);
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, true, body);
}
else {
console.log('error in response %s'.red.bold, body);
callbackFn && callbackFn.call(self, false, body);
}
});
};
self.options = options;
events.EventEmitter.call(this);
//sets property value ex. viewportSize
_.extend(this, {
set:function (propertyName, propertyValue, callbackFn) {
var self = this;
// allow functions to be set
if(typeof propertyValue === 'function') {
propertyValue = {
_fn: propertyValue.toString()
};
}
request.post(self.options.hostAndPort + '/page/properties/set', {
form:{
propertyName:propertyName,
propertyValue:JSON.stringify(propertyValue, null, 4)
}
},
function (error, response, body) {
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, !!body);
}
else {
callbackFn && callbackFn.call(self, false, body);
}
});
},
//gets property value ex. version
get:function (propertyName, callbackFn) {
var self = this;
request.post(this.options.hostAndPort + '/page/properties/get', {form:{ propertyName:propertyName}},
function (error, response, body) {
error && console.error(error);
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, body);
}
else {
console.log('error in response %s'.red.bold, body);
callbackFn && callbackFn.call(self, false, body);
}
});
},
settings: {
set:function (propertyName, propertyValue, callbackFn) {
request.post(self.options.hostAndPort + '/page/settings/set', {
form:{
propertyName:propertyName,
propertyValue:JSON.stringify(propertyValue)
}
},
function (error, response, body) {
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, !!body);
}
else {
callbackFn && callbackFn.call(self, false, body);
}
});
},
get:function (propertyName, callbackFn) {
request.post(self.options.hostAndPort + '/page/settings/get', {form:{propertyName:propertyName}},
function (error, response, body) {
error && console.error(error);
if (response && response.statusCode === 200) {
console.log('response is ',body, 'code: ',response.statusCode);
console.log('response parsed: ',JSON.parse(body));
callbackFn && callbackFn.call(self, JSON.parse(body));
}
else {
console.log('error in response %s'.red.bold, body);
callbackFn && callbackFn.call(self, false, body);
}
});
}
},
includeJs:function (url, callbackFn) {
executeMethod.call(this, '/page/functions/includeJs', callbackFn, url);
},
sendEvent:function (eventArgs, callbackFn) {
var args =
[
'/page/functions/sendEvent',
callbackFn,
eventArgs.event
];
if(eventArgs.mouseX && eventArgs.mouseY) {
args = args.concat([
eventArgs.mouseX,
eventArgs.mouseY
]);
}
eventArgs.mouseX && eventArgs.mouseY && eventArgs.button && args.push(
eventArgs.button
);
eventArgs.keys && args.push(eventArgs.keys);
executeMethod.apply(this, args);
return this;
},
addCookie:function (cookie, callbackFn) {
executeMethod.call(this, '/page/functions/addCookie', callbackFn, cookie);
return this;
},
clearCookies:function (callbackFn) {
executeMethod.call(this, '/page/functions/clearCookies', callbackFn);
},
deleteCookie:function (cookieName, callbackFn) {
executeMethod.call(this, '/page/functions/deleteCookie', callbackFn, cookieName);
},
close:function (callbackFn) {
executeMethod.call(this, '/page/functions/close', callbackFn);
return this;
},
//Opens the URL and loads it to the page. Once page is loaded, the optional callback is called using onLoadFinished,
// and also provides the page status to the function ('success' or 'fail').
open:function (openUrl, callbackFn) {
var self = this,
url = self.options.hostAndPort + '/page/functions/open';
self.options.debug && console.log('calling url: %s', url);
request.post(url, {form:{ args:JSON.stringify(
[
openUrl
], null, 4)}},
function (error, response, body) {
self.options.debug && console.log(body);
if (response && response.statusCode === 200) {
body === 'success' ?
callbackFn && callbackFn.call(self, true) : callbackFn && callbackFn.call(self, false);
}
else {
callbackFn && callbackFn.call(self, false, body);
}
}
);
},
//Evaluates the given function in the context of the web page.
// The execution is sandboxed, the web page has no access to the phantom object and it can't probe its own setting.
evaluate:function (expressionFn, callbackFn) {
var self = this,
url = self.options.hostAndPort + '/page/functions/evaluate';
self.options.debug && console.log('calling url: %s', url);
request.post(url, {
form:{expressionFn:expressionFn.toString(), args:JSON.stringify(Array.prototype.slice.call(arguments, 2, arguments.length), null, 4)}
},
function (error, response, body) {
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, body);
}
else {
console.error(body);
callbackFn && callbackFn.call(self, body);
}
});
},
//Evaluates the given function in the context of the web page.
// The execution is sandboxed, the web page has no access to the phantom object and it can't probe its own setting.
evaluateAsync:function (expressionFn, callbackFn) {
var self = this,
url = self.options.hostAndPort + '/page/functions/evaluateAsync';
self.options.debug && console.log('calling url: %s', url);
request.post(url, {
form:{expressionFn:expressionFn.toString(), args:JSON.stringify(Array.prototype.slice.call(arguments, 2, arguments.length), null, 4)}
},
function (error, response, body) {
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, !!body);
}
else {
console.error(body);
callbackFn && callbackFn.call(self, body);
}
});
},
//Renders the web page to an image buffer and save it as the specified file.
// Currently the output format is automatically set based on the file extension. Supported formats are PNG, GIF, JPEG, and PDF.
render:function (filename, callbackFn) {
var self = this,
url = self.options.hostAndPort + '/page/functions/render';
self.options.debug && console.log('calling url: %s', url);
request.post(url, {form:{ args:JSON.stringify(
[
filename
], null, 4)}},
function (error, response, body) {
error && console.error(error);
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, !!body);
}
else {
console.error(body);
callbackFn && callbackFn.call(self, body);
}
}
);
},
//Renders the web page to an image buffer and returns the result as a base64-encoded string representation of that image.
// Supported formats are PNG, GIF, and JPEG.
renderBase64:function (format, callbackFn) {
var self = this,
args =
[
format
],
url = this.options.hostAndPort + '/page/functions/renderBase64';
this.options.debug && console.log('calling execute method for %s and with %d params: %s'.grey, url, args.length, JSON.stringify(args));
request.post(url, {form:{ args:JSON.stringify(args)}},
function (error, response, body) {
error && console.error(error);
if (response && response.statusCode === 200) {
callbackFn && callbackFn.call(self, body);
}
else {
console.log('error in response %s'.red.bold, body);
callbackFn && callbackFn.call(self, false, body);
}
});
return this;
},
//additional methods not in phantomjs api
//waits for selector to appear, then executes callbackFn
waitForSelector:function (selector, callbackFn, timeout) {
var self = this,
startTime = Date.now(),
timeoutInterval = 150,
testRunning = false,
//if evaluate succeeds, invokes callback w/ true, if timeout,
// invokes w/ false, otherwise just exits
testForSelector = function () {
var elapsedTime = Date.now() - startTime;
if (elapsedTime > timeout) {
self.options.debug && console.log('warning: timeout occurred while waiting for selector:"%s"'.yellow, selector);
callbackFn(false);
return;
}
self.evaluate(function (selectorToEvaluate) {
return document.querySelectorAll(selectorToEvaluate).length;
}, function (result) {
testRunning = false;
if (result > 0) {//selector found
callbackFn(true);
}
else {
setTimeout(testForSelector, timeoutInterval);
}
}, selector);
};
timeout = timeout || 10000; //default timeout is 2 sec;
setTimeout(testForSelector, timeoutInterval);
},
injectJs:function (filename, callbackFn) {
executeMethod.call(this, '/page/functions/injectJs', callbackFn, filename);
return this;
}
// evaluateAsync:function (expressionFn, callbackFn) {
// executeMethod.call(this, 'page/functions/deleteCookie', callbackFn);
// },
});
}
;
util.inherits(WebPage, events.EventEmitter);
module.exports = WebPage;
}
(module)
)
;