Following the one-year anniversary of having quit smoking, now that I no longer take smoke breaks, I do the next best thing - I make my browser take smoke breaks. Heh heh. For real, though. This occasion's web tutorial is centered around the idea of browser idle time. When the browser is left alone for a suitable amount of time, a screensaver comes up! And in this case, I want it to feature a smoking cigarette.
And the more time passes, the shorter I want the cigarette to get. Oh, and there needs to be animation.
Can I squeeze all of that into a single-part web tutorial? Let's find out!
Some HTML as usual. There's some Lorem Ipsum text, and a div with the id
overlay. In the CSS, we want to set the outline for all divs to
red.
<!DOCTYPE html>
<html>
<head>
<title>Screensaver</title>
<style>
div { outline: 1px solid red; }
</style>
<script>
</script>
</head>
<body>
<h1>Some sample text. Leave this screen alone for 5 seconds to see the popup!</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque et justo sit amet libero interdum bibendum at nec dui. Mauris lacinia sapien nec blandit dapibus. Nunc suscipit in nunc in finibus. Praesent mi arcu, convallis auctor auctor luctus, gravida eget enim. Nunc quis finibus nisl, semper hendrerit nisl. Curabitur faucibus, velit varius efficitur suscipit, nibh ipsum bibendum turpis, placerat elementum nulla lorem eget diam. Phasellus lobortis aliquet leo, non aliquet mauris volutpat vel. Aliquam facilisis dui id sapien dignissim vulputate. Nullam cursus convallis sem vel maximus. Sed ac sollicitudin justo, at vehicula ligula. Praesent sit amet tellus massa. Nam nec varius ipsum.</p>
<p>Cras varius, nisl vitae lobortis sodales, purus ligula dictum eros, volutpat finibus neque sem et ligula. Vivamus id odio varius, blandit elit sit amet, scelerisque enim. Sed luctus molestie leo, suscipit ultricies nisl elementum id. Donec lacus erat, laoreet vel viverra vel, aliquam vitae elit. Vestibulum venenatis congue lacus a facilisis. Nulla condimentum, metus volutpat rhoncus maximus, purus mauris imperdiet dolor, at bibendum lacus justo nec risus. Praesent scelerisque libero magna, at lobortis justo convallis vel. Suspendisse cursus, odio ultricies auctor laoreet, ligula leo vulputate diam, et efficitur mauris nisl et urna. Quisque sit amet rutrum magna. Aenean at vestibulum urna. Nam ornare justo a tortor molestie auctor. Quisque at malesuada mi, volutpat feugiat sem.</p>
<p>Vestibulum luctus tempor ligula. Nulla id tortor ut est rutrum viverra. Etiam nec sapien id massa egestas dapibus at ut ipsum. Nunc sed tortor euismod, aliquet arcu et, commodo justo. Proin pretium vel neque sed maximus. In vitae vestibulum quam. Integer quis ex in ligula varius tempus. Donec diam arcu, faucibus eu aliquet nec, dapibus at sem. Sed convallis urna neque, tristique condimentum mauris condimentum vel. Praesent eu interdum quam, at ullamcorper augue. Mauris euismod odio libero, vel pharetra lorem egestas et. Nam laoreet ultricies venenatis. Integer ante risus, commodo nec eros ac, convallis tempus magna. Maecenas dictum lacus magna.</p>
<p>Proin eu iaculis felis, sed lobortis lectus. Pellentesque malesuada diam eu porttitor aliquam. Fusce posuere dapibus odio vitae suscipit. Mauris consectetur, tortor et pulvinar auctor, ante eros ornare nunc, non fringilla neque quam sed erat. In velit turpis, ultricies ut egestas in, porttitor eget erat. Sed et risus molestie, maximus mauris eu, accumsan sem. Sed pellentesque feugiat elit. Integer nisl nulla, condimentum eu purus id, vestibulum commodo ligula. Phasellus consectetur, justo in mollis egestas, nisl felis laoreet ipsum, ac feugiat nunc ante sit amet risus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla in enim lacus. Proin nulla dui, dictum at porttitor et, consectetur ac augue. Aliquam fringilla ex nec diam efficitur, vel vehicula mi molestie. Phasellus pellentesque in sem vitae porta. Nulla ut ultricies enim. Duis eu mauris sit amet lectus mattis efficitur molestie non magna.</p>
<div id="overlay">
</div> </body>
</html>
Now, the modal popup is going to be visible by default - for now. For that we have the styling for
overlay.
position will be
absolute, and we will set
height and
width properties to cover the entire screen with a translucent
black background.
left and
top properties will be at 0px so as to begin on the top left corner of the screen. And of course,
display is set to
block to be visible by default.
<style>
div { outline: 0px solid red; }
#overlay
{
width: 100%;
height: 100%;
position: absolute;
left: 0px;
top: 0px;
background-color: rgba(0, 0, 0, 0.5);
display: block;
}
</style>
The first step of many steps!
Let's follow up by adding a div, styled with the CSS class
content, in there.
<div id="overlay">
<div class="content">
</div>
</div>
We'll style it like this - a square with a
black background and rounded corners set in the middle of
overlay.
<style>
div { outline: 0px solid red; }
#overlay
{
width: 100%; height: 100%; position: absolute; left: 0px; top: 0px; background-color: rgba(0, 0, 0, 0.5); display: block; }
.content
{
width: 500px;
height: 500px;
margin: 5% auto 0 auto;
background-color: rgba(0, 0, 0, 1);
border-radius: 30px;
}
</style>
Yep, here's that
black square. Impossible to miss.
We introduce a new div inside that one, and style it using the CSS class
cigarette.
<div id="overlay">
<div class="content">
<div class="cigarette">
</div>
</div>
</div>
This is how we style
cigarette. It is a long vertical rectangle set in the middle of its parent,
content. Think of it as a holder for all the divs that will make up the components of the cigarette.
.content
{
width: 500px;
height: 500px;
margin: 5% auto 0 auto;
background-color: rgba(0, 0, 0, 1);
border-radius: 30px;
}
.cigarette
{
width: 50px;
height: 400px;
margin: 10% auto 0 auto;
}
All in position, so far!
Now we'll do the individual parts of the cigarette. We have two divs - the first styled using
body and the second styled using
butt.
<div class="cigarette">
<div class="body">
</div>
<div class="butt">
</div>
</div>
body takes up full width but only 300 out of the 400 pixels on offer. There is explicitly no background.
.cigarette
{
width: 50px;
height: 400px;
margin: 10% auto 0 auto;
}
.body
{
width: 100%;
height: 300px;
background-color: none;
}
butt, similarly, takes up fill width and has only 100 pixels height. We have as its background a linear gradient, going from
brown,
yellow, then
brown again.
.cigarette
{
width: 50px;
height: 400px;
margin: 10% auto 0 auto;
}
.butt
{
width: 100%;
height: 100px;
background-color: rgba(255, 170, 0, 1);
background: linear-gradient(90deg,rgba(255, 170, 0, 1) 0%, rgba(255, 255, 170, 1) 50%, rgba(255, 170, 0, 1) 100%);
}
.body
{
width: 100%;
height: 300px;
background-color: none;
}
This is what it looks like now. We'll be working on the empty-looking div next.
In
body, we nest two more divs. Instead of classes, they will have ids because this will make them easier to manipulate via JavaScript later. The ids are
empty (because that div represents "empty" space) and
burnable (because it's the part of the cigarette that gets "burned").
<div class="body">
<div id="empty">
</div>
<div id="burnable">
</div>
</div>
body has 300 pixels in height, so
empty takes up 50 and
burnable takes up 250.
burnable has a linear background that goes from
light grey,
white, then
light grey again.
.body
{
width: 100%;
height: 300px;
background-color: none;
}
#empty
{
width: 100%;
height: 50px;
}
#burnable
{
width: 100%;
height: 250px;
background-color: rgba(255, 255, 255, 1);
background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}
So now we have an unlit cigarette.
In
burnable, we have a div styled using the
tip CSS class.
<div id="burnable">
<div class="tip">
</div>
</div>
tip has a height of 5 pixels and takes up full width; that's all there is to it.
#burnable
{
width: 100%;
height: 250px;
background-color: rgba(255, 255, 255, 1);
background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}
.tip
{
width: 100%;
height: 5px;
}
In that div, let's add three divs, each styled using the
ember CSS class.
<div id="burnable">
<div class="tip">
<div class="ember"></div>
<div class="ember"></div> <div class="ember"></div> </div>
</div>
ember is floated left and has an
orange background.
.tip
{
width: 100%;
height: 5px;
}
.ember
{
float: left;
background-color: rgba(250, 150, 0, 1);
}
However, height and width vary according to their order. There are three divs in there, styled using
ember, and we use the
nth-of-type pseudoselector, passing in either "odd" or "even" and then adjusting the width and height accordingly. The middle (or second) "ember" is supposed to be the largest one.
.ember
{
float: left;
background-color: rgba(250, 150, 0, 1);
}
.ember:nth-of-type(odd)
{
width:15px;
height:3px;
}
.ember:nth-of-type(even)
{
width:20px;
height:5px;
}
Just for fun, let's add a bit of animation. This is totally superfluous. The animation name is
emberglow and we want it to last for 2 seconds, run forever, and alternate smoothly between states. You'll see that for
emberglow, I basically have background go from
orange to
red.
.ember
{
float: left;
background-color: rgba(250, 150, 0, 1);
animation-name: emberglow;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.ember:nth-of-type(odd)
{
width:15px;
height:3px;
}
.ember:nth-of-type(even)
{
width:20px;
height:5px;
}
@keyframes emberglow
{
from { background-color: rgba(250, 150, 0, 1); }
to { background-color: rgba(255, 0, 0, 1); }
}
There you go, a glowing tip! I feel like there's a filthy joke I could insert here, but let's move on...
In
empty, we insert divs. Several divs. But let's just start with three. Style them using the CSS class
smoke.
<div id="empty">
<div class="smoke"></div>
<div class="smoke"></div>
<div class="smoke"></div>
</div>
Give each of them a random
height,
weight,
margin-top and
margin-left property. Do bear in mind that the effective width of
empty is 50 pixels due to its parents, so the sum of
margin-left and half of
width shouldn't exceed 50, otherwise we'll have an overflow problem. Similarly, if we subtract half of
width from
margin-left, it should not be less than 0.
width and
height should be the same. You can have negative values for
margin-top if you want the divs to overlap.
<div id="empty">
<div class="smoke" style="width:15px;height:15px;margin-top:5px;margin-left:25px;"></div>
<div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
<div class="smoke" style="width:18px;height:18px;margin-top:0px;margin-left:5px;"></div>
</div>
smoke has a translucent
white background and a nice fuzzy
grey outline by way of the
box-shadow property.
#empty
{
width: 100%;
height: 50px;
background-color: rgba(0, 0, 0, 1);
}
.smoke
{
background-color: rgba(255, 255, 255, 0.2);
border-radius: 50%;
box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
}
#burnable
{
width: 100%;
height: 250px;
background-color: rgba(255, 255, 255, 1);
background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}
And that's how the first three divs would look like.
Add several more! Make sure
width,
height,
margin-top and
margin-left properties vary.
<div id="empty">
<div class="smoke" style="width:15px;height:15px;margin-top:5px;margin-left:25px;"></div>
<div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
<div class="smoke" style="width:18px;height:18px;margin-top:0px;margin-left:5px;"></div>
<div class="smoke" style="width:10px;height:10px;margin-top:-5px;margin-left:30px;"></div>
<div class="smoke" style="width:15px;height:15px;margin-top:0px;margin-left:20px;"></div>
<div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:15px;"></div>
<div class="smoke" style="width:8px;height:8px;margin-top:-5px;margin-left:13px;"></div>
<div class="smoke" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
<div class="smoke" style="width:8px;height:8px;margin-top:0px;margin-left:22px;"></div>
<div class="smoke" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
<div class="smoke" style="width:20px;height:20px;margin-top:-5px;margin-left:15px;"></div>
<div class="smoke" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
<div class="smoke" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
<div class="smoke" style="width:8px;height:8px;margin-top:0px;margin-left:30px;"></div>
<div class="smoke" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
<div class="smoke" style="width:8px;height:8px;margin-top:0px;margin-left:10px;"></div>
<div class="smoke" style="width:15px;height:15px;margin-top:0px;margin-left:15px;"></div>
<div class="smoke" style="width:20px;height:20px;margin-top:-5px;margin-left:10px;"></div>
<div class="smoke" style="width:8px;height:8px;margin-top:-2px;margin-left:30px;"></div>
<div class="smoke" style="width:10px;height:10px;margin-top:-2px;margin-left:15px;"></div>
<div class="smoke" style="width:15px;height:15px;margin-top:-0px;margin-left:20px;"></div>
</div>
And you'll see their
red outlines even if they're not visible after the glowing tip otherwise.
Now, at random, have each div styled using
smoke1 or
smoke2.
<div id="empty">
<div class="smoke smoke1" style="width:15px;height:15px;margin-top:5px;margin-left:25px;"></div>
<div class="smoke smoke1" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
<div class="smoke smoke2" style="width:18px;height:18px;margin-top:0px;margin-left:5px;"></div>
<div class="smoke smoke1" style="width:10px;height:10px;margin-top:-5px;margin-left:30px;"></div>
<div class="smoke smoke2" style="width:15px;height:15px;margin-top:0px;margin-left:20px;"></div>
<div class="smoke smoke2" style="width:20px;height:20px;margin-top:-2px;margin-left:15px;"></div>
<div class="smoke smoke1" style="width:8px;height:8px;margin-top:-5px;margin-left:13px;"></div>
<div class="smoke smoke2" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
<div class="smoke smoke1" style="width:8px;height:8px;margin-top:0px;margin-left:22px;"></div>
<div class="smoke smoke2" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
<div class="smoke smoke2" style="width:20px;height:20px;margin-top:-5px;margin-left:15px;"></div>
<div class="smoke smoke1" style="width:15px;height:15px;margin-top:-2px;margin-left:5px;"></div>
<div class="smoke smoke2" style="width:12px;height:12px;margin-top:-5px;margin-left:8px;"></div>
<div class="smoke smoke1" style="width:8px;height:8px;margin-top:0px;margin-left:30px;"></div>
<div class="smoke smoke2" style="width:20px;height:20px;margin-top:-2px;margin-left:10px;"></div>
<div class="smoke smoke2" style="width:8px;height:8px;margin-top:0px;margin-left:10px;"></div>
<div class="smoke smoke1" style="width:15px;height:15px;margin-top:0px;margin-left:15px;"></div>
<div class="smoke smoke2" style="width:20px;height:20px;margin-top:-5px;margin-left:10px;"></div>
<div class="smoke smoke1" style="width:8px;height:8px;margin-top:-2px;margin-left:30px;"></div>
<div class="smoke smoke2" style="width:10px;height:10px;margin-top:-2px;margin-left:15px;"></div>
<div class="smoke smoke1" style="width:15px;height:15px;margin-top:-0px;margin-left:20px;"></div>
</div>
This is for more animation. These CSS classes each call an animation, with different durations. However, both of these animations will run forever, and alternate back and forth.
.smoke
{
background-color: rgba(255, 255, 255, 0.2);
border-radius: 50%;
box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
}
.smoke1
{
animation-name: smokebubble1;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.smoke2
{
animation-name: smokebubble2;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
#burnable
{
width: 100%;
height: 250px;
background-color: rgba(255, 255, 255, 1);
background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}
Here are their animations. The
box-shadow and
margin-left properties are animated. I won't bother showing screenshots because the range of motion is rather limited.
.smoke1
{
animation-name: smokebubble1;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
.smoke2
{
animation-name: smokebubble2;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-direction: alternate;
}
@keyframes smokebubble1
{
from { box-shadow: 0 0 11px rgba(255, 255, 255, 1); margin-left: 26px; }
}
@keyframes smokebubble2
{
from { box-shadow: 0 0 12px rgba(255, 255, 255, 0.8); margin-left: 24px; }
}
#burnable
{
width: 100%;
height: 250px;
background-color: rgba(255, 255, 255, 1);
background: linear-gradient(90deg, rgba(230, 230, 230, 1) 0%, rgba(255, 255, 255, 1) 50%, rgba(230, 230, 230, 1) 100%);
}
In the JavaScript, we define the object
idleCounter. In it, there are the properties
lastActivity and
msToPopup.
lastActivity is a timestamp and defaults to
null.
msToPopup is an integer, defaults to 5, and defines the number of seconds of idle time encountered before the popup happens.
<script>
var idleCounter =
{
lastActivity: null,
msToPopup: 5
};
</script>
We then have the method,
startTimer(). What it does is set the
observeLastActivity() method to run every second.
<script>
var idleCounter =
{
lastActivity: null,
msToPopup: 5,
startTimer: function()
{
setInterval(
() =>
{
this.observeLastActivity();
},
1000
);
}
};
</script>
And we want this method to run as soon as the page is loaded.
<script>
var idleCounter =
{
lastActivity: null,
msToPopup: 5,
startTimer: function()
{
setInterval(
() =>
{
this.observeLastActivity();
},
5000
);
}
};
window.onload = () =>
{
idleCounter.startTimer();
};
</script>
Create
observeLastActivity(). If
lastActivity is falsy (not defined or just
null) then we run the
setLastActivity() method. We'll run the
popup() method regardless.
var idleCounter =
{
lastActivity: null,
msToPopup: 5,
popupOpen: false,
observeLastActivity: function()
{
if (!this.lastActivity) this.setLastActivity();
this.popup();
},
startTimer: function()
{
setInterval(
() =>
{
this.observeLastActivity();
},
5000
);
}
};
So we have two new methods to create. Start with
setLastActivity(). This basically involves setting
lastActivity to the current time, then running
popup().
var idleCounter =
{
lastActivity: null,
msToPopup: 5,
setLastActivity: function()
{
this.lastActivity = new Date();
this.popup();
},
observeLastActivity: function()
{
if (!this.lastActivity) this.setLastActivity();
this.popup();
},
startTimer: function()
{
setInterval(
() =>
{
this.observeLastActivity();
},
5000
);
}
};
Now for the
popup() method. Define
d as the current time. Then use the
getTime() method on
d and
lastActivity to get the number of milliseconds since the first day of 1970, for each timestamp, and subtract to get
diff. Of course
d will always be greater than
lastActivity, even if by a couple milliseconds.
var idleCounter =
{
lastActivity: null,
msToPopup: 5,
popupOpen: false,
setLastActivity: function()
{
this.lastActivity = new Date();
this.popup();
},
observeLastActivity: function()
{
if (!this.lastActivity) this.setLastActivity();
this.popup();
},
startTimer: function()
{
setInterval(
() =>
{
this.observeLastActivity();
},
5000
);
}
popup: function()
{
var d = new Date();
var diff = d.getTime() - this.lastActivity.getTime();
}
};
Then divide
diff (which is in milliseconds) by 1000 to get the number of seconds. And create a conditional to check if
diff is now greater or equal to
msToPopup.
popup: function()
{
var d = new Date();
var diff = d.getTime() - this.lastActivity.getTime();
diff = diff / 1000;
if (diff >= this.msToPopup)
{
}
else
{
}
}
Define
overlay as the modal,
empty as the
empty div and
burnable as the
burnable div. This is where it gets exciting.
popup: function()
{
var d = new Date();
var diff = d.getTime() - this.lastActivity.getTime();
diff = diff / 1000;
var overlay = document.getElementById("overlay");
var empty = document.getElementById("empty");
var burnable = document.getElementById("burnable");
if (diff >= this.msToPopup)
{
}
else
{
}
}
Now if
diff is greater or equal to
msToPopup, we want to display the modal by setting the
display property of its
style object to
block. By default, we will set
empty's height to 50 pixels and
burnable's height to 250 pixels. If not, we hide
overlay.
popup: function()
{
var d = new Date();
var diff = d.getTime() - this.lastActivity.getTime();
diff = diff / 1000;
var overlay = document.getElementById("overlay");
var empty = document.getElementById("empty");
var burnable = document.getElementById("burnable");
if (diff >= this.msToPopup)
{
overlay.style.display = "block";
empty.style.height = "50px";
burnable.style.height = "250px";
}
else
{
overlay.style.display = "none";
}
}
Here's the fun part. If
diff is greater or equal to twice
msToPopup, set
empty's height to 80 pixels and
burnable's height to 220 pixels. It will still add up to 300 pixels. The effect is that if more idle time has passed,
empty will be taller and
burnable will be shorter.
popup: function()
{
var d = new Date();
var diff = d.getTime() - this.lastActivity.getTime();
diff = diff / 1000;
var overlay = document.getElementById("overlay");
var empty = document.getElementById("empty");
var burnable = document.getElementById("burnable");
if (diff >= this.msToPopup)
{
overlay.style.display = "block";
empty.style.height = "50px";
burnable.style.height = "250px";
if (diff >= (this.msToPopup * 2))
{
empty.style.height = "80px";
burnable.style.height = "220px";
}
}
else
{
overlay.style.display = "none";
}
}
And so on, and so forth. I've set a few cases here and you should feel free to add more.
popup: function()
{
var d = new Date();
var diff = d.getTime() - this.lastActivity.getTime();
diff = diff / 1000;
var overlay = document.getElementById("overlay");
var empty = document.getElementById("empty");
var burnable = document.getElementById("burnable");
if (diff >= this.msToPopup)
{
overlay.style.display = "block";
empty.style.height = "50px";
burnable.style.height = "250px";
if (diff >= (this.msToPopup * 2))
{
empty.style.height = "80px";
burnable.style.height = "220px";
}
if (diff >= (this.msToPopup * 3))
{
empty.style.height = "100px";
burnable.style.height = "200px";
}
if (diff >= (this.msToPopup * 5))
{
empty.style.height = "200px";
burnable.style.height = "100px";
}
if (diff >= (this.msToPopup * 10))
{
empty.style.height = "250px";
burnable.style.height = "50px";
}
}
else
{
overlay.style.display = "none";
}
}
And while we're at it, disable the
red outline.
div { outline: 0px solid red; }
When you first refresh the browser, there should be no modal because the
popup() method detects that 5 seconds of inactivity have not passed, so the modal remains invisible. Wait for 5 seconds, and it should come up!
At 10 seconds, the cigarette grows shorter.
At 15 seconds...
At 25 seconds...
At 50 seconds...
For the final touch, add these two lines. This ensures that if you move the mouse or press a keyboard button,
setLastActivity() gets run and will result in the modal disappearing until another 5 seconds of inactivity passes.
window.onload = () =>
{
idleCounter.startTimer();
document.body.addEventListener("keypress", ()=> { idleCounter.setLastActivity(); });
document.body.addEventListener("mousemove", ()=> { idleCounter.setLastActivity(); });
};
Well done!
We just implemented a browser screen saver. Cool, right?!
Don't be idle now!
T___T