Go Back   CodingForums.com > :: Client side development > JavaScript programming

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 11-28-2010, 03:39 AM   PM User | #1
pavinder
Regular Coder

 
Join Date: Mar 2005
Posts: 115
Thanks: 3
Thanked 0 Times in 0 Posts
pavinder is an unknown quantity at this point
nested timeouts - how to implement?

I'm running into issues while trying to make some simple code to loop through and display different combinations of 3 colours.
I want to show 3 boxes on screen and then loop through/increment each possible combination of colours.

For simplicity's sake I'm trying to go from black to white in each box like this:

(box1rgb/box2rgb/box3rgb)

step 1: 000 000 000
step 2: 000 000 111
step 3: 000 000 222
.
.
.
step 256: 000 000 255255255
step 257: 000 111 000
step 258: 000 111 111
step 259: 000 111 222
.
.
.
step 512: 000 111 255255255
step 513: 000 222 000
step 514: 000 222 111
.
etc.

Yes, I know the thing will take a long time to complete!

The colour is being set by the statement
document.getElementById("box#").style.backgroundColor = "rgb(" + p1 + "," + p1 + "," + p1 + ")"; where p1 is the incrementing variable.

Firstly I tried to do it using nested "for" loops.
Code:
  for (p3=0;p3<=255;p3++) {

    document.getElementById("box03").style.backgroundColor  = "rgb(" + p3 + "," + p3 + "," + p3 + ")";
  
    for (p2=0;p2<=255;p2++) {

      document.getElementById("box02").style.backgroundColor  = "rgb(" + p2 + "," + p2 + "," + p2 + ")";
    
      for (p1=0;p1<=255;p1++) {
      
        document.getElementById("box01").style.backgroundColor  = "rgb(" + p1 + "," + p1 + "," + p1 + ")";
		
      }
    }
  }
But of course the code executes far too rapidly for each combination to be seen. It all chewed up lots of CPU and nothing was smooth.

To slow the process, I tried a single recursive loop using a timeout statement:
Code:
function loopandshow() {
  document.getElementById("box01").style.backgroundColor  = "rgb(" + currentcolour + "," + currentcolour + "," + currentcolour + ")";
  currentcolour += 1;
  if (currentcolour <= 255) window.setTimeout("loopandshow();", 20);
}
This worked smoothly for a single box but I couldn't work out how to nest this format 3 levels deep and have each level increment only when the deeper level had completed the cycle.

Any advice would be gratefully appreciated.

Thanks in advance.
pavinder is offline   Reply With Quote
Old 11-28-2010, 12:37 PM   PM User | #2
vwphillips
Senior Coder

 
Join Date: Mar 2005
Location: Portsmouth UK
Posts: 4,355
Thanks: 3
Thanked 458 Times in 445 Posts
vwphillips is a jewel in the roughvwphillips is a jewel in the roughvwphillips is a jewel in the rough
may help

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title></title>
<style type="text/css">
/*<![CDATA[*/
.message {
  width:100px;height:100px;background-Color:#FFCC66;
}

/*]]>*/
</style>
<script type="text/javascript">
// AnimateII (08-October-2010)
// by Vic Phillips http://www.vicsjavascripts.org.uk

// To progressively change the
// left, top, width, height, color, background-Color, Opacity, border-Width, border-Color, clip
// style of an element over a specified period of time.

// **** Application Notes

// **** The HTML Code
//
// when moving an element the inline or class rule style position of the element should be assigned as
// 'position:relative;' or 'position:absolute;'
// If not assigned the style position of the element will be assigned as 'position:relative;' by the script.
//
// The element would normally be assigned a unique ID name.
//

// **** Initialising the Script.
//
// The script is initialised by assigning an instance of the script to a variable.
// e.g A = new zxcAnimateII('left','id1')
// where:
//  A           = a global variable                                                               (variable)
//  parameter 0 = the mode(see Note 1).                                                           (string)
//  parameter 1 = the unique ID name or element object.                                           (string or element object)
//  parameter 2 = the initial value.                                                              (digits, default = 0)
//

