reptile7's JavaScript blog
Thursday, May 29, 2014
 
Condition Red
Blog Entry #321

In this entry we will check over the "Flashing Warning" script offered by the Scripts that Display Text script collection of the Java Goodies JavaScript Repository. The Flashing Warning script was written by Greg Bland in 1998 and is posted here. To see what the script's effect should look like, click on the Warning! widget below:

Warning!

(You've always wanted to adorn your Web page(s) with a flashing warning sign, haven't you? I thought so.)

The script puts the warning text in a layer

<layer name="funlayer" bgcolor="red" top="" left="">Warning text</layer>

and attempts to create the flashing effect by incrementally reddening the layer's background color. However, the reddening is carried out via a loop (actually two loops), and as a result I don't see any flashing at all upon running the script with Netscape Communicator 4.61 in the SheepShaver environment: we previously observed in Blog Entry #192 that loops are not meant for executing code in a controlled, gradual way. We will unlayerize and unloop the script in due course.

Script deconstruction

The script begins by setting up an array of hexadecimal digit strings running from 1 to f, inclusive.

c = new Array(6); // 6??
c[1] = "1"; // No use is made of this element/value.
c[2] = "2";
...
c[14] = "e";
c[15] = "f";


Could this data be written as a single line via the array literal syntax? That it could:

var c = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];

Loading the script document

<body onload="mkbg( );">

calls a recursive mkbg( ) function.

function mkbg( ) { ... window.setTimeout("mkbg( );", 100); /* mkbg( ) calls itself every 100 milliseconds. */ }

