"for" loops and loading in an iframe...

Subscribe to "for" loops and loading in an iframe... 23 posts, 6 voices

 
tcd Scriptwright

I'm trying to write/find a script that would create an iframe in a specific webpage, then load in this iframe urls that are almost all the same, differing only in the final digits.

So, my questions are two:

1) if I create an iframe called "quiklik" both as name and as class/id, how do I tell the script to load URLS in it?

2) instead of storing each url in a biiig array, I thought I could create a "for" loop that would generate the final digits, then append these digits to the common url-string. But I don't know how to make the for loop run at a pre-determined speed.

I even tried searching in userscripts forums/script but couldn't find anything useful. So I don't even know if I'm re-inventing warm water.

Got any ideas/suggestions?

Right now my code's a mess. I'll post a draft on monday, as I'm leaving town for the week-end :)

Thanks in advance! :)

 
Naja Melan Scriptwright hi, first open eMule and get the following:
  • Professional javascript for web developpers by N C Zakas
  • Javascript Programmer'S Reference by C Wootton
they are your best buddies for learning javascript... You should read the one by Zakas cover to cover... you will never regret it...

there is also a guide about writing greasemonkey scripts here: http://diveintogreasemonkey.org/
the wiki might also prove useful: http://wiki.greasespot.net/Main_Page/

whilst they are downloading you could try something like:
(without doubt there is an error here or there, but just try it and see what you get)



document.body.addEventListener( "load", swapPages );

function swapPages()
{
  for( var i = 0; i < x; ++i )
  {
    setTimeout( "quiklik.src = generateURL( i );", 1000*i );
  }

  function generateURL( num )
  {
    //well obviously you have to do this yourself...lol
    //maybe you don't need this function at all. and you could just
    //concenate some strings like:
    // quiklik.scr = "www.google.com?search=" + i;
  }
}
 
alien_scum Scriptwright

That's a good start however setTimeout won't work like that as the stuff in quotes will evaluate in the page scope and hence have no access to your variables or functions, also the load event is redundant.

This will wait for the iframe to load then load the next url

function next() {
  if (i<x) {
    quiklik.src=generateURL(i);
    i++;
  }
}
quiklik=document.body.appendChild(document.createElement('iframe'));
quiklik.addEventListener("load",next,false);
var i=0;
next();

 
Naja Melan Scriptwright

yeah,

just learned that as well... This article seems pretty interesting on the XPCNativeWrapper issues:

http://www.oreillynet.com/lpt/a/6257

greets

ps: alien_scum, like your nifty solution...;-)

pps: in fact, after reconsideration, I think you fall for the same trap.... how where you expecting to get access to the generateURL function?
On top of that, the hole function swapPages will remain, because the eventlistener holds a reference to it. because generateURL is part of this function, it will still be there after the userscript finishes. the only question is, will it still be there when the setTimout code runs. Im not sure about that. any case it could easily be solved by this (i think, cant be bothered to test it):

btw it is still not ideal, and i think i would not use it like this in release version, because of the global variable that would be available to the host site.
After all i have come to think that often the best thing to do might be to built a wrapper of your own, and get pages with a GM_xmlhttpRequest, and then extract info from them and put it in your version of the site... this way their scripts don't run and you have total control, no? That way also the user script keeps running as long as the site is open. so these problems do no longer occur. See it more like a browser runs scripts that are programs displaying stuff for which these programs use the internet as a db to get information..., pages or xml documents from which they draw the right content to show to the user

A combination of both could do:

document.body.addEventListener( "load", swapPages );

function swapPages()
{
  for( var i = 0; i < x; ++i )
  {
    setTimeout( next, 1000*i );
  }

  function next()
  {
    if ( ++i < 100 ) 
    {
      quiklik.src = generateURL();
    }

    function generateURL()
    {
      //well obviously you have to do this yourself...lol
      //maybe you don't need this function at all. and you could just
      //concenate some strings like:
      // quiklik.scr = "www.google.com?search=" + i;
    }
  }
}

 
alien_scum Scriptwright

