I'm using slimerjs to render some html files. Each file contains a JSON string that gets loaded with a call to
fabricjsCanvas.loadFromJson(jsonString, fabricjsCanvas.renderAll.bind(fabricjsCanvas));
This is where I open my page
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the page!');
phantom.exit(1);
} else {
page.render(output);
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 5000);
}
});
As you can see I had to set a timeout after which slimerjs closes the page saving what's on it. I really don't like this solution, coz I need to render multiple pages, some of them are very small, and could take less than 200 milliseconds, others are huge and could take more than 5000, so this is just bad for perfomances and isn't even a "safe solution" against page taking a long time to render. I tryid putting a console.log at the end of canvas.renderAll call and then add this piece of code to my slimerjs script
page.onConsoleMessage = function (msg) {
console.log(msg);
page.render(output);
phantom.exit();
};page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit(1);
}
});
I hoped that this would have helped but nothing really changed, the reanderAll finishes before all objects are displayed.
Is there some event I can catch, or something else I can do to prevent this?
You should use a callback page.onLoadFinished().
page.onLoadFinished = function(status, url, isFrame) {
console.log('Loading of '+url+' is a '+ status);
page.render();
};
This function run after full upload page
I managed to find a solution. First of all I changed my html template,since I only need to render per page i used a StaticCanvas instead of a normal canvas. Since a static canvas only has 2 frames to render(the top one and the secondary container, at least this is what I've learned in my experience) I added an event lister on after:render event, so after the second frame has been rendered I print a console message, at this point the page.onConsoleMessage gets called and closes the phantom process.
In this way I don't need to allow a standard amount of time that could be too much (loosing perfomances) or not enough (and the image would result blank).
this is the script in my html template
var framesRendered = 0;
var canvas = new fabric.StaticCanvas('canvas', {width: {{width}}, height: {{height}} });
canvas.setZoom({{{zoom}}});
canvas.on('after:render', function() {
if(framesRendered == 1)
console.log('render complete');
else framesRendered++;
});
canvas.loadFromJSON({{{data}}}, canvas.renderAll.bind(canvas), function (o, object) {
if (object.type === 'picturebox' && object.filters.length) {
object.applyFilters(function () {
canvas.renderAll();
});
}
});
and this is my slimerjs script
page.onConsoleMessage = function(){
page.render(output);
phantom.exit();
};
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit(1);
}
});
I'll leave this one here in case someone needs it.
Related
I'm using navigator.geolocation.watchPosition function to check if the browser's location is on or not. But this function is getting called more than once (without page load). For example if i switch to another tab and go back to the page's tab it gets called.
My code is
<script>
navigator.geolocation.watchPosition(function (position) {
alert("i'm tracking you!");
},
function (error) {
if (error.code == error.PERMISSION_DENIED){
alert("you denied me :-(");
}
});
</script>
How can i call that function only once?
Just keep track of whether the watchPosition function has fired your function or not:
var firedAlready = false;
navigator.geolocation.watchPosition(function (position) {
if(!firedAlready) {
alert("i'm tracking you!");
firedAlready = true;
}
},
function (error) {
if (error.code == error.PERMISSION_DENIED){
alert("you denied me :-(");
}
});
Try to create an enclosure for your code! This will make sure it's only performed once.
var something = (function() {
var executed = false;
return function () {
if (!executed) {
executed = true;
// do something
}
};
})();
Also consider disabling the event handler with navigator.geolocation.clearWatch().
E.g.:
var id;
id = navigator.geolocation.watchPosition(function (position) {
alert("i'm tracking you!");
navigator.geolocation.clearWatch(id);
},
function (error) {
if (error.code == error.PERMISSION_DENIED){
alert("you denied me :-(");
// I suspect this is actually unnecessary since we were denied.
navigator.geolocation.clearWatch(id);
}
});
For more details check the MDN page about Geolocation.clearWatch().
You can clear position listener when you have already got the result.
enter link description here
Or you maybe use navigator.geolocation.getCurrentPostion to test whether is on or off.
You need to save the status of checking the permission in local storage for long duration use or better use cookies for that site and save the preferences there.
If you only do a variable when the page is reloaded the variable is reinitialized.
Your data need to be persistent.
Start from here if you are new with cookies
http://www.w3schools.com/js/js_cookies.asp
I'm loging in to facebook with PhantomJS. Here is my code:
var page = require('webpage').create();
phantom.cookiesEnabled = true;
page.open("http://www.facebook.com/login.php", function(status) {
if (status === "success") {
page.evaluate(function() {
document.getElementById("email").value = "email";
document.getElementById("pass").value = "pass";
document.getElementById("loginbutton").click();
});
window.setTimeout(function() {
page.render("page.png");
phantom.exit();
}, 5000);
}
});
page.open("https://www.facebook.com/myprofil", function(status) {
window.setTimeout(function() {
page.render("profil.png");
phantom.exit();
}, 5000);
});
so far so good the login process goes fine, but I didn't find the right method to visit my profile URL for example in the same session. I tried to use page.open again, but I keep getting the login form with the page I requested as if I never logged in before. What's the proper way to navigate within your session?
page.open is an asynchronous function. It takes some time to open a page. When it's done, the callback is called. If you're calling page.open twice in a row without waiting for the first call to be finished, you're essentially overwriting the first request, so only the second one will be executed.
You need to nest the calls:
page.open("http://www.facebook.com/login.php", function(status) {
if (status === "success") {
page.evaluate(function() {
document.getElementById("email").value = "email";
document.getElementById("pass").value = "pass";
document.getElementById("loginbutton").click();
});
window.setTimeout(function() {
page.render("page.png");
page.open("https://www.facebook.com/myprofil", function(status) {
window.setTimeout(function() {
page.render("profil.png");
phantom.exit();
}, 5000);
});
}, 5000);
}
});
Remember that PhantomJS exits as soon as phantom.exit() is called. So you should probably have a single phantom.exit() when your script is supposed to end.
PROBLEM: the function inside page.evaluate doesn't find any img (therefore, console.log(images.length) outputs 0); however, there are many images in the page, and some even have ids.
QUESTION: What's going on? Why $('img') doesn't find anything?
UPDATE 1: This is a <frame> problem. I had to switch to the frame in order to make the jQuery script correctly work.
DETAILS: I'm running a phantomjs script to access a webpage (link) and fetch all available images. It first saves a screenshot of the page just for comparison, and then it should through every <img> tag (using jQuery $('img')) and get the image dimensions and, using phantomjs's page.clipRect, it saves each image inside a folder.
var page = require('webpage').create();
var url = 'http://www.receita.fazenda.gov.br/pessoajuridica/cnpj/cnpjreva/cnpjreva_solicitacao.asp';
page.open(url, function (status) {
console.log("Status: " + status);
if (status === "success") {
page.render('example.png');
}
// Asynchronous call!
page.includeJs('http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js', function () {
console.log('\n Evaluate Page \n');
// Sandboxed
var images = page.evaluate(function () {
var images = [];
function getImgDimensions($i) {
return {
top: $i.offset().top,
left: $i.offset().left,
width: $i.width(),
height: $i.height(),
}
}
$('img').each(function () {
var img = getImgDimensions($(this));
images.push(img);
});
return images;
});
console.log(images.length);
images.forEach(function (imageObj, index, array) {
page.clipRect = imageObj;
page.render('images/' + index + '.png');
});
// Exit the session
phantom.exit();
});
});
I've looked at the site. The img that you want is inside of an iframe. You first need to switch to it.
Use for example:
page.switchToChildFrame(0);
to switch to the first child frame. Do this before you call page.includeJs().
If you want to do something in the parent page afterwards, you would have to change back with page.switchToParentFrame();.
I want to console.log the content of web page with nodejs and phantomjs. This is my code:
var phantom = require('phantom');
phantom.create(function(ph) {
return ph.createPage(function(page) {
return page.open("http://zehinz.com/test.html", function(status) {
if (status === 'success') {
//console.log the content of page with Javascript executed ???
} else {
console.log('some error');
ph.exit();
}
});
});
});
How can I output the dynamically rendered content of web page?
In plain PhantomJS one would use page.content, but since you're using a bridge, the content property has to be explicitly fetched from the PhantomJS process in the background. You can do this with page.get.
In your case, this is
page.get('content', function(content){
console.log("Content", content);
ph.exit();
});
I am trying to wrap a PhantomJS script in a node.js process. The phantom script grabs a url from the arguments provided on the command line and outputs a pdf (much similar to the rasterize.js example included with the pahntom install).
The phantom script I have works fine, it's just my employer wants a node script if possible. No problem, I can use the node-phantom node module to wrap it.
But now I've hit a stumbling block, my phantom script has:
var page = require('webpage').create();
So, node.js is trying to find a module called 'webpage', the 'webpage' module is built into the phantom install so node can't find it. As far as I can tell, there is no npm module called 'webpage'.
'webpage' is used like this:
page.open(address, function (status) {
if (status !== 'success') {
// --- Error opening the webpage ---
console.log('Unable to load the address!');
} else {
// --- Keep Looping Until Render Completes ---
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});
where address is the url specified on the command line and output is another argument, the name and type of the file.
Can anyone help me out? This is quite an abstract one so I'm not expecting much if I'm honest, worth a try though.
Thanks.
EDIT - Approx 2hrs later
I now have this which throws out a PDF:
var phanty = require('node-phantom');
var system = require('system');
phanty.create(function(err,phantom) {
//var page = require('webpage').create();
var address;
var output;
var size;
if (system.args.length < 4 || system.args.length > 6) {
// --- Bad Input ---
console.log('Wrong usage, you need to specify the BLAH BLAH BLAH');
phantom.exit(1);
} else {
phantom.createPage(function(err,page){
// --- Set Variables, Web Address, Output ---
address = system.args[2];
output = system.args[3];
page.viewportSize = { width: 600, height: 600 };
// --- Set Variables, Web Address ---
if (system.args.length > 4 && system.args[3].substr(-4) === ".pdf") {
// --- PDF Specific ---
size = system.args[4].split('*');
page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
: { format: system.args[4], orientation: 'portrait', margin: '1cm' };
}
// --- Zoom Factor (Should Never Be Set) ---
if (system.args.length > 5) {
page.zoomFactor = system.args[5];
} else {
page.zoomFactor = 1;
}
//----------------------------------------------------
page.open(address ,function(err,status){
if (status !== 'success') {
// --- Error opening the webpage ---
console.log('Unable to load the address!');
} else {
// --- Keep Looping Until Render Completes ---
process.nextTick(function () {
page.render(output);
phantom.exit();
}, 200);
}
});
});
}
});
But! It's not the right size! The page object created using the phantom 'webpage' create() function looks like this before it's passed the URL:
Whereas mine in my node script, looks like this:
Is it possible to hard code the properties to achieve A4 formatting? What properties am I missing?
I'm so close!
It should be something like:
var phantom=require('../node-phantom');
phantom.create(function(error,ph){
ph.createPage(function(err,page){
page.open(url ,function(err,status){
// do something
});
});
});
Your confusion here is because you want to reuse the same concepts and metaphors from your PhantomJS script. It does not work that way. I suggest that you spend some time studying the included tests of node-phantom, see https://github.com/alexscheelmeyer/node-phantom/tree/master/test.
Using https://github.com/sgentle/phantomjs-node I have made an A4 page in nodejs using phantom with the following code:
phantom.create(function(ph){
ph.createPage(function(page) {
page.set("paperSize", { format: "A4", orientation: 'portrait', margin: '1cm' });
page.open("http://www.google.com", function(status) {
page.render("google.pdf", function(){
console.log("page rendered");
ph.exit();
})
})
})
});
Side Note:
the page.set() function takes any variable that you would set in the rasterize.js example. See how paperSize is set above and compare it to the relevant lines in rasterize.js