reptile7's JavaScript blog
Monday, September 29, 2014
 
Planning a Color Trip
Blog Entry #336

We return now to our discussion of the Text Fader 1.3 script and its various functions. Today's post will be largely devoted to the fadeText( ) function, which carries out a set of foundational calculations in support of the script's fading operations. Its name notwithstanding, the fadeText( ) function doesn't actually fade the movie strings - the changeColor( ) function does that.

fadeText( ) parameterization

In the previous post we set the script functions in motion by calling the layout( ) function and then moved the fadeMe01 div to the center of the viewport via the getMid( ) and moveLayer( ) functions. In the layout( ) function the moveLayer("fadeMe01", midY - 50, midX - 200); call is followed by four calls to the fadeText( ) function, which are spread out timewise by window.setTimeout( ) commands.

fadeText("fadeMe01", "<span class='content'><b><a href='mailto:agent_teg@ThePentagon.com?subject=Text%20Fader%201.3'>Teg WoRKz</a></b> Interactive</span><div class='small'>proudly presents</div>", "000000", "CCCCCC", 10, 2);

window.setTimeout("fadeText('fadeMe01', '<span class=\"content\"><b>Teg WoRKz</b> Interactive</span><div class=\"small\">proudly presents</div>', 'CCCCCC', '000000', 10, 4);", 8000);

window.setTimeout("fadeText('fadeMe01', '<div class=\"small\">A</div><span class=\"content\"><b>Teg WoRKz</b> Production</span>', '000000', 'CCCCFF', 10, 2);", 12000);

window.setTimeout("fadeText('fadeMe01', '<div class=\"small\">A</div><span class=\"content\"><b><a href=\"mailto:agent_teg@ThePentagon.com?subject=Text%20Fader%201.3\">Teg WoRKz</a></b> Production</span>', 'CCCCFF', '000000', 10, 4);", 20000);


N.B. The quote formatting of the delayed fadeText( ) calls can be simplified by using the func, delay setTimeout( ) syntax, e.g.:

window.setTimeout(function ( ) { fadeText("fadeMe01", "<span class='content'><b>Teg WoRKz</b> Interactive</span><div class='small'>proudly presents</div>", "CCCCCC", "000000", 10, 4); }, 8000);

Each fadeText( ) call passes six arguments to the fadeText( ) function.

function fadeText(obj, str, rgb1, rgb2, speed, step) { ... }

(0) arguments[0] is the id of the div whose text will be faded, and is given an obj identifier.
(1) arguments[1] is a text+HTML string that will be loaded into the obj div, and is given an str identifier.
(2) arguments[2] is the str text's starting color in the form of a #-less six-digit RRGGBB value, and is given an rgb1 identifier.
(3) arguments[3] is the str text's ending color in the form of a #-less six-digit RRGGBB value, and is given an rgb2 identifier.
(4) arguments[4] will serve as the setTimeout( ) delay for a recursive changeColor( ) call, and is given a speed identifier.
(5) arguments[5] controls the gradualness of the rgb1rgb2 transition, and is given a step identifier.

Having gone through this, I should note that the fadeText( ) function does not in fact act on the obj, str, and speed arguments but merely passes them on to the changeColor( ) function.

More CSS

Each of the above str strings contains a span with a class='content' identifier and a div with a class='small' identifier; these elements are styled with:

.content { font: 16pt Verdana, Arial, Helvetica; width: 400px; text-align: center; }
.small { font: 6pt Verdana, Arial, Helvetica; width: 400px; text-align: center; }


The width and text-align properties ordinarily do not apply to span elements - we can make them apply if we give the spans an inline-block display although there's no burning reason to do so - the .content rule's width:400px; declaration can be thrown out and its text-align:center; declaration can be applied to the fadeMe01 div.

#fadeMe01 { position: absolute; text-align: center; width: 410px; }
.content { font: 16pt Verdana, Arial, Helvetica; }


Also:
• Link underlines are subtracted via an a { text-decoration:none; } rule.
• Oddly, b elements are styled with a b { font-weight:bold; } rule. Are there any situations in which <b></b> and font-weight:bold; give different renderings? Maybe, I don't know - I suppose there's no harm in keeping the rule but I'd get rid of it.