GreaseMonkey wraps each script in a function and calls it on the DomLoaded event so global variables in the script can't leak to the page. Ususaly you send a string to addEventListener, which evaluated in the page, and hence in it's scope, but this way addEventListener and setTimeout work by sending a pointer to the function so it can be called later. It then calls the function at the appropriate moment, the function will evaluate in it's original scope, in the case of a function in a userscript this scope will be the userscript, and hence the function will have access to all the function in that scope, eg GM_xhr, even though they are not accessible from the page which is triggering the event. My code will work (try it) and be entirely safe.

won't work:

setTimeout( "quiklik.src = generateURL( i );", 1000*i );

whereas the follow will:
setTimeout( function() {quiklik.src = generateURL( i );}, 1000*i );

or:
setTimeout(next, 1000*i );
function next() {quiklik.src = generateURL( i );}

If you just want the server to think you visited the page, eg competitions, polls or faking reading e-mail, GM_xhr could be a better solution. They will be quicker (no pictures css ect to load) and can all be sent simultaneously.

PS you code there wont work as when next gets called by setTimeout i==x

 
tcd Scriptwright

ok, I'm tearing my hair out...
this is the piece of code I've managed to come up with...

function QScycle()
{
   var ni = document.getElementById('footer');
   var QSname = "DangQuikLik";
   var QSiframe = document.createElement('div');
   QSiframe.setAttribute("id",QSname);
   var url = 'http://blablablah.com/';
   code_str  = "<iframe align=\"center\" frameborder=\"0\" height=\"100\" id=\"QuikLik\" "; 
   code_str += "name=\"QuikLik\" scrolling=\"no\" src=\""+ url +"\" width=\"50\">";
   code_str += "</iframe>";
   QSiframe.innerHTML = code_str;
   ni.appendChild(QSiframe);
   ni.insertBefore(QSiframe, ni.childNode);

   for (var n = min; n < max; n++)
   {
      setTimeout(function() {QuikLik.src = "http://blablablah.com/redirect.link.phtml?id=" + n;}, TO+n);
      if (n == (max-1)) {alert("I'm finished!");}
   };

   var ni = document.getElementById('footer');
   var olddiv = document.getElementById('DangQuikLik');
   ni.removeChild(olddiv);
};

window.addEventListener("load", QScycle, true);

