reptile7's JavaScript blog
Wednesday, March 27, 2013
Light Up the Sky
Blog Entry #283

In today's post we will take up the mouse fireworks script offered by Section 8 of the JavaScript subsector of Lissa Explains It All. The mouse fireworks script was authored by Matt Gabbert and, on the basis of its browser-screening code, evidently goes back to the 1999-2000 period when the current versions of IE and Netscape were IE 5.x and Netscape 4.x, respectively. In response to a click on the page, the mouse fireworks script projects from the click point a fountain-like burst of text or images - the script's effect more resembles a geyser than an actual fireworks display, IMO.

Contra the How do I make mouse fireworks? text, the script doesn't contain any copyright information. Regarding the email address that Lissa provides for Matt, the domain is now - I have no idea if Matt is still living in Upper Saddle River, New Jersey. Matt used to run a site, which is gone and is not accessible via but can now be viewed at the Internet Archive.

The projectiles and Lissa's demo

Lissa provides for the mouse fireworks script a fireworks.shtml demo that works with IE 4+, Netscape 4.x, and Opera 5-9.2. For her projectiles Lissa uses a set of ten colored copyright symbols that will display as ♥ characters if the Symbol typeface is available.

<div id="sparks">
<div id="sDiv0" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#ff00ff" size="1">©</font></div>
<div id="sDiv1" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#9463f7" size="1">©</font></div>
<div id="sDiv2" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#319cff" size="1">©</font></div>
<div id="sDiv8" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#00ff00" size="2">©</font></div>
<div id="sDiv9" style="position:absolute;visibility:hidden;"><font face="symbol,wingdings,arial" color="#ffc0ff" size="2">©</font></div>

The ♥ character, termed the "black heart suit" or "valentine" character by the W3C, has long been standard - it was given its own parameter entity definition in HTML 4 (see the next-to-the-last entry in this list) and its hexadecimal Unicode code position is U+2665 - and can be rendered via a &hearts; or &#9829; reference.
FYI: IE 5.x supports both &hearts; and &#9829;; IE 4.x supports the latter but not the former; Netscape 4.x doesn't support either of them.

As for the other font element face values:
(a) In the Wingdings typeface the © character maps onto a three pointed black star, which is not now a Unicode character (U+1F7C2).
(b) If you did want a burst of © characters, then Arial is a perfectly suitable typeface, of course.

You may know that the W3C names the #ff00ff color "fuchsia" and the #00ff00 color "lime"; the other three colors - #9463f7, #319cff, and #ffc0ff - do not have recognized color names (I couldn't find them in Wikipedia's "Lists of colors" entries).

We noted in the previous entry that a size="1" font element attribute is equivalent to a font-size:x-small; style declaration; similarly, a size="2" font element attribute is equivalent to a font-size:small; style declaration. These font-size values are way too small for my taste and I crank them up to 36px and 48px respectively for the demo below.

The © characters are wrapped in div elements so that we can apply CSS to them. The div wrappers are given ordinalized ids: sDiv0, sDiv1, sDiv2, etc. The script makes no use of the outer sparks div, which can be removed if desired. I myself would hold onto the sparks div as the CSS child selector mechanism allows us to use it as a 'class' for applying styles to its child divs:

#sparks > div { position: absolute; visibility: hidden; }
/* If your projectiles are all the same color and size, then you can put that information here too. */

So, for those of you for whom Lissa's demo does not work, click in the div below to see what it looks like.
(As noted above, my ♥s are a lot bigger than Lissa's ♥s; also, I don't care for Lissa's #ffcc00 background color so I've changed it to aliceblue.)

Click any spot in this div.

Browser screening and fireworks monitoring

The mouse fireworks script's JavaScript begins with the following set of variable declarations:

var ver = navigator.appVersion;
var dom = document.getElementById ? 1 : 0;
var ie5 = (ver.indexOf("MSIE 5") > -1 && dom) ? 1 : 0;
var n = (document.layers);
var ie = (document.all);
var sparksAflyin = 0;
var totalSparks = 0;
var sparksOn = 1;

The n variable safeguards some Netscape event model code in the script's initMouseEvents( ) and mouseDown( ) functions and some layer object code in the script's moveTo( ) function. The ie variable safeguards some document.body and document.all code in the script's moveTo( ) function.