Slice and decimalize

The fadeText( ) body initially declares an r1/g1/b1/r2/g2/b2 set of variables.

var r1, g1, b1, r2, g2, b2;

Next, fadeText( ) splits the rgb1 and rgb2 colors into their RR, GG, and BB components, which are decimalized via an external hexToDec( ) function; the decimal R/G/B values are assigned to the r1-b2 variables.

for (cnt = 1; cnt <= 2; cnt++) { eval("r" + cnt + " = hexToDec(rgb" + cnt + ".slice(0, 2));"); eval("g" + cnt + " = hexToDec(rgb" + cnt + ".slice(2, 4));"); eval("b" + cnt + " = hexToDec(rgb" + cnt + ".slice(4, 6));"); } ... function hexToDec(hex) { var value = 0; while (true) { if (convert[value] == hex) break; value++; } return value; }

We'll address the form of this code in a moment, but first, here's what's going on in more detail:

(1) As far as I am aware we've never worked with the slice( ) method of the String object before. In the present case slice( ) behaves just like substring( ), e.g., "Hello World".slice(0, 2); extracts/returns the He section of Hello World, i.e., the section running from the 0th character up to but not including the 2nd character indexwise. Each rgb+cnt.slice( ) operation gives an RR|GG|BB value that is passed to the hexToDec( ) function and given a hex identifier.

(2) The hexToDec( ) function runs through the convert database in order (convert[0], convert[1], ...) via a while loop until it finds a convert[value] value that equals hex, at which point a break statement terminates the loop and value is returned to the fadeText( ) function, which assigns the hexToDec( ) return to r|g|b+cnt.

The for loop with its cnt counter/index and the eval( ) function allow us to 'ordinalize' the code and thereby make it more compact than it otherwise would be. However, we really should be getting rid of the eval( ) calls. Is it that much more effort to write out six lines of code for the r1-b2 assignments? No, it isn't.

r1 = hexToDec(rgb1.slice(0, 2));
g1 = hexToDec(rgb1.slice(2, 4));
b1 = hexToDec(rgb1.slice(4, 6));
r2 = hexToDec(rgb2.slice(0, 2));
g2 = hexToDec(rgb2.slice(2, 4));
b2 = hexToDec(rgb2.slice(4, 6));


Moreover, the hexToDec( ) function is redundant in that the rgb1/rgb2 RR/GG/BB values can be decimalized via the parseInt( ) function (h/t commoner and the Java Goodies Color Gradient Text script).

r1 = parseInt(rgb1.slice(0, 2), 16);
g1 = parseInt(rgb1.slice(2, 4), 16);
b1 = parseInt(rgb1.slice(4, 6), 16);
r2 = parseInt(rgb2.slice(0, 2), 16);
g2 = parseInt(rgb2.slice(2, 4), 16);
b2 = parseInt(rgb2.slice(4, 6), 16);


Pre-fade increments

The Text Fader 1.3 script is designed to change the R/G/B components of the rgb1 color to their rgb2 counterparts
(a) in a proportional manner and
(b) as gradually as is practically possible.
Toward this end, the script looks at the r1r2, g1g2, and b1b2 'color distances' and then bases the changeColor( ) fading process on the largest of those distances.

When the for loop has finished executing, control passes to a with block whose first three statements copy the r1, g1, and b1 values to s2r, s2g, and s2b variables; in the changeColor( ) function s2r/s2g/s2b will hold the RR/GG/BB color values of the str string as it fades.

/* These variables are declared globally just before the hexToDec( ) function. */
var s1, s2, s3, s2r, s2g, s2b, smallest;
...

with (Math) { s2r = r1; s2g = g1; s2b = b1; ... }

As you may know, Mozilla discommends the use of the with statement. Losing the with code will require us to laboriously prepend M, a, t, h, . to some up-and-coming abs( ) commands, but if you've come this far in the discussion you should be up to the task of doing that. ;-)