The problem is that the Error console keeps saying that QuikLik is not defined :((

...In what language do I tell FF that the new url has to be injected in that specific iframe?

I might try the stupidest route: e.g. creating and destroying the iframe for EACH istance of n, but I'm sure that'll lead to trouble (FF crashes, memory leaks, what else...)

 
Descriptor Scriptwright

"QuikLik" is a window name and a document id, but you didn't define it anywhere in your JavaScript code above.

And I think it was mentioned twice that addEventListener("load") does nothing in Greasemonkey, unless you want window.QuikLik.addEventListener("load") (not sure about that).

 
alien_scum Scriptwright

Once more from the top...

var ni = document.getElementById('footer');
var QuikLik = document.createElement('iframe');
QuikLik.setAttribute("id",QuikLik);
ni.appendChild(QuikLik);

QuikLik.style="width:50px; height :50px"

function next() {
  if (n<max) {
    quiklik.src="http://blablablah.com/redirect.link.phtml?id=" + n;
    n++;
  } else {
    alert("I'm finished!");
    ni.removeChild(QuikLik);
  }
}
quiklik.addEventListener("load",next,false);
var n=0,max=10;
next();

Just copy and paste and you should be done.

As descriptor pointed out you need to define QuikLik. I dropped the div, it seamed like extra effort, the "load" event went as well as it is redundant unless the page runs some vital script onload that you have to wait for (which is very unlikely).

QuikLik.style="width:50px; height :50px"
could easily become
QuikLik.style.display='none';
if you don't want to see the iframe.

 
tcd Scriptwright

Thanks alien_scum! Your script worked :) a lot better than mine.

just a minor snag: if the redirect link points to a non-existent domain the loop (and also any timeout) doesn't go on, it sits and waits for something (what?)

...I swear, sometimes I feel that trying to write Javascript code is like teaching a deaf person how to speak cantonese...

I suppose I'll have to try the GM_xmlhttpRequest, right?

 
alien_scum Scriptwright

All in all I think gm_xhr would be the better solution.
This will do it if you don't need any notification that its been done,

for (var n = min; n < max; n++)
{
  GM_xmlhttpRequest({
    method: 'get',
    url: "http://blablablah.com/redirect.link.phtml?id=" + n
  });
};

 
tcd Scriptwright

Again, thanks Alien... however, as I found out, a simple gm_xhr wouldn't work as I'd wish: I need to simulate as best as I can an actual click (cookie included).

So I've rewritten the script...

function gmxhr(txt,link,n)
{
   QLurl = link + n;
   QLcookie = document.cookie;
   document.title = txt + n;
   GM_xmlhttpRequest({
      method: 'get',
      url: QLurl,
      headers: {
         'Host': 'www.ohyeah.com',
         'User-Agent': 'Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4'
         'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'
         'Accept-Language': 'en,it;q=0.7,fr;q=0.3'
         'Accept-Encoding': 'gzip,deflate'
         'Accept-Charset': 'UTF-8,*'
         'Keep-Alive': '200'
         'Connection': 'keep-alive'
         'Referer': 'http://www.ohyeah.com/'
         'Cookie': QLcookie
         }
   });
}

function next()
{
   for (var n = min; n < max; n++)
   {
      window.setTimeout(function(){gmxhr('PCLink #','http://www.ohyeah.com/redirect_click.phtml?item_id=',n)}, TO);
   }
}
window.addEventListener("load", next, true);

Haven't yet tested it. I'm still having two questions in my mind:
  1. Would the window.setTimeout() really slow down the damned script as I want it to do? (TO is set to 1 sec)
  2. Will gmxhr() really grab from my browser the cookie and use it in the GM_xhr()?
I feel like there are some inconsistencies in scoping within JS... :p

 
Joel H Scriptwright

It looks like you are running into scoping issues, but it's not a javascript issue. It's a greasemonkey issue. For various security reasons, greasemonkey has to run its javascript in its own sandbox. As a result of this, you occasionally have to jump through hoops if you want something to act on a delay or on some user action, as opposed to simply executing immediately onload. There is fairly extensive discussion of this on the wiki.

-Joel

 
tcd Scriptwright

*sigh* no matter what I do, the GM_xhr keeps running at full speed :(
Is there REALLY some way to slow down a for cycle with a GM_xhr in it, short of tying a 1ton weight to its legs?

 
Descriptor Scriptwright

Maybe it's getting too complicated, start over from the top of this thread and just rewrite your script. When it gets to be too much, I make a little hole and it all escapes - oh, sorry wrong movie.
If setTimeout is in a for loop, that just creates a bunch of function calls (up to "max" number all waiting for "TO" milliseconds), the script does not pause at a setTimeout, it continues on. It took me a while to grasp this myself, and it can be a pain to figure out how to get JavaScript to "wait".

 
tcd Scriptwright

That's what I'm tempted to do: erase the whole thing, take a breath, drink a coffee and take a stroll then restart from scratch.

One thing I noticed, though, with the FF add-on "tamper data", is that GM_xhr doesn't seem to send the "referrer" along with the other headers I specified. Is this normal or just a "feature" of this function? :)

:)

 
Descriptor Scriptwright

I imagine it's filtering that header out since people consider it to be a privacy issue (my tests show that opening in new tab don't send the Referer so why worry about it), but I can't say.
How do you see the headers? From server logs or something like LiveHTTPHeaders?
For me, LiveHTTPHeaders doesn't show this request at all, I don't really understand XMLHttpRequest but I at least got it to pop up an alert.

 
tcd Scriptwright

I use a Firefox add-on called Tamper Data, which lets you show in another window the flow of page requests sent by FF (and GM too) and see at a glance the headers of any request you pick up. I think it's similar to LiveHTTPHeaders, though I haven't tried this one.