Even at the time the script was written, the ver, dom, and ie5 declarations could have been thrown out as no use is made of the ie5 variable. I noted earlier that the script works with IE 4.x. On the IE side the © divs are accessed via document.all.divID expressions; accessing the divs with the getElementById( ) method would have frozen out IE 4.x users.

The sparksAflyin variable is a boolean whose 1 or 0 value respectively signifies that a fireworks burst is or is not in progress, whereas the totalSparks variable is a counter that tracks the end of a fireworks burst; together these variables prevent the user from setting off more than one fireworks burst at the same time - as you can imagine, 'stacking' the fireworks bursts gives a pretty spastic display.

The sparksOn variable is initialized to 1 and remains at 1 as long as the user is at the mouse fireworks page; it can be thrown out as its role in the mouseDown( ) function - safeguarding the mouseDown( ) function body - is unnecessary.

Moving on, the preceding declarations are followed by five functions - in order, initMouseEvents( ), mouseDown( ), moveTo( ), SHOW( ), and HIDE( ) - and then a set of twenty arrays whose values are used to calculate and set offsets for the fireworks projectiles; we'll discuss the arrays when we go through the moveTo( ) function.


Loading the mouse fireworks page

<body onload="initMouseEvents( );">

calls the initMouseEvents( ) function, which coassociates the mouseDown( ) function, mousedown events, and the document object:

function initMouseEvents( ) {
    document.onmousedown = mouseDown;
    if (n) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE); }

For Netscape 4.x, which does not support event bubbling, it is necessary to capture mousedown events at the level of the document or window object if you'd like to set off a fireworks burst by clicking any spot on this page, but unless I'm missing something it shouldn't be necessary to likewise capture mousemove events (there are no onmousemove event handlers in the script).

So let's click on the page and get the fireworks action under way, shall we? Our click - more precisely, its mousedown component - calls the mouseDown( ) function, whose first order of business is to get the click's page coordinates (vis-à-vis its viewport coordinates):

function mouseDown(e) {
    if (sparksOn) { /* As noted above, the sparksOn gate is excess baggage. */
        var mousex = (n) ? e.pageX : event.x + document.body.scrollLeft;
        var mousey = (n) ? e.pageY : event.y + document.body.scrollTop; ... } ... }

On the Netscape side we get the click coordinates via the e object's pageX and pageY properties - so far, so normal.