The mkbg( ) function gets right to work: an outer-inner loop pair initially darkens the funlayer layer's background color to #220000 and then sequentially raises the layer's 'red quotient' to pure red (#ff0000).

for (i = 2; i <= 15; i++) { for (i2 = 2; i2 <= 15; i2++) { color1 = c[i]; color2 = c[i2]; bl = "#" + color1 + "" + color1 + "0000"; document.funlayer.bgColor = bl; } }

The outer loop runs according to an i counter; the inner loop runs according to an i2 counter. Each loop runs for 14 iterations.

The inner loop plugs i and i2 into the c array to get their hexadecimal digit string equivalents, which are respectively given color1 and color2 identifiers. The color1 value is inserted twice between "#" and "0000" strings to give a six-digit RGB color that is assigned to the bgColor property of the funlayer layer object via the intermediacy of a bl variable; no use is made of the color2 value. BTW, the + "" term between the two color1s is unnecessary.

If we're going to leave color2 high and dry, then the inner loop can be folded into the outer loop

for (i = 2; i <= 15; i++) { color1 = c[i]; document.funlayer.bgColor = "#" + color1 + color1 + "0000"; }

although you can certainly deploy color2 if you want to via a

document.funlayer.bgColor = "#" + color1 + color2 + "0000";

statement that reddens the funlayer layer somewhat more slowly (#230000, #240000, #250000, ...) than the color1 + color1 combination does (#330000, #440000, #550000, ...).

Rounding out the mkbg( ) function is a second outer-inner loop pair

for (i = 2; i >= 4; i--) { for (i2 = 2; i2 >= 4; i2--) { color1 = c[i]; color2 = c[i2]; bl = "#" + color1 + "" + color1 + "0000"; document.funlayer.bgColor = bl; } }

that is not executed because, strangely, the condition of the outer loop returns false from the get-go. Nothing more needs to be said about this code.

Slow down baby

Let's get back to the first outer-inner loop pair. Regardless of whether we use two loops or just one, the browser/processor burns through those iterations so quickly that all we see on the page is a pure red layer, or at least that's what happens on my computer. Subtracting the loop action

var i = 2, color1; function mkbg( ) { color1 = c[i]; document.funlayer.bgColor = "#" + color1 + color1 + "0000"; i++; if (i == 16) i = 2; window.setTimeout("mkbg( );", 100); }

gratifyingly gives rise to a flashing layer.

Other practical details

No layer

The funlayer layer can be traded in for the container element of your choice: I use a span for the demo at the outset of the post. (You had to click my demo because onload cannot be used with a span as of this writing although HTML5 is gonna change that.) We access and increment the container background color in the usual manner:

document.getElementById("containerID").style.backgroundColor = "#" + color1 + color1 + "0000";

It doesn't have to be red

The RGB concatenation is easily tweaked to give other colors.

Yellow:
document.getElementById("containerID").style.backgroundColor = "#" + color1 + color1 + color1 + color1 + "00";
Lime:
document.getElementById("containerID").style.backgroundColor = "#00" + color1 + color1 + "00";
Blue:
document.getElementById("containerID").style.backgroundColor = "#0000" + color1 + color1;
And so on.

Tuesday, May 20, 2014
 
A Basic Visual upon Your Arrival
Blog Entry #320

The Scripts that Display Text script collection of the Java Goodies JavaScript Repository contains a "You Came In..." script that I thought we might discuss in today's post. Authored by Joseph Cosentino in 1998, the You Came In... script outputs a display of the form:



"Been there, done that. We could bang out something like this in our sleep at this point," you are thinking, and you would be right. The You Came In... script is nonetheless noteworthy because its business end is actually a VBScript (Visual Basic Scripting Edition) script and not a JavaScript script. Joe warns the reader that the script is MSIE 4.0 ONLY but doesn't say anything at all about its language="vbscript" - a smoking gun that Joe is working on the Windows platform vis-à-vis the Mac platform, with which VBScript is not compatible.

Joe's You Came In... script demo doesn't work for me but it might work for you if you're using IE on a PC, so if that's your situation then go check it out. The script itself can be accessed here.

VBScript and JavaScript have a lot in common, and the You Came In... script's script element code should make intuitive sense to you; most of that code can be readily deconstructed with a little help from our friends:
(1) Microsoft's VBScript documentation is ground zero for all things VBScript.
(2) The W3Schools VBScript Tutorial is also useful.

Structure and presentation

The outputted text is framed by a one-row, one-cell table, and styled with a font element.

<table border="3" width="100%" bgcolor="#000080"><tr><td align="center" bgcolor="#000080">
<font color="#ffffff" face="Arial,Geneva,Helvetica" size="2">
<script language="vbscript"><!--
...
//--></script></font></td></tr></table>


Is there any reason we should be using a table here vs. a div? No, there isn't. Moreover, the presentational attributes of the table, td, and font elements can be smoothly rolled into a single style rule.

#dateDiv { border: outset 3px; background-color: navy; text-align: center; color: white; font-family: Arial, sans-serif; }
/* Arial, Geneva, Helvetica? Overkill. And size="2" is too small for my taste. */
...
<div id="dateDiv"></div>


Date and its discontents, part 2

Let's get the VBScript action going, shall we?

d = weekday(date)

The script element's first statement
(1) gets the current date by calling the Date function and then
(2) feeds that date to the Weekday( ) function, which returns an index representing the current day of the week, and
(3) assigns the Weekday( ) index to a d variable.

More specifically, the Weekday( ) function (like the Month( ), Day( ), and Year( ) functions, vide infra) acts on a date argument that can be "any expression representing a date". Once upon a time Microsoft's VBScript materials featured a glossary that provided a date expression definition; the MSDN Library inexplicably no longer hosts this glossary but you can still find it floating around the Web - the good folks at Commodity Systems Inc. have posted it here, for example - so for the record, a date expression is:
Any expression that can be interpreted as a date. This includes any combination of date literals, numbers that look like dates, strings that look like dates, and dates returned from functions. A date expression is limited to numbers or strings, in any combination, that can represent a date from January 1, 100 through December 31, 9999.

Dates are stored as part of a real number. Values to the left of the decimal represent the date; values to the right of the decimal represent the time. Negative numbers represent dates prior to December 30, 1899.
Other notes
• VBScript keywords (date, weekday, etc.) are case-insensitive.
• Note that date is not followed by parentheses (although you can put them there if you want).
• The Weekday( ) return runs from 1 for Sunday to 7 for Saturday and thus does not quite match that for JavaScript's dateObject.getDay( ) method, which ranges from 0/Sunday to 6/Saturday.
• VBScript statements are terminated with carriage returns and not semicolons.

(The Date object page of the Mozilla JavaScript Reference provides links to pages that describe the Date object methods mentioned in this section.)

Subsequently an if...then...else construct maps d onto a corresponding text string and then assigns that string to a today variable.

if d = 1 then
today = "Sunday"
elseif d = 2 then
today = "Monday"
...
elseif d = 7 then
today = "Saturday"
end if


After that we have an analogous chunk of code that gets the current date again, feeds the date to the Month( ) function, which returns an index representing the current month, assigns the Month( ) index to an m variable, conditionally maps m onto a corresponding text string, and assigns that string to a mon variable.

m = month(date)
if m = 1 then
mon = "January"
elseif m = 2 then
mon = "February"
...
elseif m = 12 then
mon = "December"
end if


The Month( ) return runs from 1 for January to 12 for December and is thus slightly out of sync with that for JavaScript's dateObject.getMonth( ) method, which ranges from 0/January to 11/December.

The <script> concludes with document.Write commands that get the date, year, and time parts of the display, splice the date/time components with various strings, and write it all to the page:

document.Write "<b>You entered this site on"
document.Write " " & today & ", " & mon & " " & day(now) & " " & year(date)
document.Write " at " & Time( )


There is no mention of the document object on Microsoft's VBScript Objects and Collections and Write Method pages, although this page makes clear that the document object is part of the VBScript API. The Write arguments can be surrounded with parentheses if desired.

The Day( ) function corresponds to JavaScript's dateObject.getDate( ) method. The author could have fed date to the Day( ) function but instead reached for the Now function, which returns the current date and time as a string of the form:

5/19/2014 1:33:29 PM

Evidently the Now return is good enough of a date expression (vide supra) for our purposes.

The Year( ) function corresponds to JavaScript's dateObject.getFullYear( ) method. The Time function, whose call does not need to be followed by parentheses, returns the current time as a string of the form 2:06:09 PM and is approximately equivalent to JavaScript's dateObject.toLocaleTimeString( ) method (I say "approximately" because the toLocaleTimeString( ) return varies a bit from browser to browser).

As you would guess, the & characters serve as concatenation operators. FYI: VBScript does have a + operator that can be used to concatenate two strings but not a string and a number (as we're doing here).

Escape from IE for Windows

Could the You Came In... VBScript be tightened up a bit? Sure, but it's not worth our while to do that given VBScript's IE-only support. So let's wrap up this post with a quick 'translation' of the You Came In... VBScript into JavaScript:

(1) Set up arrays of weekday/month name strings that map onto the returns of the Date object's getDay( ) and getMonth( ) methods.

var dayArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var monthArray = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];


(2) Fetch the current Date( ) and piece together a corresponding dateString for the You Came In... display.

var myDate = new Date( );
var dateString = "You entered this site on " + dayArray[myDate.getDay( )] + ", " + monthArray[myDate.getMonth( )] + " " + myDate.getDate( ) + " " + myDate.getFullYear( ) + " at " + myDate.toLocaleTimeString( );


(3) Set the dateDiv div's innerHTML to the dateString.

window.onload = function( ) { document.getElementById("dateDiv").innerHTML = dateString; }

That'll do it, folks.

Thursday, May 01, 2014
 
Gradient Finale
Blog Entry #319

Today's post will complete our deconstruction of the Java Goodies "Color Gradient Text" script. In the previous post we called on the script's interpolate( ) function to calculate decimal rgb(rr, gg, bb) color values for the characters of the ABCDEFG thetext string; before we deploy this data, however, we have to deal with some...

Opera strangeness

The interpolate( ) calls are followed by the following if statement:

if (browser == "opera") { rr = 255 - rr; gg = 255 - gg; bb = 255 - bb; }

The preceding code would 'invert' the interpolate( ) rr/gg/bb settings if the user's browser had been identified as Opera at the beginning of the script. I have absolutely no idea why we would want to do this. Throw it out.

FWIW

As noted in the Pre-color section of Blog Entry #315, Opera passes the script's navigator.appName.indexOf("etscape") >= 0 test, i.e., it identifies as Netscape. However, we can in fact flag Opera qua Opera via the OPR in its userAgent string*:

if (/OPR/.test(navigator.userAgent)) browser = "opera";
/* The test( ) method of the RegExp object is documented here. */


*Opera has quietly dropped support for the window.opera object, BTW.

Here's what the display would look like if the browser == "opera" if condition returned true:

ABCDEFG

I don't think we want that.

Get it on the page

The thetext characters are colored and written to the page with:

document.write(thetext.charAt(i).fontcolor(tohex[rr] + tohex[gg] + tohex[bb])); } // End of for loop

The tohex array was previously detailed in the Pre-color section of Blog Entry #315 (vide supra). The rr/gg/bb values are plugged into the tohex array to get the values' two-digit hexadecimal string equivalents, e.g., tohex[255] outputs "ff", tohex[170] outputs "aa", and tohex[85] outputs "55". The tohex outputs are concatenated to give a (#-less) RRGGBB color value, which is applied to the charAt(i)th character via a fontcolor( ) operation. Finally, a document.write( ) operation prints out the newly colored character.

An alternative rendering

The original rendering leaves a lot of room for improvement.
• The fontcolor( ) method's HTML reflection, the font element, is deprecated: we should be using some sort of CSS operation to color the thetext characters.
• The tohex thing is unnecessary: we can color the thetext characters with the rr/gg/bb values just as they are.
• The document.write( ) commands require us to commingle structure and behavior: the script's code will be easier to read and maintain if we can separate its HTML and JavaScript.
A bit of cleanup is in order, wouldn't you say?

Rather than put the display in a single div, let's cue up a set of four empty divs for the four thetext strings. The divs can be coded normally

<div id="div0"></div>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>


and styled at will at a later point.

The gradient( ) calls can be placed in a window.onload-triggered function and equipped with arguments[2]s

window.onload = function ( ) { gradient("always wanted to be a genius?", "ff0000 ffffff 0000ff", "div0"); gradient("...well...do it, in...", "444444 dddddd 444444", "div1"); gradient("snazzy, eye-catching, gradient colors!","ff8888 ffff88 88ff88 88ffff 8888ff ff88ff", "div2"); gradient("but, um, just be careful not to overdo it", "884488 444488 448888 448844 888844 884444", "div3"); }

that will allow us to direct the outputted colored strings to the empty divs.

function gradient(thetext, thecolors, divID) { ... document.getElementById(divID).innerHTML = colorString; }

The colored strings can be built with:

var colorString = ""; for (i = 0; i < numchars; ++i) { ... colorString += "<span style='color:rgb(" + rr + ", " + gg + ", " + bb + ");'>" + thetext.charAt(i) + "</span>"; }

Each thetext character is placed in a separate span element in order to style it. The CSS coloration of an element with an rgb( ) function is treated here in the CSS 2.1 Specification.

OK, gradient("ABCDEFG", "ff0000 ffffff 0000ff"); was a pretty simple example. But you now have the tools to work through the original gradient( ) runs or another gradient( ) run of your liking.

At this point I would ordinarily offer you a gradientdemo.html demo based on the new-and-improved code we've developed over the last several entries. However, I canceled my EarthLink Dial-Up Service a few weeks ago and I do not have server space to which I can FTP files as of this writing - a situation I hope to rectify in the not-too-distant future. Frustratingly, I cannot upload a demo to Blogger itself: Blogger does allow me to upload images and videos but (as far as I am aware) not *.html files. Having said all this, I trust you're up to the task of crafting your own demo if you want to do that.


Powered by Blogger

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