Currently I'm googling for info on that thing of "Referer" field in GM_xhr.

 
Naja Melan Scriptwright

hi,

if you want the timeout to fire at different times, dont rely on your for loop. you have to consider they all get set pretty fast, but you are in control of the time they wait right? just give the second param of setTimeout different values. For example if you use n as a variable in your for loop, and you increment it on each go, assuming you start with 0, use this parameter: n*1000. this way the first one will fire immediately, the second after 1s, then after 2s, 3s, 4s, etc...

good luck

 
tcd Scriptwright

Naja,
I tried this too, but it looks like that in this specific case the for loop and the window.setTimeout function botch up the GM_xhr's reception of the numerical variable (so that what really got sent is only the max variable for n times (the range in n to max)). If I don't use setTimeout, the for sends correctly the numeric variable to GM_xhr, but things get messy as both my pc and my router try to send as fast as they can these GM_xhr (and I _know_ for sure that, on the receiving end, some firewall is gonna put my IP address in a temporary blacklist :D -- that's what I'd do on their side! --)

Do other kind of loops work as the for loop (while() and what else?) ?

 
alien_scum Scriptwright

To get it to send the the correct request you have to get it to remember what value n was the simplest way is to use a closure like so:

function getter(n){return function() {gmxhr('PCLink #','http://www.ohyeah.com/redirect_click.phtml?item_id=',n)}}
for (var n = min; n < max; n++) {
  window.setTimeout(getter(n), TO*(n-min));
}

 
tcd Scriptwright

Thanks alien, but i found out that a recursive function worked better :) In this moment I'm on another PC, as soon as I'm back home I'll post the exact code... Anyway, if memory serves me right, it's something like:

function next(n)
{  if (n < max)
   {  (timeout maths here);
      window.setTimeout(function() {gmxhr(n)}, to);
      n++;
      next(n);
   }
}

 
tcd Scriptwright

ok, the code I came up with is actually:

function next(i)
{  if (i < max)
   {  k = i +'';
      k = parseInt(k.slice(-2))+23;
      var t = (TO + i + (k*13));
      gmxhr('http://www.somesite.com/dangsponsor_click.phtml?item_id='+ i);
      window.setTimeout(function(){i++; next(i)},t);
   }
}

Again, thanks a lot to alien_scum, naja, descriptor and joel H :-)

 
jerone Scriptwright

Ok, because i think my problem is the same as here and i cant find the solution, i'll post my script;
I think the problem is thus with the setTimeout() function (marked strong), but i can't find the correct answer.
If someone can help me...

var closeCommand = function() {
	this.shiftOpacity = function(id) {
		if(document.getElementById(id).style.opacity) {
			if(document.getElementById(id).style.opacity == 0) {
				this.opacity(id, 0, 100, 500);
			}
			else {
				this.opacity(id, 100, 0, 500);
			}
		}
		else {
			document.getElementById(id).style.opacity = 1
			this.opacity(id, 100, 0, 500);
		}
	}
	
	this.opacity = function(id, opacStart, opacEnd, millisec) {
		var speed = Math.round(millisec / 100);
		var timer = 0;
		if(opacStart > opacEnd) {
			for(i = opacStart; i >= opacEnd; i--) {
				window.setTimeout(this.changeOpac(i,id),(timer * speed));
				this.changeOpac(i,id);
				timer++;
			}
		} 
		else if(opacStart < opacEnd) {
			for(i = opacStart; i <= opacEnd; i++)
				{
				window.setTimeout(this.changeOpac(i,id),(timer * speed));
				timer++;
			}
		}
	}
	
	//change the opacity for different browsers
	this.changeOpac = function(opacitys, id) {
		var object = document.getElementById(id).style;
		object.opacity = (opacitys / 100);
		object.MozOpacity = (opacitys / 100);
		object.KhtmlOpacity = (opacitys / 100);
		object.filter = "alpha(opacity=" + opacitys + ")";
	} 

	this.shiftOpacity("overlayDIV");
}