I'm new to CasperJS and I'm trying to figure out the execution flow.
This is what I'm trying to achieve:
load a page
stores an image of the page
pass this image to a function and execute it (this process is quite long: ~15 seconds)
wait for the function to return the result
use the returned value to fill a field in the form in the loaded page
submit the form
this is a code snippet which tries to explain the solution I came up with:
var globProcessedImage;
var casper = require('casper').create({
viewportSize: {
width: 1024,
height: 768
}
});
casper.start('http://example.com/');
casper.then(function() {
this.captureSelector('./image.png', '#img-node');
});
casper.waitFor(function() {
return globProcessedImage !== undefined;
}, function then() {
this.sendKeys('#imagePassword', globProcessedImage);
});
casper.then(function() {
this.capture('./page.png');
});
casper.run();
casper.on('image.processed', function() {
setTimeout(function() {
globProcessedImage = 'my_result';
}, 15000);
});
This results in ReferenceError: Can't find variable: globProcessedImage.
It's still unclear to me how web automation and "external" functions mix together with CasperJS, as well as how parameters are passed between the page and casper/phantom environments.
Maybe something like that :
var globProcessedImage ;
var casper = require('casper').create({
viewportSize: {
width: 1024,
height: 768
}
});
casper.start('http://example.com/');
casper.options.waitTimeout = 16000;
casper.then(function() {
this.captureSelector('./image.png', '#img-node');
this.emit('image.processed');
});
/*
* If you need to wait for a fix time, it's okay, but a better way would be to wait in Casper
* 'the thing you do with your image' in a waitFor. -> asynchronous. With this solution you combine two timers, this is not the optimized solution.
*/
casper.waitFor(function() {
return globProcessedImage !== undefined;
}, function then() {
this.sendKeys('#imagePassword', globProcessedImage);
});
casper.then(function() {
this.capture('./page.png');
});
casper.run();
casper.on('image.processed', function() {
setTimeout(function() {
//when you will emit this event in your script, it will set the value of globProcessedImage to 'my_result' after 15sec
globProcessedImage = 'my_result';
}, 15000);
});
Related
I'm running a webpage in some graphics playout software. The function update() is automatically called by the graphics software when the page initially loads up, however I need to wait for the animation library to finish loading before the code in update() executes.
animation.addEventListener('DOMLoaded', function () {
console.log('Animation ready');
});
// This is automatically called by the graphics renderer on page load.
function update(data) {
// update the data in the animation...
};
I've come up with a workaround using setInterval that check if the animation has loaded before running the update code:
var animationLoaded = false;
animation.addEventListener('DOMLoaded', function() {
console.log('Animation ready');
animationLoaded = true;
});
// This is automatically called by the graphics renderer on page load.
function update(data) {
var updateInterval = setInterval(function() {
if (animationLoaded) {
clearInterval(updateInterval);
// update the data in the animation...
}
}, 10);
}
I feel there is a much better way of doing this, maybe using async/await? but I am unsure how to do this?
An idea, may not be the best :
on the update function, if DOMLoaded has been trigger then do your job, else save the data parameter
on the DOMLoaded function, call update with the saved datas (if set)
var animationLoaded = false;
var animationData = null;
animation.addEventListener('DOMLoaded', function() {
animationLoaded = true;
if (animationData !== null) {
update(animationData);
}
});
// This is automatically called by the graphics renderer on page load.
function update(data) {
if (animationLoaded) {
// update the data in the animation...
} else {
animationData = data;
}
}
In my Rails 5.2.2 app I am using Turbolinks.
I have discovered that when I leave a page, the functions that were started continues.
I have organised my functions below a return statement that checks the body class. In my example below, if the body class is not foobar the functions below do not run.
// assets/javascripts/pages/foobar.js
var goLoop;
$(document).on("turbolinks:load", function() {
if (!$("body").hasClass("foobar")) {
return;
}
return goLoop();
});
goLoop = function() {
return setTimeout((function() {
console.log("Hello");
return goLoop();
}), 1000);
};
First time I visit the page, the goLoop function is triggered.
When I follow a link away from the page, the function runs. If I had not used Turbolinks, this would not have happened.
If I follow another link back to the page, the function is triggered again, so now it runs twice.
How can I avoid this, without disabling Turbolinks?
Use the turbolinks:before-cache to teardown your timeout using clearTimeout. You will need to keep a reference of the current timeout ID. So your solution might look like:
var goLoop;
var timeout;
$(document).on("turbolinks:load", function() {
if (!$("body").hasClass("foobar")) {
return;
}
return goLoop();
});
goLoop = function() {
return timeout = setTimeout((function() {
console.log("Hello");
return goLoop();
}), 1000);
};
$(document).on("turbolinks:before-render", function() {
clearTimeout(timeout);
});
You can use PageVisibilityAPI to see is current page active or not.
and for the loop issue, you should check whether it's exist or not then run timeout function.
I have an variable
var IsAjaxing;
I set it to true everytime a ajax is fired on the page. And then set it to false when ajax is finished;
I am building a SafeAjaxing event so work would only be done when the page is not ajaxing:
// safe-ajaxing: Triggers when no ajax is running
$($fieldRenderPageDOM).on("safe-ajaxing", '.field-render', function(e, work) {
$.when({ IsAjaxing: false }).done(work);
});
This doesn't seem to wait, work is always called immediately.
It would be called like this:
$fieldDOM.trigger("safe-ajaxing", function () {
$fieldDOM.trigger("do-work");
$fieldDOM.trigger("do-some-more-work);
});
You should use promises for this purpose:
var IsAjaxing = function(){
var defer = $.Deferred().resolve();
return {
On: function(){
defer = $.Deferred();
},
Off: function(){
defer.resolve();
},
Promise: function() {
return defer.promise();
},
IsOn: function() {
return defer.state() == "pending";
},
IsOff: function() {
return defer.state() != "pending";
}
};
}();
And then your event will be:
// safe-ajaxing: Triggers when no ajax is running
$($fieldRenderPageDOM).on("safe-ajaxing", '.field-render', function(e, work) {
$.when(IsAjaxing.Promise()).done(work);
});
Each time when you start ajax request run:
IsAjaxing.On();
Each time when you finish ajax run:
IsAjaxing.Off();
To check the current state of IsAjaxing, call the IsOn and IsOff function.
This might not be the best way, but it works.
You really should optimize the code i've written, but this is to get you started.
var isAjaxing = false;
var check = function(){
if(isAjaxing){
// do something
alert();
// freeze the checking
// manual restart is required
clearInterval(interval);
}
}
var interval = setInterval(check, 10);
// to demonstrate the variable change
setInterval(function(){
isAjaxing = true;
}, 3000);
This scripts checks if the variable is changed every 10 miliseconds.
Note: The clearInterval() function is used to stop checking.
I am building a SafeAjaxing event so work would only be done when the page is not ajaxing.
Don't build this yourself. Just use the builtin ajax events of jQuery, namely
ajaxStart "This event is triggered if an Ajax request is started and no other Ajax requests are currently running."
ajaxStop "This global event is triggered if there are no more Ajax requests being processed."
I'm using PhantomJS to scrape data from a webpage. PhantomJS is not returning anything from the evaluate method. The script just runs for a few seconds and then exits.
I've already checked to see if PhantomJS is connecting to the page -- it is.
PhantomJS is also able to grab the page title. I've already double-checked the class I'm looking for, yes -- I'm spelling it correctly.
var page = require('webpage').create();
page.open('http://www.maccosmetics.com/product/13854/36182/Products/Makeup/Lips/Lipstick/Giambattista-Valli-Lipstick', function(status) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
waitFor(function() {
return page.evaluate(function() {
$('.product__price').is(':visible');
});
}, function(){
search = page.evaluate(function() {
return $('.product__price').text();
});
console.log(search)
});
});
phantom.exit();
});
I don't know what's going wrong here.
It's not showing you anything, because you're exiting too early. All functions (except evaluate()) that take a callback are asynchronous.
You're requesting to include jQuery in the page by calling page.includeJs(), you immediately exit PhantomJS. You need to exit when you're finished:
var page = require('webpage').create();
page.open('http://www.maccosmetics.com/product/13854/36182/Products/Makeup/Lips/Lipstick/Giambattista-Valli-Lipstick', function(status) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
waitFor(function() {
return page.evaluate(function() {
$('.product__price').is(':visible');
});
}, function(){
search = page.evaluate(function() {
return $('.product__price').text();
});
console.log(search);
phantom.exit();
});
});
});
It seems that helloworld.js gets loaded multiple times based on the number of times I click #load. I say this because when I look at Google Chromes Developer Tools Network tab, it shows helloworld.js as many times as I click #load.
$(document).ready(function() {
$("#load").click(function(){
$.getScript('helloworld.js', function() {
hello();
});
});
});
The hello() function looks like this:
function hello(){
alert("hello");
}
Is it possible to detect if helloworld.js has already loaded?
So if it hasn't loaded, load it, and if it has loaded, don't load it.
This is what Developer Tools currently shows me if I click the #load button 4 times:
Set a flag when file loaded successfully. If flag is set then skip the file loading again.
Try this code,
var isLoaded = 0; //Set the flag OFF
$(document).ready(function() {
$("#load").click(function(){
if(isLoaded){ //If flag is ON then return false
alert("File already loaded");
return false;
}
$.getScript('helloworld.js', function() {
isLoaded = 1; //Turn ON the flag
hello();
});
});
});
So why not only fire the event once like this:
$("#load").one("click", function() {
$load = $(this);
$.getScript('helloworld.js', function() {
hello();
// bind hello to the click event of load for subsequent calls
$load.on('click', hello);
});
});
That would prevent subsequent loads and avoids the use of a global
Another option is letting .getScript() run but let it take the script from browser's cache so you won't have it reloaded each and every time.
To achieve this, add such code:
$.ajaxSetup({
cache: true
});
This is taken from the documentation page.
You could create a helper function:
var getScript = (function() {
var loadedFiles = {};
return function(filename, callback) {
if(loadedFiles[filename]) {
callback();
} else {
$.getScript(filename, function() {
loadedFiles[filename] = true;
callback();
});
}
};
})();