The photo gallery JavaScript includes code which generates the virtual "LCD" text panel. The version of the code shown here is for the panel as it is used on the information page. The code is slightly different from that used in the gallery but the basic text processing and display flow is the same. Note that there are a number of additional cosmetic tweaks for formatting in this window.


// (c) 2000-2002 D.Holmes

                                // counters etc. for  imgLoadChk()

var t_total = 0;		// thumbnail
var f_total = 0;		// fullsize
var et_total = 0;		// empty thumbnail
var ef_total = 0;		// empty fullsize
var checkthumbN = 0;		// how many thumbnails loaded so far
var checkfullN = 0;		// how many fullsized loaded so far
var type = "";			// image type:  t,f,et,ef - thumbnail,
				// fullsized, empty thumb or ful
var num = 0;
var checkfullid, checkthumbid;	// image load checking timer


var maxStrings		=  9;	// message text strings limit


var rMax		=  7;	// lcd row
var cLen		= 30;	// lcd column length in char
var cellWidth		= 11;	// in pixels
var cellHeight		= 15;	//  "   "

				// colour codes for font glyph image
				// selection and background colour
var letterbg = "#001955";       // y2 & B
if (colourcode == "b"  || colourcode == "gB") letterbg = "#FFFFFF";
if (colourcode == "B2") letterbg = "#042999";


// timers (in milliseconds)

var StartDelay		= 4000;	// initial slideshow delay

var RepeatDelay		= 6000; // formfeed delay used at end of
				// screenfull of text

var StatusInterval	= 1000; // repeatLoop() interval for
				// checking if we need to rerun
				// mainLoop()


// "typing" speed timers - used to delay
//  image swapping of character glyph

var typeNormal	= true;	
var typeSpeed	= 70;
var crlfSpeed	= (typeSpeed * 4);
var highSpeed	= 0;
var commaPause	= 6;
var stopPause	= 18;
var delayPause	= 0;

// ASCII codes used to test for special char
// in particular see putLine() which handles
// actual character output to "lcd screen

BELchar		=   7;
BSchar		=   8;	
HTchar		=   9;
LFchar		=  10;
VTchar		=  11;	
FFchar		=  12;
CRchar		=  13;
SPACE		=  32;

MagicTABs	=   true;
TABwidth	=   8;
LowerASCII	=  32;
UpperASCII	= 126;

// Initialize tracking indices used to monitor
// screen location etc.
var rIndex     =  0;
var cIndex     =  0;
var sIndex     =  0;
var sCount     =  1;
var currString = "";


var printing	= false;	// test if currently printing
var timerStatus	= 0;		// used to prevent start (-1) if there
				// wasn't a "stop" to clear timers (0)

// timer IDs used to kill off all timers
var putLineID;
var formFeedID;
var mainLoopID;
var repeatIntervalID;

var INformFeed = -1;		// set in endofline() - cleared in
				// formfeed and tested & cleared in
				// startDisplay()

// LCD clock display (clock is client's time
var clockBlink = false;		// flipper for ":" flash
var nowTime;
var clkIntervalID;
var clockLen = 8;

// Pre-cache the glyphs

for (imgs = 32; imgs <= 126 ; imgs++) {
        eval("var " + imgprefix + imgs + " = new Image()\;");
}
for (imgs = 32; imgs <= 126 ; imgs++) {
        eval(imgprefix + imgs + ".src = \"" + fileprefix + 
	     imgs + filesuffix + "\"\;");

}


// Prep. display for restart after text run is complete
// (other than clock which is independent)

function endgame() {
	formFeed();
	stopDisplay();

	currString = "To replay this text, reload"	
		+'\t'+ "the page or select the"
		+'\t'+ "checkbox shown on the right ->";
	typeNormal = false;
	putLine();
	document['lcd2c29'].src = imgdir + "r-arrow_t.gif";
	return;
}

// Called by user clicking tic box of FORM; can happen at any time
function replay() {
	formFeed();
	stopDisplay();
	sCount = 1;

	repeatIntervalID = setInterval("repeatLoop()",StatusInterval);

	document.forms[0].reset();
}

// Clock sits on last row and runs independently
function LCDclock() {

	var c = 0

	var now = new Date();
	var nowHour = now.getHours();
	var nowMin  = now.getMinutes();
	var now12Hr

	if (now.getTimezoneOffset() == 0) {
		nowTime = ((nowHour < 10) ? "0" : "") + nowHour;
		nowTime += ((clockBlink) ? " " : ":")
		nowTime += ((nowMin < 10) ? "0" : "") + nowMin;
		nowTime += " GMT";
		clockLen = 9;
	} else {
		now12Hr  = ((nowHour > 12) ? nowHour - 12 : nowHour);
		nowTime  = ((now12Hr < 10) ? " " : "") + now12Hr;
		nowTime += ((clockBlink) ? " " : ":")
		nowTime += ((nowMin < 10) ? "0" : "") + nowMin;
		nowTime += (nowHour >= 12) ? " pm" : " am";
		clockLen = 8;
	}

	clockBlink  = !clockBlink;

	while (c < clockLen) {
		target = "lcd" + rMax + "c" + (cLen - clockLen + c);
		source = fileprefix+nowTime.charCodeAt(c)+filesuffix;
		document[target].src = source;
		c++;
	}
}
	
