After opening a webpage, check if it has opened before proceeding - javascript

I am trying to create a chrome extension that, with a click of a button opens several webpages that I often visit. Currently when clicked it opens 1-4 of the 4 webpages I want it to, often stopping prematurely. The code is pretty simple, so I figured it is a processing issue. For this reason I want to introduce some delay. I've been told not to use sleep() from the research I have found so I am trying to implement code that makes my For loop wait until the page has loaded before proceeding. Here is the code:
function OpenInNewTabWinBrowser(url) {
var win = window.open(url, '_blank');
//win.focus();
}
function CheckLoading() {
return document.readyState === "interactive";
}
var websites = ['https://reddit.com', 'https://xkcd.com', 'http://poorlydrawnlines.com', 'https://explosm.net'];
var MoveAlong;
for (var i = websites.length - 1; i >= 0; i--) {
OpenInNewTabWinBrowser(websites[i]);
console.log("Just opened a window!");
MoveAlong = CheckLoading();
console.log("Just checked if it was loading!");
/*while (MoveAlong == false) {
console.log("Just realized it hasn't loaded all the way!");
sleep(10);
console.log("Just woke up!");
MoveAlong = CheckLoading();
console.log("Just double checked if it had loaded!")
}
console.log("Just broke out of the while loop!")*/
}
console.log("Just finished doing everything you asked master!")
When I run the code as is I don't always open every page. The commented section is what I have tried to utilize as a pausing function but when that code is un-commented it only opens up one page and never anymore. I have also tried supplying console.log comments for debugging but when I inspect popup if even one window opens up the console closes itself and I am left with no means of reading where the code went wrong.
I have also tried this loop and function to check for a loaded page and then unpause. This code replaced the For loop from the snippet above. It also didn't work correctly.
var i = websites.length - 1;
do {
MoveAlong = false;
OpenInNewTabWinBrowser(websites[i]);
i--;
window.onload = function() {
MoveAlong = true;
}
}
while (MoveAlong == true && i >= 0);
Any help is much appreciated. On how to properly debug, on how to detect if the website is loading, on how to make this extension work. I have been a partial lurker for a while but now I am trying to actively code every day. This is my first post and hopefully it will be the beginning to a fun hobby. Thank you again.

Related

Casperjs slowing down looping through links