Moving on, the r1/g1/b1r2/g2/b2 color distances are calculated by:

s1 = Math.abs(r1 - r2);
s2 = Math.abs(g1 - g2);
s3 = Math.abs(b1 - b2);


The s1/s2/s3 values compose a ratio in proportion to which we will increment the rgb1 R/G/B components. At this point the s1-s3 numbers are not so useful. Let's change that, shall we?

An if...else if...else construct determines whether the s1, s2, or s3 distance is largest and assigns that distance to a smallest variable.

if (s1 > s2 && s1 > s3) { smallest = s1; }
else if (s2 > s1 && s2 > s3) { smallest = s2; }
else { smallest = s3; }


This code breaks down if two of the distances are equal and larger than the third distance, e.g., for a "0000FF""FFFFFF" (blue to white) transition, in which case the smaller distance is assigned to smallest: changing the > operators to >= solves this problem.

Division of s1/s2/s3 by smallest converts them to a minimized while normalized-to-1 form.

s1 = (s1 == 0) ? 0 : s1 / smallest;
s2 = (s2 == 0) ? 0 : s2 / smallest;
s3 = (s3 == 0) ? 0 : s3 / smallest;


Testing if s1, s2, or s3 is 0 becomes necessary only if smallest itself is 0, which would happen if rgb1 and rgb2 were equal, which should not be the case.

The resulting s1/s2/s3 values are multiplied by step.

s1 *= step;
s2 *= step;
s3 *= step;


The step adjustments vary the incrementation, and therefore the speed, of an rgb1rgb2 transition. Consider the first two fadeText( ) calls: the second call's "CCCCCC""000000" fade is twice as fast as the first call's "000000""CCCCCC" fade because the calls' step values are 4 and 2, respectively.

If an s1/s2/s3 value needs to go down and not up for the rgb1rgb2 transition, it is converted to its additive inverse.

if (r1 > r2) { s1 = -s1; }
if (g1 > g2) { s2 = -s2; }
if (b1 > b2) { s3 = -s3; }


We're almost ready to call the changeColor( ) function. The pre-fade increment data for the first fadeText( ) call's "000000""CCCCCC" transition is:

r1 = g1 = b1 = 0
r2 = g2 = b2 = 204
s2r = s2g = s2b = 0
s1 = s2 = s3 = 2 (204 → 1 → 2)
smallest = 204


We'll apply this data (well, some of it) to the str string in the following entry.

Friday, September 19, 2014
 
It Fades and Spins
Blog Entry #335