// mainLoop() handles launching the printing of each string
// of text as well as interpreting magic strings which
// control operation
function mainLoop() {

	printing = true;
	rIndex = 0;
	cIndex = 0;
	if (sCount > maxStrings) {
		sCount = 1;
		formFeedID = setTimeout("formFeed()",RepeatDelay);
	} else {
		currString = eval("Text" + sCount);
		if (currString == "xHALTx") {
			stopDisplay();
			return;
		} else if (currString == "xCODEx") {
			stopDisplay();
			endgame();	// run special routine
			return;
		} else if (currString == "xSKIPx") {
			printing = false;
			sCount++;
			return;
		}
		formFeed();
		putLine();
		sCount++;
	}

}

// Testing wrapper for mainLoop()
function repeatLoop() {

	if (! printing) {
		printing = true;
		mainLoop();
	}
}

// Run from HTML onload event handler
function afterload() {

	// never use the last row for text; clock is there
	rMax-- ;
	clkIntervalID = setInterval("LCDclock()",2000);

	repeatIntervalID = setInterval("repeatLoop()",StatusInterval);
}


// Clears the entire display area to space chars
function formFeed() {
	clearTimeout(putLineID);
	for (r = 0; r < rMax ; r++) { 
		for (c = 0; c < cLen ; c++) {
			target = "lcd" + r + "c" + c;
			source = imgprefix + "32.src";
			document[target].src = eval(source);
		}
	}
	target = "lcd" + "0" + "c" + "0";
	source = cursorimg.src;
	document[target].src = source;

	rIndex = 0;
	cIndex = 0;

	INformFeed = -1;
	printing   = false;

}

// Clears a single row
function clearRow(row) {
        clearTimeout(putLineID);
        r = row;
        if (r < rMax) {
                for (c = 0; c < cLen ; c++) {
                        target = "lcd" + r + "c" + c;
                        source = imgprefix + "32.src";
                        document[target].src = eval(source);
                }
        }
        cIndex = 0;
        rIndex = r;
}

// Reset flags to show we're not printing and
// typing speed is returned to normal
function outputFinished() {
	printing = false;
	typeNormal = true;
}

// Kill all text activity 
// and reset modes
function stopDisplay() {

	stillloading = false;
	clearTimeout(checkthumbid);
	clearTimeout(checkfullid);
	clearTimeout(mainLoopID);
	clearTimeout(repeatIntervalID);
	clearTimeout(formFeedID);
	clearTimeout(putLineID);
	timerStatus = 0;
	outputFinished();
}

// Text processor - here is the actual parsing and output of the text
// This function features:
//	- parsing of magic character conditions,
//	- typing speed mode (fast or normal)
//	- pausing for punctuation
//	- handling quoted or bracketed punctuation
//	- handling repeated punctuation ('...' etc.)
//
// The function works by outputing a character (image swapping within 
// the LCD TABLE drawn by drawTable() ) and then calling itself again 
// based on how it wants to deal with the next character based upon 
// what it just printed, what's next, where it is on the line etc.