I'm learning CasperJS by making a test for my website that grabs all the links from the nav bar and loops through opening them all up and running a small test for each page (check the title, hit a search button, see if results come back, etc). I also included a "Quick Test" flag that will only check the page title before moving on to the next link. There are about 25 links total.
The issue is that somehow the script gets stuck after about 10 full tests, but works fine with quick-testing. This is the loop I'm using to open each page:
casper.each(linkList, function(self, link){
self.thenOpen(link, function(){
self.echo(link);
temp = Date.now();
this.open(urlPrefix + link);
this.then(function(){
temp = (Date.now()) - temp;
self.echo("Load time: "+temp.toString()+"ms");
switch(link){
//case statements for specific pages
// - run specialized versions of testPage()
case "Example":
testExample(this);
break;
default:
testPage(this);
break;
}
});
});
});
The testPage() and page specific functions all look something like:
function testPage(ths){
checkTitle(ths, "Page Title");
if(quickTest)
return;
ths.click('#searchButton');
casper.waitForSelectorTextChange("#results",function(){
temp = ths.evaluate(function(){
return $("tr.row").length;
});
if(temp>0)
casper.echo("Results returned");
else
casper.echo("No results returned");
});
}
The checkTitle() function is just a simple:
function checkTitle(ths, name){
temp = ths.getTitle();
casper.echo("Page Title: "+temp+" - App loads: "+(temp==name ? "PASSED" : "FAILURE"));
}
Now, if quickTest is true then the loop finishes, no problems. If quickTest is false then the loop hangs indefinitely on the 12th page. Coincidentally, the 11th page is literally the same page, just with more options for the search filters. Additionally, my casperjs scripts is telling me it take the page 13410ms to load with quickTest=false and only 460ms with quickTest=true, which is confusing since no code between the 2 timestamps is skipped/added from that flag and loading the page in IE doesn't take nearly that long.
Why is casper slowing down after looping through links?
I managed to stumble upon this page. It appears that somewhere in this process there is a memory leak. While I'm still unfamiliar with casperjs and phantomjs, I would guess it involves the this.open() bit in the loop. I've managed to get all the tests to finish by adding the following:
casper.page.close();
casper.page = casper.newPage();
So the beginning of the loop code now looks like:
casper.each(linkList, function(self, link){
self.thenOpen(link, function(){
self.echo(link);
casper.page.close();
casper.page = casper.newPage();
temp = Date.now();
this.open(urlPrefix + link);
......

Loop not stopping or not starting

I am making a small userscript for a survey site. Whenever I click the survey and it is indicated that I did not receive it the script is supposed to refresh a certain amount of times trying to claim the survey until it gives up.
(If you do not know what a userscript is, think of it as something that stays running all the time on a specific website, so you can control things using javascript selectors and stuff)
This is my script so far (the javascript portion as that is what the problem is in):
var elementExists = document.getElementsByClassName("message warning")[0];
if(elementExists)
{
var attempts = 0;
while(attempts<5)
{
attempts += 1;
location.reload();
if(elementExists)
{
//nothing
}
else
{
window.stop();
}
}
window.stop();
}
This is actually my first time using Javascript so I assumed that would be the reason for errors, but after 45 minutes of debugging I am completely baffled. If I remove that last "window.stop();" the code refreshes the webpage infinitely. If that stays there then the code doesn't even start. It seems almost as if the while loop is being skipped if the "window.stop();" is present. Is this something that Javascript does, or is the problem elsewhere?
If someone could lead me in the right direction or help me fix this I would be very grateful!
(Also I checked the selector to see if that is the issue, but I have done that correctly)
UPDATE: Turns out location.reload(); stops the script and thus forces a reload. Since I am creating a userscript I realized that I could use the Greasemonkey APIs (or more like stumbled upon). By using GM_setValue and GM_getValue I was able to work around this problem and the script successfully reloaded a certain amount of times (depending on the variable tries) and stopped when it finished. But after messing around a bit, then reverting to the older version the script, the script doesn't doesn't execute at all anymore; "counter < tries" seems to be false for some reason... could anyone figure out why? Also if documentation is needed:
https://wiki.greasespot.net/GM_getValue
https://wiki.greasespot.net/GM_setValue
var tries = 5;
var elementExists = document.getElementsByClassName("message warning")[0];
var counter = GM_getValue('counter', 0);
if(elementExists && counter < tries)
{
GM_setValue('counter', ++counter);
location.reload();
}
(Both counter and tries seem to be integer values.. so there should be in problem in comparing them...)
Also as suggested by #yuriy636 I attempted to reset the variables and created something like this
var tries = 5;
var elementExists = document.getElementsByClassName("message warning")[0];
var counter1 = GM_getValue('counter1', 0);
if(elementExists && counter1 < tries)
{
GM_setValue('counter1', ++counter1);
location.reload();
}
if(elementExists && counter1 == tries)
{
GM_deleteValue('counter1');
window.close();
}
if(!!elementExists)
{
GM_deleteValue('counter1');
return;
alert("stops script while hidden");
}
But again I am hit with the infinite loop.. RIP
Update 2: Not so RIP afterall... solution:
var tries = 50;
var elementExists = document.getElementsByClassName("message warning")[0];
var counter = GM_getValue('counter', 0);
if(elementExists && counter < tries)
{
GM_setValue("counter", counter + 1);
location.reload();
}
else
{
GM_deleteValue("counter");
}
if(elementExists && counter >= tries)
{
window.close();
}
100% Working, after indicated amount of tries, if error still exists the page is closed
The most likely problem is that you have location.reload() in your while loop. This causes the page to refresh before anything interesting happens in your loop. In this particular code I would expect the page to refresh seemingly infinitely because every time the page refreshes, it will refresh again.
Normally this would look more like:
var elements = document.getElementsByClassName("message warning");
for (var i = 0; i < elements.length; i++) {
console.log (elements[i]);
}
.getElementsByClassName returns an array of elements with the class message and/or warning which you are capturing only the 1st one [0]. Hope that helps.

Javascript -- any way to force a draw of new elements before processing?

I have a javascript function or two that takes a few seconds to process. I want to show an activity indicator so users don't think it's hung. I tried bootstrap animated progress bars and spin.js -- the problem is, I put the code for them before the processing, but the browser doesn't seem to update to show the new elements until after the function has finished. Obviously, an activity indicator that shows up after processing isn't incredibly useful. Is there a way to force the redraw before the processing occurs?
EDIT: here's an example code. This is from an HTA application, hence the pulling of files and such. I know the forced "wait" portion is a bad idea, usually, but in this program, files are opened and closed immediately, this is to prevent a race conflict and I just want to give it a quick 10 retry timeout before it tells them something is wrong. So, the problem is that this.spin() doesn't display its good until after the alert pops up.
Database.prototype.tryDatabase = function(fileName, toEdit, table, keepAlive) {
if (typeof keepAlive === 'undefined') keepAlive = false;
for (timeInd=0; timeInd < 5; timeInd++) {
var complete = this._addToFile(fileName, toEdit, table, keepAlive);
if (complete === false) {
var until = moment().valueOf() + 2000;
this.spin();
while (moment().valueOf() <= until) {;}
}
else break;
}
if (complete === false) window.alert("Database was locked for an extended period, please try again");
else if (!keepAlive) $('#added_to_db_modal').modal('show');
return complete;
}

How to call a site's function from a Chrome extension?

I'm trying to make a Chrome extention for myself, so that when I visit any sort of channel at Twitch.tv, the chat will automatically hide.
I've been looking at it with Firebug and I found toggle_chat(). If I type that in the console, the chat is no longer visible.
In my userscript file, I have written
window.onload = function() {
toggle_chat();
}
but it says
Uncaught ReferenceError: toggle_chat is not defined" in the console when I load a Twitch channel.
Any ideas how to make this work?
This has nothing to do with timing. Chrome extensions and content scripts execute in an isolated world, meaning they have no access to the page's javascript including functions. You could make it so that your content script appends a <script> element that then calls the page function that you want but it would be far easier to just simulate a click on the #right_close element. You can do this with pure Javascript like this:
window.onload = function(){
var evObj = document.createEvent('Events');
evObj.initEvent('click', true, false);
document.querySelector('#right_close').dispatchEvent(evObj);
}
I know this is very hacky, but it gets the job done, and sometimes that exactly what you need. :) It'll check for the function roughly ever half second until it exists. When it's finally there, it'll call the function then clear the timer.
window.onload = function() {
var id = null;
var check = function() {
if (typeof toggle_chat === "function") {
toggle_chat();
clearInterval(id);
}
}
id = setInterval(check, 500);
}

Getting functions from another script in JS

I load this JS code from a bookmarklet:
function in_array(a, b)
{
for (i in b)
if (b[i] == a)
return true;
return false;
}
function include_dom(script_filename) {
var html_doc = document.getElementsByTagName('head').item(0);
var js = document.createElement('script');
js.setAttribute('language', 'javascript');
js.setAttribute('type', 'text/javascript');
js.setAttribute('src', script_filename);
html_doc.appendChild(js);
return false;
}
var itemname = '';
var currency = '';
var price = '';
var supported = new Array('www.amazon.com');
var domain = document.domain;
if (in_array(domain, supported))
{
include_dom('http://localhost/bklts/parse/'+domain+'.js');
alert(getName());
}
[...]
Note that the 'getName()' function is in http://localhost/bklts/parse/www.amazon.com/js. This code works only the -second- time I click the bookmarklet (the function doesn't seem to get loaded until after the alert()).
Oddly enough, if I change the code to:
if (in_array(domain, supported))
{
include_dom('http://localhost/bklts/parse/'+domain+'.js');
alert('hello there');
alert(getName());
}
I get both alerts on the first click, and the rest of the script functions. How can I make the script work on the first click of the bookmarklet without spurious alerts?
Thanks!
-Mala
Adding a <script> tag through DHTML makes the script load asynchroneously, which means that the browser will start loading it, but won't wait for it to run the rest of script.
You can handle events on the tag object to find out when the script is loaded. Here is a piece of sample code I use that seems to work fine in all browsers, although I'm sure theres a better way of achieving this, I hope this should point you in the right direction:
Don't forget to change tag to your object holding the <script> element, fnLoader to a function to call when the script is loaded, and fnError to a function to call if loading the script fails.
Bear in mind that those function will be called at a later time, so they (like tag) must be available then (a closure would take care of that normally).
tag.onload = fnLoader;
tag.onerror = fnError;
tag.onreadystatechange = function() {
if (!window.opera && typeof tag.readyState == "string"){
/* Disgusting IE fix */
if (tag.readyState == "complete" || tag.readyState == "loaded") {
fnLoader();
} else if (tag.readyState != "loading") {
fnError();
};
} else if (tag.readyState == 4) {
if (tag.status != 200) {
fnLoader();
}
else {
fnError();
};
};
});
It sounds like the loading of the external script (http://localhost/bklts/parse/www.amazon.com/js) isn't blocking execution until it is loaded. A simple timeout might be enough to give the browser a chance to update the DOM and then immediately queue up the execution of your next block of logic:
//...
if (in_array(domain, supported))
{
include_dom('http://localhost/bklts/parse/'+domain+'.js');
setTimeout(function() {
alert(getName());
}, 0);
}
//...
In my experience, if zero doesn't work for the timeout amount, then you have a real race condition. Making the timeout longer (e.g. 10-100) may fix it for some situations but you get into a risky situation if you need this to always work. If zero works for you, then it should be pretty solid. If not, then you may need to push more (all?) of your remaining code to be executed into the external script.
The best way I could get working: Don't.
Since I was calling the JS from a small loader bookmarklet anyway (which just tacks the script on to the page you're looking at) I modified the bookmarklet to point the src to a php script which outputs the JS code, taking the document.domain as a parameter. As such, I just used php to include the external code.
Hope that helps someone. Since it's not really an answer to my question, I won't mark this as the accepted answer. If someone has a better way, I'd love to know it, but I'll be leaving my code as is:
bookmarklet:
javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://localhost/bklts/div.php?d='+escape(document.domain);})();
localhost/bklts/div.php:
<?php
print("
// JS code
");
$supported = array("www.amazon.com", "www.amazon.co.uk");
$domain = #$_GET['d']
if (in_array($domain, $supported))
include("parse/$domain.js");
print("
// more JS code
");
?>

Categories

Resources