On the IE side we get the click coordinates via the event object's x and y properties, whose returns are respectively adjusted by document.body.scrollLeft and document.body.scrollTop in order to take user scrolling into account. According to Microsoft, the x and y properties play out differently with IE 4.x and IE 5.x, specifically, they should return a click's viewport coordinates with IE 4.x and its page coordinates with IE 5.x (if the click's target element does not have a relatively positioned ancestor). In practice on my iMac, event.x and event.y return a click's viewport coordinates when using IE 4.5 and IE 5.1.6.

The x and y properties are today also supported by Google Chrome, Safari, and Opera, and they also return a click's viewport coordinates with these browsers - Mozilla's browsers currently don't support them - they are not standard but are on track to be standardized. Somewhat ironically, Netscape equipped its first-generation event object with x and y properties that could have been used by the mouseDown( ) function to get a click's page coordinates on the Netscape side way back when; however, those properties are only supported by Netscape 4.x.

You can hold onto the event.x and event.y expressions if you want - if the display area doesn't exceed the viewport, as is the case for Lissa's demo and my demo above, then the viewport coordinates vs. page coordinates thing isn't going to be an issue anyway - although I myself would respectively replace x and y with clientX and clientY, which reliably return a mouse event's viewport coordinates with all of the major browsers. Netscape's clientX/clientY support began with Netscape 6 and its scrollLeft/scrollTop support began with Netscape 7. Meanwhile, IE 9+ supports the Netscape-cum-W3C event model and the pageX/pageY properties but earlier IE versions don't. Putting it all together, we should be able to accommodate pretty much everybody with:

mousex = e ? e.pageX : event.clientX + document.body.scrollLeft;
mousey = e ? e.pageY : event.clientY + document.body.scrollTop;

With the click coordinates in hand, the mouseDown( ) function next checks if a fireworks burst is not in progress

if (!sparksAflyin) { ... }

and if the coast is clear it iteratively calls on the SHOW( ) function to visibilize the © divs:

for (var k = 0; k <= 9; k++)
    eval('SHOW("sDiv' + k + '")');
function SHOW(divName) {
    if (document.all) /* Why document.all and not ie? I don't know. */
        eval('document.all.' + divName + '.style.visibility = "visible";');
    else if (document.layers) /* Why document.layers and not n? I don't know. */
        eval('document.layers["' + divName + '"].visibility = "visible";'); }

Are any of the preceding uses of the eval( ) function necessary? Nope:

for (var k = 0; k <= 9; k++) SHOW("sDiv" + k);
function SHOW(divName) {
    if (ie) document.all(divName).style.visibility = "visible";
    if (n) document.layers[divName].visibility = "show"; }

As shown above, the layer object's 'true' visibility value is actually show and not visible; in this case the Netscape 4.x JavaScript engine replaces* visible with show, the initial visibility value for a top-level layer.
(*You can confirm this by running a window.alert(document.sDiv0.visibility); command.)

Subsequently the mouseDown( ) function turns on the sparksAflyin boolean and unnecessarily resets the totalSparks counter to 0:

sparksAflyin = 1;
totalSparks = 0; /* This line can be commented out. */

We are now ready to get those fireworks projectiles moving, and we'll go through the details thereof in the following entry.

Saturday, March 16, 2013
Like a Thief in the Wire
Blog Entry #282

In today's post we will discuss the How do I block my document source? script (hereafter "block-source script") offered by Section 6 of the JavaScript subsector of Lissa Explains It All. Lissa credits the block-source script's authorship to Urs Dudli and Peter Gehrig; I suspect that Peter alone crafted the script given that
(a) Peter says Urs Dudli is a great artist and I am a great writer at his "Kostenlose DHTML und JavaScript Text Animationen" page and
(b) the script doesn't make use of any images,
although Urs or Peter is welcome to contact me and correct me if I'm wrong about this.

Right-clicking on a Web page pops up a context menu that features a View Source (or equivalent) command
A context menu with a highlighted 'View Source' command
via which the user can access the page's source: the block-source script aims to thwart this process by canceling the right-click. The script also displays a tabular form that solicits the user's name, country, and email address

The 'order window' for the author's source code

and pops up an alert( ) box bearing a "Please order my HTML-source by email!" message. As Urs, Peter, and Lissa envision it, the user fills in the requested form data and clicks the button, which submits the data to the page author's email address, and then the author replies with a selling price for the page source. (In the script's credits, Urs and Peter give the script a Turn source-code-thieves into source-code-buyers title.) Quaint, huh? Good luck with that one, guys.

The How do I block my document source? subsection includes a demo link that when clicked is supposed to call a document1( ) function, which is nowhere to be found. An Internet Archive search for the Section 6 javascript6.shtml page returns: Sorry. This URL has been excluded from the Wayback Machine. So we don't have a current demo to work with, and I can't tell you how well the original demo worked back in the day.

The JavaScript