The law of diminishing returns is rapidly setting in with respect to the Java Goodies Scripts that Display Text sector: most of the remaining scripts are simply too trivial to cover. We will work through one last sector offering, that being the "Text Fader 1.3" script*, which was written by Emmanuel C. Halos in August 1998.
(*Two previous versions of the script - "Text Fader" and "Fade Out" - also appear in the sector, but we won't be discussing them.)

The Text Fader 1.3 script creates a 'movie' in which text strings
(1) fade out of a black background to the foreground or
(2) fade from the foreground into the background or
(3) fade from one foreground color to another foreground color.
The script demo does not work with any of the OS X browsers on my iMac although it might work for you if you're using IE on a PC.

The Text Fader 1.3 script does not have a separate Grab the Script page: you'll have to extract the script from the source of the demo page.

Starting HTML/CSS

Each movie string will be housed in one of two div elements.

#fadeMe01 { position: absolute; visibility: visible; width: 410px; }
#fadeMe02 { position: absolute; visibility: visible; width: 400px; }
...
<div id="fadeMe01"></div>
<div id="fadeMe02"></div>


• The fadeMe01 and fadeMe02 divs are absolutely positioned but are not offset** in any way. As they are also the first two elements of the document body, the divs are both initially placed in the upper-left-hand corner of the viewport.
(**Because the initial top and left values are auto and not 0, each div has an 8px margin-top and margin-left.)

• The visibility declarations can be removed as the initial visibility value is visible.

The black background color is set by the body element start-tag.

<body bgcolor="#000000" text="#cccccc" link="#ffffff" vlink="#ffffff" onload="layout( );">

The deprecated bgcolor, text, link, and vlink attributes should be traded in for corresponding style rules:

body { background-color: black; color: #cccccc; }
a:link, a:visited { color: white; }


A script element in the document head holds the script's JavaScript, which we'll get to in just a bit. Meanwhile, the divs are followed by a noscript element.

<noscript>Silly! You'll need to have JavaScript enabled and you must have <a href="http://www.microsoft.com/">Internet Explorer 4</a> or <a href="http://developer.netscape.com">Netscape Navigator 4</a> or <a href="http://developer.webtv.net">WebTV</a>(?) for this script to work.</noscript>

Leaving aside the (in)advisability of nagging at users without JavaScript support, I should point out that the content model of the noscript element differs in the Strict and Transitional DTDs, namely, it's (%block;)+ in the former and (%flow;)* in the latter, and consequently the above noscript will pass a transitional validation but throw an error in a strict validation, so you may want to wrap the noscript text in a div or p element. BTW, the http://developer.netscape.com and http://developer.webtv.net links are dead. (Like Netscape, WebTV is kaput - read about it here.)

Starting JavaScript

User agents of yesteryear

As intimated by the noscript text, the current versions of IE and Netscape in 1998 were IE 4.x and Netscape 4.x, respectively; the script defines style, layers, and both variables that will screen for these browsers.

<script type="text/javascript"> ...
var style = document.all;
var layers = document.layers;
var both = layers || style;


In the SheepShaver environment, I find that the script works flawlessly with Netscape 4.61, is slightly buggy with IE 4.5, and works flawlessly with IE 5.1.7 once I remove the quirks mode → strict mode-inducing

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">

document type declaration at the top of the document.

Only IE 4+ and Opera 5-9.2 will go through a document.all gate, whereas only Netscape 4.x will go through a document.layers gate: thank goodness such tests have been obsoleted by the W3C DOM.

Radix ricochet

Each movie string has
(1) a starting color whose R/G/B components go from hexadecimal to decimal to hexadecimal numbers
and
(2) an ending color whose R/G/B components go from hexadecimal to decimal numbers
in the course of the script. To facilitate these base 16 ↔ base 10 interconversions, the script creates a convert database of two-digit hexadecimal number strings running from "00" to "FF" inclusive.

var convert = new Array( );
var hexbase = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F");
var value = 0;

for (x = 0; x < 16; x++) for (y = 0; y < 16; y++) { convert[value] = hexbase[x] + hexbase[y]; value++; }

An empty array and an ordinal array of hexadecimal digits are created and given the identifiers convert and hexbase, respectively. A value variable will serve as the index for the convert array and is initialized to 0. Subsequently a two-dimensional loop statement builds the convert database in sequence from the hexbase values:

convert[0] = "00", convert[1] = "01", ... convert[15] = "0F", convert[16] = "10", ...
convert[50] = "32", ... convert[100] = "64", ... convert[200] = "C8", ... convert[255] = "FF"


We'll see later that this code, too, is unnecessary.

Orphans

The idme and rgb variable declarations at the beginning of the script element

var idme = 908601; var rgb;

can be thrown out as these variables find no use in the script.

Subroutine it

Over and above its smattering of top-level code, the script element contains eight functions, in source order:
(1) stopClock( )
(2) moveLayer( )
(3) hexToDec( )
(4) writeOn( )
(5) changeColor( )
(6) fadeText( )
(7) getMid( )
(8) layout( )

The action kicks off with the layout( ) function, which fires when the page loads (<body onload="layout( );">, vide supra).

The center of attention

The layout( ) function first calls the getMid( ) function.

function layout( ) { getMid( ); ... }

The getMid( ) function locates the (midX, midY) midpoint of the viewport.

function getMid( ) { tvAgent = navigator.userAgent; if (layers) { maxX = window.innerWidth; maxY = window.innerHeight; } if (style) { maxX = document.body.clientWidth; maxY = document.body.clientHeight; } if (tvAgent.indexOf("WebTV") != -1) { maxX = 544; maxY = 378; } midX = maxX / 2; midY = maxY / 2; }

For modern browsers the getMid( ) function can be recast as:

function getMid( ) { maxX = window.innerWidth ? window.innerWidth : document.body.clientWidth; maxY = window.innerHeight ? window.innerHeight : document.body.clientHeight; midX = maxX / 2; midY = maxY / 2; }
/* The ?: conditional operator is documented here. */

We've tussled with this sort of thing before - here's the skinny (go here for a more detailed discussion):
The document.body.clientWidth/document.body.clientHeight property pair gets the viewport dimensions for IE 6+ as long as the browser is running in quirks mode; window.innerWidth/window.innerHeight does the job for everyone else. The innerWidth/innerHeight measurements would include the width of a vertical scrollbar and/or the height of a horizontal scrollbar if they were present.

The layout( ) function next calls the moveLayer( ) function

moveLayer("fadeMe01", midY - 50, midX - 200);

and passes thereto fadeMe01, midY-50, and midX-200. The moveLayer( ) function moves the fadeMe01 div to the center of the viewport (not the exact center, but close enough for our purpose).

if (layers) { layerRef = "document.layers"; styleRef = ""; }
if (style) { layerRef = "document.all"; styleRef = ".style"; }


function moveLayer(obj, topx, leftx) { if (both) { eval(layerRef + "['" + obj + "']" + styleRef + ".top = topx"); eval(layerRef + "['" + obj + "']" + styleRef + ".left = leftx"); } }

This seriously ugly code can be replaced with:

function moveLayer(obj, topx, leftx) { document.getElementById(obj).style.top = topx + "px"; document.getElementById(obj).style.left = leftx + "px"; }

We're ready to put a string in the fadeMe01 div and get the fading under way - we'll do that in the following entry.

Wednesday, September 10, 2014
 
When in Rome
Blog Entry #334

Over and above the New Array Text Pages scripts, Jenson's Easy to Use JavaScripts directory features a Roman Numeral (RomanNumerals.html) Script that I thought we would look at in today's post. The RomanNumerals.html script
(1) converts Arabic numerals to Roman numerals and also
(2) creates a date string whose day and year numbers are expressed in Roman numerals.

On the whole the RomanNumerals.html script is well-designed but it does have one serious flaw, namely, it's not interactive: a corrective demo will be provided at the end of the post.

Numeric inputs

Specifically, the RomanNumerals.html script converts Arabic integers in the range 1-3999 inclusive to their Roman numeral equivalents.

• For a 0 input the script outputs an empty string.

• Floating-point inputs are Math.floor( )ed, e.g., 3.14159 gives III.

• Oddly, the script accommodates negative integer inputs in the range -1 to -3999 inclusive - -25 gives -XXV, e.g. - even though Roman numerals are not really meant for negative numbers.

• For integers larger than 3999 (or smaller than -3999) the script outputs an "Out of Range" string.

Wikipedia's "Roman numerals" entry includes a Special values section that addresses the Roman approaches to zero, fractions, and large numbers. Meanwhile, wikiHow reports that the Romans themselves just wrote MMMM for 4000.

The cast of characters

In his script commentary, Jenson correctly notes:
Roman numerals are based on only two basic digits: 1 and 5.
However, they use different characters depending on the power of 10 being represented.
'1' Roman Numerals
Roman NumeralArabic EquivalentPower of 10
I11 × 100
X101 × 101
C1001 × 102
M10001 × 103
  
'5' Roman Numerals
Roman NumeralArabic EquivalentPower of 10
V55 × 100
L505 × 101
D5005 × 102


The I, V, X, L, C, D, and M Roman numerals are arrayed in ascending order via a romanDigits Object object

romanDigits[0] = "I";
romanDigits[1] = "V";
romanDigits[2] = "X";
romanDigits[3] = "L";
romanDigits[4] = "C";
romanDigits[5] = "D";
romanDigits[6] = "M";
romanDigits.length = 7;


that instantiates the buildArray( ) constructor.

function buildArray( ) { var a = arguments; for (i = 0; i < a.length; i++) this[i] = a[i]; this.length = a.length; }

var romanDigits = new buildArray("I", "V", "X", "L", "C", "D", "M");


Could romanDigits be formulated as a bona fide Array object? That it could.

var romanDigits = ["I", "V", "X", "L", "C", "D", "M"];

Fragment it

For this and the following sections we will work with a 374CCCLXXIV example.

The business end of the RomanNumerals.html script comprises two functions:
(1) an arabicToRoman( ) function effectively divides an inputted Arabic integer into its expanded notation terms;
(2) an arabicToRomanDigit( ) function 'Romanizes' those terms.

A document.write( ) command calls the arabicToRoman( ) function; the 374 argument is given a number identifier.

function arabicToRoman(number) { ... }

document.write("374 in Roman Numerals is ", arabicToRoman(374));


The arabicToRoman( ) body initially declares j, string, and sign variables.

var j, string = "", sign = "";

If number is negative, then sign is set to a stringified minus sign and number is converted to its additive inverse.

if (number < 0) { sign = "-"; number = -number; }

Subsequently number is floor( )ed whether it needs it or not.

number = Math.floor(number);

If number is 4000 or greater, then Out of Range is returned to the calling document.write( ) command and the function exits.

if (number >= 4000) return "Out of Range";

With these preliminaries out of the way, we're ready to get to work on our number=374. A for loop splits 374 into its constituent digits and passes
(a) those digits and
(b) the powers of 10 associated with them
to the arabicToRomanDigit( ) function.

for (j = 0; number > 0; j++) { digit = number % 10; number = (number - digit) / 10; string = arabicToRomanDigit(digit, j) + string; }

The loop runs for three iterations; more generally, it runs as many times as there are digits in number. Noting that

374 = 3 × 102 + 7 × 101 + 4 × 100

here's a rundown of the number processing in those iterations:

j = 0
374 is moduloed by 10 to give 4, which is assigned to a digit variable.
(374 - 4) / 10 gives 37, which is assigned to number.
4 and 0 are passed to the arabicToRomanDigit( ) function.

j = 1
37 is moduloed by 10 to give 7, which is assigned to digit.
(37 - 7) / 10 gives 3, which is assigned to number.
7 and 1 are passed to arabicToRomanDigit( ).

j = 2
3 is moduloed by 10 to give 3, which is assigned to digit.
(3 - 3) / 10 gives 0, which is assigned to number.
3 and 2 are passed to arabicToRomanDigit( ).

With number now 0, the loop stops at the beginning of (what would be) its fourth iteration because the number > 0 condition returns false.

This is not the only way to split number: alternatively, we could stringify number and then go after its characters via, say, the substring( ) method. But given that number is in fact a number (and that we'll be doing some simple arithmetic on the 7 in the arabicToRomanDigit( ) function), the modulo/division approach is the more appropriate way to go.

Romanize it

The arabicToRomanDigit( ) function's digit and j arguments are given the identifiers digit and power, respectively. The arabicToRomanDigit( ) body initially declares i and string variables.

function arabicToRomanDigit(digit, power) { var i, string = ""; ... }

The declarations are followed by a series of if statements that converts Arabic digits to corresponding Roman strings. Following modern practice, 4s and 9s are handled subtractively, e.g., 4 becomes IV and not IIII. A set of romanDigits[n + power * 2] expressions relates the power values to the romanDigits series of numerals.

if (digit >= 5 && digit <= 8) { // Adds a V|L|D Roman numeral to string as needed string = romanDigits[1 + power * 2]; digit = digit - 5; } if (digit >=1 && digit < 4) { // Adds one, two, or three I|X|C|M Roman numerals to string as needed for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; } if (digit == 4 || digit == 9) { string = romanDigits[0 + power * 2]; if (digit == 4) string = string + romanDigits[1 + power * 2]; else string = string + romanDigits[2 + power * 2]; }

Returning to our 374CCCLXXIV example and recalling that romanDigits = ["I", "V", "X", "L", "C", "D", "M"]:

(4) If digit is 4 and power is 0:
if (digit == 4 || digit == 9) { string = romanDigits[0 + power * 2]; sets string to I.
if (digit == 4) string = string + romanDigits[1 + power * 2]; appends a V to the I to give string = IV.

(70) If digit is 7 and power is 1:
if (digit >= 5 && digit <= 8) { string = romanDigits[1 + power * 2]; sets string to L.
digit = digit - 5; drops digit to 2.
if (digit >=1 && digit < 4) { for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; appends two Xs to the L to give string = LXX.

(300) If digit is 3 and power is 2:
if (digit >=1 && digit < 4) { for (i = 0; i < digit; i++) string = string + romanDigits[0 + power * 2]; sets string to CCC.

The arabicToRomanDigit( ) function concludes with a return string; statement that returns string to the arabicToRoman( ) function.

Assembly and output

Back at the arabicToRoman( ) function, string (i.e., the arabicToRoman( ) string - note that the arabicToRoman( ) string and the arabicToRomanDigit( ) string are both local variables) is appended to the arabicToRomanDigit( ) return and the resulting string is assigned to string.

string = arabicToRomanDigit(digit, j) + string;

For the three for (j = 0; number > 0; j++) loop iterations, string goes from an empty string to IV to LXXIV to CCCLXXIV. The order of concatenation operands is important: a string += arabicToRomanDigit(digit, j); assignment would give a 'reversed' string = IVLXXCCC.

When the loop has finished executing, a return sign + string; statement appends string to sign and returns the resulting string to the document.write( ) command, which then writes

374 in Roman Numerals is CCCLXXIV

to the page. If it were up to me we would send string to an empty <div id="romanDiv"><div> via a

document.getElementById("romanDiv").textContent = "374 in Roman numerals is: " + string;

command, but to each his own.

The date

The script presents the following code for writing a date in Roman numerals:

var month = new buildArray("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
var today = new Date( );
var yr = today.getYear( );
if (yr < 100) yr = yr + 1900;
document.write("<br>Today is ", arabicToRoman(today.getDate( )), " ", month[today.getMonth( )], " ", arabicToRoman(yr));


I see you rolling your eyes. "getYear( )??" Yeah, that's the reason why the year in the date demo at the javascript/RomanNumerals.html page is off. Setting yr to today.getFullYear( ) sorts out the situation.

Sample output:
Today is VII September MMXIV

Demo

As promised, we end with a demo.

Input an Arabic integer in the range 1-3999 inclusive:




A !/^\d+$/.test(number) gate weeds out negative numbers, floating-point numbers, and nonnumeric inputs.

Monday, September 01, 2014
 
More Quotes and Greetings
Blog Entry #333

In today's post we'll go through the last two New Array Text Pages scripts:
(4) RandomQuotes.html, which displays a random quote whenever its document loads;
(5) TODGreeting.html, which displays a Good morning or Good afternoon or Good evening greeting depending on the hour of the day.

The RandomQuotes.html script is the shortest and most straightforward New Array Text Pages script and can be deconstructed quickly. The TODGreeting.html script is wildly more complex than it needs to be vis-à-vis the simple effect it creates and consequently we're not going to spend a lot of time on it, either.

You can see demos for these scripts respectively at the RandomQuotes.html and TODGreeting.html pages of Jenson's javascript/ directory.

Quoting at random

The RandomQuotes.html script arrays 15 quote strings via a quote Object object

quote[0] = "Back Up My Hard Drive? How do I Put it in Reverse?";
quote[1] = "What we have here is a failure to assimilate.<br>[Cool Hand Locutus]";
...
quote[14] = "A polar bear is a rectangular bear after a coordinate transform.";
quote.length = 15;


that is an instance of the

function buildArray( ) { var a = arguments; for (i = 0; i < a.length; i++) this[i] = a[i]; this.length = a.length; }

template we worked with in the New Array Text Pages CookieQuotes.html and QuoteOfTheDay.html scripts. Subsequently the script gets a random quote quote and writes it to the page with:

document.write(quote[Math.floor(Math.random( ) * quote.length)]);

And that's all she wrote for the Java Goodies RandomQuotes.html script. I myself would recast quote as a bona fide Array object and write the quote quote to the innerHTML of an empty div.

var quote = [ ...Quote strings... ];
...
window.onload = function ( ) { document.getElementById("quoteDiv").innerHTML = quote[Math.floor(Math.random( ) * quote.length)]; }
...
<div id="quoteDiv"></div>


The Source Code section of the javascript/RandomQuotes.html page provides a slightly more complicated script in which quote's toString( ) method is overridden with a randomArrayElement( ) function.

function randomArrayElement( ) { return this[Math.floor(Math.random( ) * this.length)]; }
quote.toString = randomArrayElement;
document.write(quote);


That's what time of day it is

The Source Code section of the javascript/TODGreeting.html page provides a script that exactly matches the Java Goodies TODGreeting.html JavaScript.

The TODGreeting.html script first creates the data structure below:

greetingList[0] = {start: 0, end: 11, greet: "morning"}
greetingList[1] = {start: 12, end: 17, greet: "afternoon"}
greetingList[2] = {start: 18, end: 23, greet: "evening"}
greetingList.length = 3;


The greetingList object is an instance of a greetingArray object type.

var greetingList = new greetingArray(0, 11, "morning", 12, 17, "afternoon", 18, 23, "evening");

Each greetingList[i] property is an object that
(a) is an instance of a greeting object type and
(b) has three properties: start, end, and greet.

function greeting(start, end, greet) { ... if (start <= end) { this.start = start; this.end = end; } ... this.greet = greet; }

function greetingArray( ) { var a = arguments; ... for (ti = ai = 0; ai < a.length; ti ++, ai += 3) this[ti] = new greeting(a[ai], a[ai + 1], a[ai + 2]); this.length = a.length / 3; }

The script overrides the toString( ) method of each greetingList[i] object with a greetingToString( ) function that returns the object's greet value.

function greetingToString( ) { return this.greet; }
greeting.prototype.toString = greetingToString;


Next, a document.write( ) command calls a TODgreeting( ) function that
(a) gets the current new Date( ).getHours( ) hour,
(b) maps the hour to the greetingList[i] object whose start/end goalposts bound the hour and then
(c) returns the object, which in the context of the write( ) operation is converted to its greet value by the greetingToString( ) function.
The greet return is appended to a "Good " string and the resulting greeting is written to the page.

function TODgreeting( ) { var today = new Date( ); var hr = today.getHours( ); for (i = 0; i < greetingList.length; i++) { if (hr >= greetingList[i].start && hr <= greetingList[i].end) { return greetingList[i]; } } ... }
document.write("Good " + TODgreeting( ));

The TODgreeting( )/greetingToString( ) return can also be planted in a normal run of text.

You can also include the greeting in the body of the document this <script type="text/javascript">document.write(TODgreeting( ));</script>.
Just don't spend all <script type="text/javascript">document.write(TODgreeting( ));</script> here.


Jenson is welcome to his convoluted code. I for my part would replace the greeting( )/greetingToString( )/greetingArray( )/TODgreeting( ) functionality with:

var greet;
var today = new Date( );
var hr = today.getHours( );
if (0 <= hr && hr <= 11) greet = "morning";
if (12 <= hr && hr <= 17) greet = "afternoon";
if (18 <= hr && hr <= 23) greet = "evening";

window.onload = function ( ) { document.getElementById("greetingDiv").textContent = "Good " + greet; }
...
<div id="greetingDiv"></div>


What a spoilsport I am, huh? As for the inline greet thing, you can
(i) put the text in a separate <div id="greetingDiv2"></div> container,
(ii) replace the microscripts with empty <span></span>s,
(iii) get the spans via a document.getElementById("greetingDiv2").getElementsByTagName("span"); command, and
(iv) load greet into the spans
per the Embed it section of the previous post.

That'll do it for our New Array Text Pages odyssey. There's one more "ETU" script I'd like to cover, however, that being the javascript/ directory's Roman Numeral Script, which we'll put under the microscope in the following entry.


Powered by Blogger

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