// **** Executing the Effect
//
// The effect is executed by an event call to function 'A.animate(10,800 ,5000,[10,800]);'
// where:
//  A           = the global referencing the script instance.                                 (variable)
//  parameter 0 = the start value.                                                            (digits, for opacity minimum 0, maximum 100)
//  parameter 1 = the finish value.                                                           (digits, for opacity minimum 0, maximum 100)
//  parameter 2 =  period of time between the start and finish of the effect in milliseconds. (digits or defaults to previous or 0(on first call) milliSeconds)
//  parameter 3 = (optional) the type of progression, 'sin', 'cos' or 'liner'.                (string, default = 'liner')
//                 'sin' progression starts fast and ends slow.
//                 'cos' progression starts slow and ends fast.

// **** Notes
//
//  Note 1:  Examples modes: 'left', 'top', 'width', 'height', 'opacity', clip, 'color', 'background-Color'.
//
//  Note 2:  The default units(excepting opacity) are 'px'.
//           For hyphenated modes, the first character after the hyphen must be upper case, all others lower case.
//
//  Note 3:  To animate colors('color' or 'background-Color') the start and finish values may be.
//           HEX('#FF0000') or RGB('rgb(255,0,0)'), abbreviated HEX or named colors are not allowed.
//
//  Note 4:  To animate 'clip' the start and finish values may be
//            arrays of the top, right, bottom and left values([0,100,20,0])
//           or the standard clip format 'rect(0px,100px,20px,0px)'.
//
//  Note 5:  It may be required to access the current value of the effect.
//           An array storing the current, start and finish values of the element effect may be accessed
//           from the element effect.data as fields 0, 1 and 2 respectively && each field is an array.
//           For color each field is an array storing the R, G, B values.
//           The current effect value is recorded in A.data[0].
//
//  Note 6:  A function may be called on completion of the effect by assigning the function
//           to the animator instance property .Complete.
//           e.g. [instance].Complete=function(){ alert(this.data[0]); };
//


// **** Functional Code(2.61K) - NO NEED to Change