The block-source script's 10 January 2000 release date indicates that it was written when IE 5.x and Netscape 4.x were the current versions of IE and Netscape, respectively. Accordingly the script's JavaScript (the script element in the subsection's first textarea field) comprises a meant-for-IE part and a meant-for-Netscape part; I will specify the former in the discussion below. I'd go through the Netscape part as well if there were any browser other than Netscape 4.x that supports the layer object, but there isn't.

At the end of the JavaScript, a buttoncheck( ) function that listens for mousedown events is registered on the document object.

document.onmousedown = buttoncheck;

Here's the buttoncheck( ) function:

function buttoncheck(e) {
    if (document.all && event.button == 2) { = "visible";
        window.alert("Please order my HTML-source by email!");
        return false; }
    /* Netscape block */ }

• The button property of the event object returns 2 for a right-button mouse event.

• The "Please mail me your source-code" form is wrapped in a div element (IE 5.x will position a form element but IE 4.x and Netscape 4.x won't) whose id is orderwindow. The buttoncheck( ) function visibilizes the orderwindow div by toggling its CSS visibility property from hidden to visible.

• With most browsers context menus are generated by mousedown events (vis-à-vis click events), which are canceled by the return false; statement.

Clicking the form's button calls a sendmail( ) function that rehides the orderwindow div and submits the user's form data.

function sendmail( ) {
    if (document.all) { = "hidden";
        document.forms[0].submit( ); }
    /* Netscape block */ }

Finally, clicking the form's button calls a closewindow( ) function that rehides the orderwindow div.

function closewindow( ) {
    if (document.all) { = "hidden"; }
    if (document.layers) document.orderwindow.visibility = "hidden"; }


The "Please mail me your source-code" form's action attribute is set to a mailto: URL specifying the page author's email address. As you might guess from the document.forms[0] reference in the sendmail( ) function, the form doesn't have an identifier, although it would be a good idea to give it one: back then we would have used a name attribute and today we would use an id attribute for this purpose. As noted above, the form's orderwindow div container is initially hidden; it's also absolutely positioned, which makes sense as we don't want the div to disrupt the normal flow of the document when we display it.

<div id="orderwindow" style="position:absolute;top:10px;left:220px;visibility:hidden;">
<form action=""> ... </form></div>

The form's legend, controls, and text input labels are laid out via a table:

<table width="300" cellpadding="4" cellspacing="0" border="0"> ... </table>

The table HTML is larded with deprecated code - specifically, the cell background colors are set with bgcolor attributes and the cell contents are marked up with font elements - and I'm not going to burden you with the whole thing; here's the "lastname" (second) row:

<td align="right" bgcolor="blue"><font face="Verdana" size="1" color="white">lastname:</td>
<td align="left" bgcolor="blue"><font face="Verdana" size="1"><input type="text" name="lastname" size="20" maxlength="40"></font></td>
<!-- You don't need to see any more of this, do you? Didn't think so. -->

• The first cell's lastname: text can and probably should be marked up with a label element.

<label for="textinputID">lastname:</label>
<!-- Explicit labeling is necessary if the text field and its label are in separate cells. -->

• On my computer, a size="1" font element attribute is equivalent to a font-size:x-small; style declaration.

• Although the font element has an (%inline;)* content model and can therefore legitimately have input element children, I find that font element attributes have absolutely no effect on a text input value; they similarly have no effect on the push button values in the table's last row.

• Even as we are looking to get rid of the font element markup, I should note as regards the first cell that the font element's end-tag is not optional.

In practice

We recently discussed two 'no right-click' scripts in Blog Entry #274; I reported then that, in general,
(1) right-button mouse events are not recognized by Classic Mac browsers and
(2) mousedown event cancelation does not suppress the generation of context menus with modern OS X browsers but
(3) contextmenu event cancelation does suppress the generation of context menus with modern OS X browsers (and some older browsers).
These considerations likewise apply to the block-source script and its buttoncheck( ) code, which can be upgraded to:

document.oncontextmenu = function ( ) {
    document.getElementById("orderwindow").style.visibility = "visible";
    window.alert("Please order my HTML source by email!");
    return false; }

We noted in The mailto: blues section of Blog Entry #172 that mailto: forms are no longer handled as they were way back when, specifically, mailto: form submission does not directly send the form data to the action URL but instead launches the user's default email program, opening a "New Message" window with the To: field set to the action URL. (This presupposes (a) that there's a default email program on the user's computer in the first place, (b) that the email program is configured correctly, and (c) that the user's browser can communicate with the email program, which may not necessarily be the case - see Wikipedia's "mailto" entry and's "When Mailto Forms Don't Work" tutorial.) Moreover, if the form's method attribute is set to post, then the form data set will appear as a 'query string'


at the beginning of the "New Message" window's message field with some but not all browsers. The net result is that the user is required to take a more active role in getting the form data to the form's author, but that's probably as it should be.


Right-click me, I dare you.

You can send me an email if you like; I promise I won't try to charge you for my demo code.

More cold water

I'm the first to tell you that the foregoing analysis is all rather academic. The source for any Web page is easily accessed via the View Source (or equivalent) command of the browser's View or Tools menu: no right-clicks required. The block-source script is not practical code but rather a 'museum piece'.

Let me wrap up this post with some relevant developer commentary.

(1) Matthias Gutfeldt subscribes to a philosophy of steal the code, not the content. Sounds right to me, Matthias.

(2) In a "How Do I Force...?" article in the Web Tips sector of his Web site, Dan Tobias explains the futility of concealing a Web page's HTML:
People have an exaggerated impression of the value of their HTML code and want to protect it from being "stolen". But there's no way to hide HTML source code from the user. The user's browser needs to receive all the HTML source code in order to display the page, so no matter what devious techniques the author uses to obscure the code, it still has to be parseable by the browser, and is thus not too hard for any halfway-intelligent user to turn into something readable.
In sum, the only way to prevent the user from grabbing your code is to not upload it to the Web in the first place.

We will conclude our Lissa Explains It All JavaScript odyssey with a look at Section 8's mouse fireworks script, which we'll take up in the following entry.

Wednesday, March 06, 2013
I'm Waiting for the Bounce
Blog Entry #281

We return now to our ongoing deconstruction of Peter Gehrig's trailing images script. At this point we have created a beam of thirteen starry.gif images and moved the beam to the bottom of the viewport via the script's moveball( ) and makesnake( ) functions. When the top edge of the beam's first image falls below the marginbottom (document.body.clientHeight-5), the image is bounced upward by the script's checkposition( ) function with an assist from the script's randommaker( ) function.

function randommaker(range) {
    rand = Math.floor(range * Math.random( ));
    return rand; }
function checkposition( ) {
    if (document.all) { ...
        if ( > marginbottom) { // At the bottom edge
            stepy = (stepy + randommaker(2)) * -1;
   -= 1; } ... } ... }

Recall that the stepy vertical step is initialized to 20 in the top-level part of the code. The stepy = (stepy + randommaker(2)) * -1; statement calculates a new, negative stepy - either -20 or -21, depending on whether the randommaker( ) rand value is 0 or 1, respectively - that will move the image beam upward via the stepy assignments in the makesnake( ) and moveball( ) functions.

For its part, the -= 1; statement shifts the beam's first image upward by a single pixel. Technically we are acting on the image's span0 span container vis-à-vis the image itself, but (at least on my computer) the span+i spans add no 'girth' to the starry.gif images.

The starry.gif image is 45px by 45px. Most or all of the first image will be below the viewport's bottom edge when the span0 span's posTop becomes greater than marginbottom; however, if we set the body element's overflow to hidden, as we should, then a vertical scrollbar will not be generated.

The stepx horizontal step is also initialized to 20 in the top-level part of the code, and is still 20. With stepx positive and stepy negative, the first image begins to move northeastward; the rest of the beam bounces and follows the first image as the moveball( ) function continues to iterate.

Depending on the width and height of the viewport, the beam will move to the viewport's (a) top edge or (b) right edge.

var margintop = 0;
if ( < margintop) { // At the top edge
    stepy = (stepy + randommaker(2)) * -1; += 1; }
if ( > marginright) { // At the right edge
    stepx = (stepx + randommaker(2)) * -1; -= 1; }

(a) When the top edge of the beam's first image rises above the margintop, the beam bounces and then moves southeastward or southwestward, depending on whether stepx is positive or negative, respectively.

(b) When the left edge of the beam's first image moves to the right of the marginright (document.body.clientWidth-5), the beam bounces and then moves northwestward or southwestward, depending on whether stepy is negative or positive, respectively.

In due course the beam will move/bounce to the viewport's left edge.

var marginleft = 0;
if ( < marginleft) { // At the left edge
    stepx = (stepx + randommaker(2)) * -1; += 1; }

When the left edge of the beam moves to the left of the marginleft, the beam bounces and then moves southeastward or northeastward, depending on whether stepy is positive or negative, respectively.

The stepx/stepy blues

If we comment out the left-edge clause's rightward 1px posLeft shift - a shift that in theory should not be necessary - then it is possible for the beam's first image to be trapped at the left edge of the viewport and then move leftward, thereby dragging the beam out of sight; this results from the fact that the stepx = (stepx + randommaker(2)) * -1; statement can produce a new stepx whose absolute value is less than that of the old stepx - e.g., stepx can go from -20 to 19 - and it specifically occurs when || > |stepx|, which (in the absence of the posLeft shift) causes the < marginleft if condition to be true indefinitely, and thus the beam doesn't bounce.

If we comment out the other 1px posLeft/posTop shifts, an analogous phenomenon can occur at the viewport's top edge but not at its right or bottom edge; in the latter cases the new |stepx| or |stepy| is always greater than or equal to the old |stepx| or |stepy|, and therefore the relevant if condition is true for only one moveball( ) iteration and the beam bounces. Even with the left-edge and top-edge shifts in place, the first image can get stuck briefly at the viewport's left or top edge, but these shifts do prevent the beam from moving off-viewport.

A related issue: it is possible for |stepx| and |stepy| to creep upward (20-2121-2222 etc.) or downward (20-2019-1918 etc.) indefinitely - not good, needless to say.

A smoother trail

You may remember that we worked with a similar checkposition( ) function in Peter's floating images script; this function adjusted stepx and stepy in a simpler way that did not give rise to the aforedescribed problems. Consider its left-edge clause:

var marginleft = 0, floatingspeed = 5;
function randommaker(range) { return 1 + Math.floor((range - 1) * Math.random( )); }

if (thisspan.posLeft < marginleft) { // At the left edge
    thisspan.posLeft += Math.abs(stepx[i]);
    stepx[i] = randommaker(floatingspeed); }

The new stepx[i] is confined to a 1 to 4 range; similarly, the top edge's new stepy[i] is confined to a 1 to 4 range, the right edge's new stepx[i] is confined to a -1 to -4 range, and the bottom edge's new stepy[i] is confined to a -1 to -4 range. Significantly, the new stepx[i]/stepy[i] values are always positive or always negative, i.e., they can't switch back and forth between positive and negative as in the trailing images script, thereby eliminating any need for thisspan.posLeft/thisspan.posTop shifts.

Applying this approach to the trailing images script's checkposition( ) function

stepx = - (18 + randommaker(5)); // At the right edge
stepx = 18 + randommaker(5); // At the left edge
stepy = - (18 + randommaker(5)); // At the bottom edge
stepy = 18 + randommaker(5); // At the top edge

enables us to suppress stepx/stepy creep and get rid of the 1px posLeft/posTop shifts.


A more proactive approach to controlling the directionality of a bouncing image is offered by the Bounce All Around script of Lisha Sterling's "How to Create a JavaScript Animation" tutorial, which uses
(a) a dirx = 1 expression to dictate left-to-right movement,
(b) a dirx = 0 expression to dictate right-to-left movement,
(c) a diry = 1 expression to dictate up-to-down movement, and
(d) a diry = 0 expression to dictate down-to-up movement.

// Initial beam movement is rightward and downward
var dirx = 1;
var diry = 1;
if (diry && parseInt(document.getElementById("span0"), 10) > marginbottom) { // At the bottom edge
    // Vertical movement changes from downward to upward
    diry = 0;
    stepy = - (18 + randommaker(5)); }


Bounce, bounce, bounce...

The above demo parallels my floating images script demo in various ways, e.g., it discards the span+i span wrappers and directly works with the starry.gif images, it organizes the images as a trails array, it moves/bounces the images by writing and reading their style.left/ properties, etc. - see Blog Entry #279 (in particular its More retool section) for a fuller discussion of these features.

In the name of completeness

• The How do I make trailing images? subsection again includes a dead link to I am not able to give you an updated link because, as noted in the previous entry, Peter has not updated the trailing images script. I said last time that my attempt to find a trailing images script at came up empty-handed; the other day I went through the scripts at Peter's "Kostenlose DHTML und JavaScript Text Animationen" page and there wasn't anything there either.

• Lissa again instructs the reader to place the trailing images script's move/bounce code (the script element in the first textarea field) between the document head and the document body. Doing this violates the content model of the html element: place the code in the document head instead.

• Lissa's you can add or subtract images aside evidently pertains to the numberofimages setting (vis-à-vis composing the beam of different images). It is of course up to you to decide how large of a beam you want, although I would argue that the original numberofimages = 12 is the right number of images to use.

We have one last Peter Gehrig script at Lissa Explains It All to cover, that being Section 6's How do I block my document source? script, which we'll run through quickly in the next post.

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)