function putLine() {

   printing = true;
   clearTimeout(putLineID);

   var localstring = currString;
   var localrownum = rIndex;
   var localcolumn = cIndex;
   var localCode   = 0;

   // if we still have some text left to deal with,
   // then get and test the next character in the string
   if (localstring.length > 0) {

      localCode = localstring.charCodeAt();

      // handle any magic tokens we use and filter bad stuff

      if (isNaN(localCode)) localCode = NotAChar;

      if        (localCode == LFchar) {
         endofLine(LFchar);
      } else if (localCode == CRchar) {
         endofLine(CRchar);
      } else if (localCode == HTchar && MagicTABs) {
         endofLine(HTchar);
      } else if (localCode == HTchar) {
      } else if ( localCode < LowerASCII || localCode > UpperASCII ) {
         localCode = NotAChar;

      } else {

      // and then output legitimate chars

         target = "lcd" + localrownum +"c"+ localcolumn;
         source = eval(imgprefix + localCode + ".src");
         document[target].src = source;

         localcolumn++;

      // Now prep to handle what would happen next
      //  making note of our current typing speed setting
      //  handling cursor, punctuation pauses, line endings 
      //  etc.
      // NOTE that we also special-case repeating punctuation 
      //  and punctuation within quotes, brackets etc. with 
      //  the delayPause for example

         // Assuming we still have more line to the right...

         if (localcolumn <= cLen) {

            // display cursor only if typing at
            // normal speed and space to the right

            if (typeNormal && (localcolumn < cLen) ) {
               target = "lcd" + localrownum + "c" + localcolumn;
               source = cursorimg.src;
               document[target].src = source;
            }

            localstring = localstring.slice(1);
            currString = localstring;
            cIndex = localcolumn;

            // if we're in high-speed mode, just output

            if (!typeNormal) putLineID = setTimeout("putLine()",highSpeed);

            // else if char wasn't punctuation or 
            //      delayed (quoted, brackets etc.)
            //   punctuation, then on to next

            else if (delayPause == 0 && (localCode > 63 || localCode == 32))
               putLineID = setTimeout("putLine()",typeSpeed);

            // else if we delayed the punctuation 
            //    pause last time for closing
            //    quotes, brackets etc. then we 
            //    need to insert a pause now
            //    44 comma, 58 colon - commaPause,
            //    otherwise period pause

            else if (delayPause != 0) {                  
               if ( (delayPause == 44) || (delayPause == 58)) {
                  putLineID = setTimeout("putLine()",
                               (typeSpeed * commaPause));
               }
               else {
                  putLineID = setTimeout("putLine()",
                               (typeSpeed * stopPause));
               }
               delayPause = 0;
             }

            // else if not punct., do next

            else if ( localCode != 46 && 
                      localCode != 44 && 
                      localCode != 33 &&    
                      localCode != 63 && 
                      localCode != 59 && 
                      localCode != 58) {
               putLineID = setTimeout("putLine()",typeSpeed);
            }

            // else if punct. followed by a closing quote, bracket etc. 
            //   then set delayPause and but don't delay YET
            //   46 period,   33 exclamation,   63 question

            else if ( ( (localCode == 46) ||
                        (localCode == 33) || 
                        (localCode == 63) ) && (localstring.length > 1) &&
                   ((localstring.charCodeAt(0) == 34) ||
                    (localstring.charCodeAt(0) == 39) ||
                    (localstring.charCodeAt(0) == 41) ||
                    (localstring.charCodeAt(0) == 93) ||
                    (localstring.charCodeAt(0) == 125) )) {
               delayPause = localCode;
               putLineID = setTimeout("putLine()",typeSpeed);
            }
         
            // deal with '...' etc. - no pause

            else if ( (localstring.length >= 1) &&
                    ((localstring.charCodeAt(0) == localCode) ||
                     (localstring.charCodeAt(0) != SPACE) &&
                     (localstring.charCodeAt(0) != HTchar) )) {
               putLineID = setTimeout("putLine()",typeSpeed);
            }

            // else pause for punct.
            else if ( (localCode == 44) || (localCode == 58) )
               putLineID = setTimeout("putLine()",(typeSpeed * commaPause));
            else {
               putLineID = setTimeout("putLine()",(typeSpeed * stopPause));
            }

         // ... otherwise if no more line is 
         // available deal with the line ending

         } else {
            endofLine(localCode);
         }
      }

   // otherwise  localstring.length <= 0
   // ... stop printing.

   } else {
      printing = false;
      typeNormal = true;
   }

}


// handle various "end of" events - end of line, end of display etc.
function endofLine(ending) {

		// if we still have space to the right, output a space char (32)
	if (cIndex < cLen) {
		target = "lcd" + rIndex + "c" + cIndex;
		source = imgprefix + "32.src";
		document[target].src = eval(source);
	}

		// if there are more rows, incr. and display cursor...
	if (rIndex < rMax) {
		rIndex++
		cIndex=0;
		target = "lcd" + rIndex + "c" + cIndex;
		source = cursorimg.src;
		document[target].src = source;
	} else {
		// ... else formfeed
		rIndex=0;
		cIndex=0;
		currString = Text0;
		INformFeed = 0;
		formFeedID = setTimeout("formFeed()",4000);
	}
		// if a TAB got us here, eat it and keep going...
		// (TABs force a crlf)
	if (ending == HTchar) {
		currString = currString.slice(1);
		if (!typeNormal) putLineID = setTimeout("putLine()",highSpeed);
		else putLineID = setTimeout("putLine()",crlfSpeed);
	} else {
		//... else clear flags
		printing = false;
		typeNormal = false;
	}
}

// drawTable() creates the HTML code for the LCD panel display
function drawTable() {

   var BGimg = cellbackground;
   var FGimg = fileprefix + "32" + filesuffix;

   var LCDtable = "<TABLE BGCOLOR=\"" + letterbg +
                  "\" WIDTH=\"" +
                  ((cellWidth * cLen) + 8) + 
		  "\" CELLPADDING=\"4\" CELLSPACING=\"0\" BORDER=\"0\">\n";

   LCDtable += "<TR><TD>\n";

   LCDtable += "<TABLE BACKGROUND=\"" + BGimg + "\" WIDTH=" +
                (cellWidth * cLen) + 
		" CELLPADDING=0 CELLSPACING=0 BORDER=0>\n";

   for (r = 0; r < rMax ; r++) {

     LCDtable += "<TR><TD NOWRAP>";
     for (c = 0; c < cLen ; c++) {
	LCDtable += "<IMG \nNAME=\"lcd" + r + "c" + c +
                "\" SRC=\"" + FGimg + "\"";
	LCDtable += "WIDTH=\"" + cellWidth + "\" HEIGHT=\"" +
                cellHeight + "\">";

     }
     LCDtable += "</TD></TR>\n";
   }
   LCDtable += "</TABLE>\n";

   LCDtable += "</TD></TR>\n";
   LCDtable += "</TABLE>\n";

   document.write(LCDtable);
   // this isn't technically polite...
   document.close();
}