function zxcAnimateII(mde,obj,srt){
 this.obj=(typeof(obj)=='string')?document.getElementById(obj):obj;
 this.mde=mde.replace(/[-#]/g,'');
 this.data=[srt?this.convert(srt,this.mde):0];
 this.to=null;
}

zxcAnimateII.prototype={

 animate:function(srt,fin,ms,c){
  this.std=!(this.mde.indexOf('olor')>0||this.mde=='clip'||this.mde=='opacity');
  srt=this.convert(srt,this.mde);
  fin=this.convert(fin,this.mde);
  this.pos=true;
  for (var z0=0;z0<srt.length;z0++){
   if (srt[z0]<0||fin[z0]<0){
    this.pos=false;
   }
  }
  c=c||'';
  this.c=c.charAt(0).toLowerCase();
  this.mS=ms||2000;
  clearTimeout(this.to);
  this.srttime=new Date().getTime();
  this.inc=Math.PI/(2*this.mS);
  this.data=[[],srt,fin];
  this.cng();
 },

 cng:function(){
  var ms=new Date().getTime()-this.srttime;
  for (var z0=0;z0<this.data[2].length;z0++){
   this.data[0][z0]=Math.floor(this.c=='s'?(this.data[2][z0]-this.data[1][z0])*Math.sin(this.inc*ms)+this.data[1][z0]:this.c=='c'?(this.data[2][0])-(this.data[2][z0]-this.data[1][z0])*Math.cos(this.inc*ms):(this.data[2][z0]-this.data[1][z0])/this.mS*ms+this.data[1][z0]);
   if (this.pos&&this.data[0][z0]<0){
    this.data[0][z0]=0;
   }
  }
  this.cngstyle();
  if (ms<this.mS){
   this.to=setTimeout(function(oop){ return function(){oop.cng(); } }(this), 10);
  }
  else {
   this.data[0]=this.data[2];
   this.cngstyle(this.data[0]);
   if (this.Complete){
    this.Complete(this);
   }
  }
 },

 cngstyle:function(){
  var v=this.data[0];
  if (this.std){
   this.obj.style[this.mde]=v[0]+'px';
  }
  else if (this.mde.indexOf('olor')>0){
   this.obj.style[this.mde]='rgb('+v[0]+','+v[1]+','+v[2]+')';
  }
  else if (this.mde=='clip'){
   this.obj.style[this.mde]='rect('+v[0]+'px,'+v[1]+'px,'+v[2]+'px,'+v[3]+'px)';
  }
  else if (this.mde=='opacity'){
   zxcOpacity(this.obj,v[0]);
  }
 },

 convert:function(col,mde){
  if (typeof(col)=='object'){
   return col;
  }
  else if (mde=='clip'){
   col=col.replace(/[rect()px]/g,'').split(',');
   return [col[0]*1,col[1]*1,col[2]*1,col[3]*1];
  }
  else if (!col.toString().match('#')){
   return [parseInt(col)];
  }
  else {
   col=parseInt(col.substring(1,3),16)+','+parseInt(col.substring(3,5),16)+','+parseInt(col.substring(5,7),16);
   col=col.replace(/[rgb()\s]/g,'').split(',');
   return [parseInt(col[0]),parseInt(col[1]),parseInt(col[2])];
  }
 }
}

function zxcOpacity(obj,opc){
 if (opc<0||opc>100) return;
 obj.style.filter='alpha(opacity='+opc+')';
 obj.style.opacity=obj.style.MozOpacity=obj.style.WebkitOpacity=obj.style.KhtmlOpacity=opc/100-.001;
}

</script>
</head>

<body>
<div class="message" id="tst" ></div>

<script type="text/javascript">
/*<![CDATA[*/

var box=new zxcAnimateII('background-Color','tst')

box.animate('#FFCC66','#FFFFCC',1000);
/*]]>*/
</script>
</body>

</html>
__________________
Vic

God Loves You and will never love you less.

http://www.vicsjavascripts.org.uk/

If my post has been useful please donate to http://www.operationsmile.org.uk/
vwphillips is offline   Reply With Quote
Users who have thanked vwphillips for this post:
pavinder (11-30-2010)
Old 11-28-2010, 03:07 PM   PM User | #3
pavinder
Regular Coder

 
Join Date: Mar 2005
Posts: 115
Thanks: 3
Thanked 0 Times in 0 Posts
pavinder is an unknown quantity at this point
Quote:
Originally Posted by vwphillips View Post
may help

Code:
.
.
.
box.animate('#FFCC66','#FFFFCC',1000);
/*]]>*/
</script>
</body>

</html>
Thanks for the code sample Vic - a really elegant effect, gratefully received.
I've tried sequencing some animations and the effect is quite stunning.

I wonder if this method could enable the "nesting" of timeout functions I'm aiming for or whether it's limit is sequential timeout functions?

Thanks in advance.
pavinder is offline   Reply With Quote
Old 11-29-2010, 08:33 AM   PM User | #4
pavinder
Regular Coder

 
Join Date: Mar 2005
Posts: 115
Thanks: 3
Thanked 0 Times in 0 Posts
pavinder is an unknown quantity at this point
Just to elaborate on what I need to achieve, the problem with timeouts is although I can create a delay until the cycle on one level has finished before executing code to change the layer above it (as Vic's code does), this isn't enough.

Vic's example allows me to execute an entire cycle of change one one level after another level has finished. For my project each level of loop has to increment just a single step after the layer below it completes a cycle.

So I think I need some hybrid between nested for loops and timeouts (if such a beast exists).
pavinder is offline   Reply With Quote
Old 11-29-2010, 09:58 AM   PM User | #5
Moodyxh
New to the CF scene

 
Join Date: Nov 2010
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Moodyxh is an unknown quantity at this point
you guys have done great job!
Moodyxh is offline   Reply With Quote
Old 11-29-2010, 10:15 AM   PM User | #6
pavinder
Regular Coder

 
Join Date: Mar 2005
Posts: 115
Thanks: 3
Thanked 0 Times in 0 Posts
pavinder is an unknown quantity at this point
Quote:
Originally Posted by Moodyxh View Post
you guys have done great job!
I think Vic has, I'm just asking for help!
pavinder is offline   Reply With Quote
Old 11-29-2010, 01:50 PM   PM User | #7
vwphillips
Senior Coder

 
Join Date: Mar 2005
Location: Portsmouth UK
Posts: 4,355
Thanks: 3
Thanked 458 Times in 445 Posts
vwphillips is a jewel in the roughvwphillips is a jewel in the roughvwphillips is a jewel in the rough
Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title></title>
<style type="text/css">
/*<![CDATA[*/
.message {
  width:100px;height:100px;background-Color:#FFCC66;
}

/*]]>*/
</style>
<script type="text/javascript">
// AnimateII (08-October-2010)
// by Vic Phillips http://www.vicsjavascripts.org.uk

// To progressively change the
// left, top, width, height, color, background-Color, Opacity, border-Width, border-Color, clip
// style of an element over a specified period of time.

// **** Application Notes

// **** The HTML Code
//
// when moving an element the inline or class rule style position of the element should be assigned as
// 'position:relative;' or 'position:absolute;'
// If not assigned the style position of the element will be assigned as 'position:relative;' by the script.
//
// The element would normally be assigned a unique ID name.
//

// **** Initialising the Script.
//
// The script is initialised by assigning an instance of the script to a variable.
// e.g A = new zxcAnimateII('left','id1')
// where:
//  A           = a global variable                                                               (variable)
//  parameter 0 = the mode(see Note 1).                                                           (string)
//  parameter 1 = the unique ID name or element object.                                           (string or element object)
//  parameter 2 = the initial value.                                                              (digits, default = 0)
//

// **** Executing the Effect
//
// The effect is executed by an event call to function 'A.animate(10,800 ,5000,[10,800]);'
// where:
//  A           = the global referencing the script instance.                                 (variable)
//  parameter 0 = the start value.                                                            (digits, for opacity minimum 0, maximum 100)
//  parameter 1 = the finish value.                                                           (digits, for opacity minimum 0, maximum 100)
//  parameter 2 =  period of time between the start and finish of the effect in milliseconds. (digits or defaults to previous or 0(on first call) milliSeconds)
//  parameter 3 = (optional) the type of progression, 'sin', 'cos' or 'liner'.                (string, default = 'liner')
//                 'sin' progression starts fast and ends slow.
//                 'cos' progression starts slow and ends fast.

// **** Notes
//
//  Note 1:  Examples modes: 'left', 'top', 'width', 'height', 'opacity', clip, 'color', 'background-Color'.
//
//  Note 2:  The default units(excepting opacity) are 'px'.
//           For hyphenated modes, the first character after the hyphen must be upper case, all others lower case.
//
//  Note 3:  To animate colors('color' or 'background-Color') the start and finish values may be.
//           HEX('#FF0000') or RGB('rgb(255,0,0)'), abbreviated HEX or named colors are not allowed.
//
//  Note 4:  To animate 'clip' the start and finish values may be
//            arrays of the top, right, bottom and left values([0,100,20,0])
//           or the standard clip format 'rect(0px,100px,20px,0px)'.
//
//  Note 5:  It may be required to access the current value of the effect.
//           An array storing the current, start and finish values of the element effect may be accessed
//           from the element effect.data as fields 0, 1 and 2 respectively && each field is an array.
//           For color each field is an array storing the R, G, B values.
//           The current effect value is recorded in A.data[0].
//
//  Note 6:  A function may be called on completion of the effect by assigning the function
//           to the animator instance property .Complete.
//           e.g. [instance].Complete=function(){ alert(this.data[0]); };
//


// **** Functional Code(2.61K) - NO NEED to Change


function zxcAnimateII(mde,obj,srt){
 this.obj=(typeof(obj)=='string')?document.getElementById(obj):obj;
 this.mde=mde.replace(/[-#]/g,'');
 this.data=[srt?this.convert(srt,this.mde):0];
 this.to=null;
}

zxcAnimateII.prototype={

 animate:function(srt,fin,ms,c){
  this.std=!(this.mde.indexOf('olor')>0||this.mde=='clip'||this.mde=='opacity');
  srt=this.convert(srt,this.mde);
  fin=this.convert(fin,this.mde);
  this.pos=true;
  for (var z0=0;z0<srt.length;z0++){
   if (srt[z0]<0||fin[z0]<0){
    this.pos=false;
   }
  }
  c=c||'';
  this.c=c.charAt(0).toLowerCase();
  this.mS=ms||2000;
  clearTimeout(this.to);
  this.srttime=new Date().getTime();
  this.inc=Math.PI/(2*this.mS);
  this.data=[[],srt,fin];
  this.cng();
 },

 cng:function(){
  var ms=new Date().getTime()-this.srttime;
  for (var z0=0;z0<this.data[2].length;z0++){
   this.data[0][z0]=Math.floor(this.c=='s'?(this.data[2][z0]-this.data[1][z0])*Math.sin(this.inc*ms)+this.data[1][z0]:this.c=='c'?(this.data[2][0])-(this.data[2][z0]-this.data[1][z0])*Math.cos(this.inc*ms):(this.data[2][z0]-this.data[1][z0])/this.mS*ms+this.data[1][z0]);
   if (this.pos&&this.data[0][z0]<0){
    this.data[0][z0]=0;
   }
  }
  this.cngstyle();
  if (ms<this.mS){
   this.to=setTimeout(function(oop){ return function(){oop.cng(); } }(this), 10);
  }
  else {
   this.data[0]=this.data[2];
   this.cngstyle(this.data[0]);
   if (this.Complete){
    this.Complete(this);
   }
  }
 },

 cngstyle:function(){
  var v=this.data[0];
  if (this.std){
   this.obj.style[this.mde]=v[0]+'px';
  }
  else if (this.mde.indexOf('olor')>0){
   this.obj.style[this.mde]='rgb('+v[0]+','+v[1]+','+v[2]+')';
  }
  else if (this.mde=='clip'){
   this.obj.style[this.mde]='rect('+v[0]+'px,'+v[1]+'px,'+v[2]+'px,'+v[3]+'px)';
  }
  else if (this.mde=='opacity'){
   zxcOpacity(this.obj,v[0]);
  }
 },

 convert:function(col,mde){
  if (typeof(col)=='object'){
   return col;
  }
  else if (mde=='clip'){
   col=col.replace(/[rect()px]/g,'').split(',');
   return [col[0]*1,col[1]*1,col[2]*1,col[3]*1];
  }
  else if (!col.toString().match('#')){
   return [parseInt(col)];
  }
  else {
   col=parseInt(col.substring(1,3),16)+','+parseInt(col.substring(3,5),16)+','+parseInt(col.substring(5,7),16);
   col=col.replace(/[rgb()\s]/g,'').split(',');
   return [parseInt(col[0]),parseInt(col[1]),parseInt(col[2])];
  }
 }
}

function zxcOpacity(obj,opc){
 if (opc<0||opc>100) return;
 obj.style.filter='alpha(opacity='+opc+')';
 obj.style.opacity=obj.style.MozOpacity=obj.style.WebkitOpacity=obj.style.KhtmlOpacity=opc/100-.001;
}

</script>
</head>

<body>
<div class="message" id="tst0" ></div>
<div class="message" id="tst1" ></div>
<div class="message" id="tst2" ></div>

<script type="text/javascript">
/*<![CDATA[*/

var box0=new zxcAnimateII('background-Color','tst0');
var box1=new zxcAnimateII('background-Color','tst1');
box1.Complete=function(){
 box0.animate('#FFCC66','#FFFFCC',1000);
}
var box2=new zxcAnimateII('background-Color','tst2');
box2.Complete=function(){
 box1.animate('#FFCC66','#FFFFCC',1000);
}
box2.animate('#FFCC66','#FFFFCC',1000);
/*]]>*/
</script>
</body>

</html>
__________________
Vic

God Loves You and will never love you less.

http://www.vicsjavascripts.org.uk/

If my post has been useful please donate to http://www.operationsmile.org.uk/
vwphillips is offline   Reply With Quote
Old 11-29-2010, 03:55 PM   PM User | #8
pavinder
Regular Coder

 
Join Date: Mar 2005
Posts: 115
Thanks: 3
Thanked 0 Times in 0 Posts
pavinder is an unknown quantity at this point
Sorry, Vic, maybe I didn't explain quite clearly enough - I don't need the 3 boxes to change sequentially. I need them to change in a similar way to a digital counter.

That is, once the bottom box has completed a full cycle of change from black to white (for the sake of example let's say it takes 128 steps), then the middle box will increment by just 1 step. Once the bottom box has completed its second full 128-step cycle, the middle box will increment another single step.

In this way it will take 128 black-->white cycles of the bottom box until the middle box completes a single cycle.
And at this point the top box will increment the first step of its 128-step cycle.

So over time, for each cycle of the top box the middle box will cycle 128 times and the bottom box will complete 128x128=16,384 cycles.

It's this conditionality which has me thinking in terms of nested loops, but if I use the zxcAnimateII() function within another function within another function then how can the code know how to just increment a single step and how to suspend processing until the box underneath it has completed a full cycle?

Of course I could set the period of time of the function to increasingly high powers of the innermost loop, but very soon (as I add a few more boxes) the numbers exceed the allowable maximum.
pavinder is offline   Reply With Quote
Old 11-30-2010, 08:46 AM   PM User | #9
pavinder
Regular Coder

 
Join Date: Mar 2005
Posts: 115
Thanks: 3
Thanked 0 Times in 0 Posts
pavinder is an unknown quantity at this point
Received a suggestion from elsewhere which helps incredibly and should be all I need to continue from here.

I've copied it below in case anyone else is interested.

Thanks again Vic for your comments and advice, much appreciated, and I can see some very interesting ways the script you shared might help in future online artworks.

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>loopandShow</title>
<style type="text/css">
#box1, #box2, #box3 {
  float: left; margin: 20px;
  width: 200px; height : 200px;
  background: black;
}
</style>
</head>
<body>
<div id = 'box1'></div>
<div id = 'box2'></div>
<div id = 'box3'></div>
<script type="text/javascript">
// From: http://www.webdeveloper.com/forum/showthread.php?t=239101

function loopandShow(els){
  // define local variables
  var elNodesStyles = [];
  var timer = null;
  var elColor = [];

  // Store nodes in an array.
  // Therefore avoiding having to search the DOM repeatedly in colourInc.
  for (var i = 0, len = els.length; i < len; i+=1){
    elNodesStyles.push(document.getElementById(els[i]).style);
    elColor.push(0);
  }

  var colourInc = function(){
    // if we've reached 255 in 3rd box, clear the timer and return
    if (elColor[2] > 255) { // optional setting all box colors to 255 here, if desired
      clearInterval(timer); timer = null; return;
    }    
    // loop through our nodes, and set property
    elNodesStyles[0].background = "rgb(" + elColor[0] + "," + elColor[0] + "," + elColor[0] + ")";
    elNodesStyles[1].background = "rgb(" + elColor[1] + "," + elColor[1] + "," + elColor[1] + ")";
    elNodesStyles[2].background = "rgb(" + elColor[2] + "," + elColor[2] + "," + elColor[2] + ")";
// adjust speed of changes with different increment assignments below
    elColor[0]+=8;;
    if (elColor[0] > 255) { elColor[1]+=16; elColor[0] = 0; }
    if (elColor[1] > 255) { elColor[2]+=32; elColor[1] = 0; }
  };  
  
  timer = setInterval(colourInc, 5);
}

// an array of elements
var elems = ['box1', 'box2', 'box3'];
loopandShow(elems);

</script>
</body>
pavinder is offline   Reply With Quote
Reply

Bookmarks

Tags
asyncronous, looping, nested, timeout

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 01:27 AM.


Advertisement
Log in to turn off these ads.