header("Location: http://slayeroffice.com/"); ?>
I suspect that a good many traditionalists will be appalled at the way my site is set up and coded...its not exactly conventional. A lot of it is "hmm, i wonder how i could do this without doing it the most obvious way", as well as the fact that I do just about everything I possibly can on the client-side as I come from a school of thought that takes any effort it can to reduce server CPU load. Whatever excuse you'll accept, it's my site and I'll code it how i like. ;)
To facilitate this walk through, I will trace through the code as it is executed, begining with initPage() and going from there. initPage(), as you no doubt have guessed, is the very first method called (via the onLoad event of the BODY element) and is the page's equivalent to a java application's public static void main(). It is where things are, you guessed it -- initialized.
function initPage() { buildLinksArray(); checkForParam(); writeNews(); stupidQuote(); initCommentsArray(); }
function buildLinksArray() { for(i=0;i<document.links.length;i++) { if(document.links[i].className == "subNavLink")linkArray[linkArray.length] = document.links[i].href; } }
One of the most important elements on the main page is a 1x1 iframe called contentServer. If you look at the HTML source of the main page, you will see that every link in the navigation menu has its target attribute set to contentServer. When a user clicks on one of these links, for example the link to Alien Sequence in the arcade menu, the page loads in the contentServer iframe. The HTML source for a content page will look like this:
<script language="javascript" src="../../scripts/page_methods_v2.js"></script> <body onload="sendContent();"> <div id="contentDiv" class="contentHTML"> <b><font size=3>Alien Sequence</font></b> <hr noshade color="#00669" size=1> <b>Current Version:</b> 1.1<br> <b>Last Revision:</b> 12.01.02<br> <b>Language:</b> Javascript<br> <b>Requires:</b> Windows, Internet Explorer v5.5+<br> <img src="http://slayeroffice.com/gr/arcade_gr/alien_shot.gif" vspace=5><br> A rendition of the electronic handheld game "Lights Out" by Tiger. 40 levels of puzzles and sets a cookie so you dont have to start over from the begining each time you come back to play. <hr noshade color="#00669" size=1> <a target="_blank" href="/arcade/alien_sequence/"><img src="/gr/go.gif" border=0></a> <a target="_blank" href="../viewSource.php?f=arcade/alien_sequence/index.html&a=Alien Sequence"><img src="/gr/viewsource.gif" border=0></a> <a href="javascript:ocw()"><img src="/gr/comment.gif" border=0></a> </body>
function sendContent() { if(parent.document.location == document.location) { location.href="http://slayeroffice.com/index.php?c=" + location.href; return; } else { parent.serveContent(); } }
function serveContent() { if(gecko!=-1) { contentBlock = document.getElementById("contentServer").contentDocument.getElementById("contentDiv").innerHTML; } else { contentBlock = document.contentServer.document.getElementById("contentDiv").innerHTML; } document.getElementById("content").innerHTML = contentBlock; showComments(); }
What if the content page is served up outside of the confines of the contentServer iframe? That brings us back to the redirect and the first function of the linkArray variable. Why redirect them? The content pages are very, very ugly outside of the controls of the main page's style sheets - not to mention that there is no way to get back to the main page or to any other content from them, so they have no business being served up on their own.
So, when the content page is served up outside of contentServer, the user is redirected back to the main page. This could provide a frustrating experience for a user who followed a link from google to very briefly see the content they wanted only to be bounced away from it with no idea how to navigate back to it. Looking back at the sendContent() function, you'll see that a paramater called c is passed back to the main page, with a value of location.href.
This brings us to the third function called from initPage() -- checkForParam().
function checkForParam() { paramString = location.href; if(paramString.indexOf('?')>-1) { param = paramString.substring(paramString.indexOf('?')+3,paramString.length); for(i=0;i<linkArray.length;i++) { if(linkArray[i].indexOf(param)!=-1) { if(gecko==-1) { document.contentServer.document.location.href = linkArray[i]; } else { document.getElementById("contentServer").src = linkArray[i]; } break; } } } }
But why store the links in linkArray you ask? Why not just load any old url thats passed to the main page into contentServer? Because not any old url needs to be -- for example, if linkArray wasnt in place and some smart ass decided to pass index.php to the main page, he would send the browser into an eternal loop as it loaded itself over and over and over again. Thats no good for my server and no good for that user's browser which would eventually crash.
Next in the initPage() line-up is writeNews():
function writeNews() { closeLists(); document.getElementById("mComments").innerHTML = ""; mDoc = document.getElementById('content'); mHTML = ""; for(i=(news.length-1);i>=(news.length-3);i--)mHTML+= news[i] + "<hr noshade color=#006699 size=1>"; mDoc.innerHTML = mHTML + "<p align=right><a class=\"newsLink\" href=\"javascript:writeAllNews();\">[archived news]</a></font>"; }
function closeLists() { for(i=0;i<listNames.length;i++)document.getElementById(listNames[i]).style.display="none"; }
writeNews() is a very simple function -- all it does is loop backwards over the length of an array called news until it has three news items, append each index's value to mHTML and then set the content div's(yes, the same div element that is used to display the content pages) innerHTML to the value of mHTML.
You'll notice the reference to writeAllNews() as an href wrapped around [archived news] which is written after the most recent three news blurbs.
function writeAllNews() { openWin("",300,300); mHTML="<body bgcolor=#E8F8FF><title>slayer.office.archived_news</title><link rel='stylesheet' type='text/css' href='scripts/styles_v2.css'>"; for(i=0;i<news.length;i++){ mHTML += "<font face=arial color=#006699 style=\"font-size:8pt;text-decoration:none;\">" + news[i] + "<hr noshade color=#006699 size=1>"; } w.focus(); w.document.write(mHTML); }
function openWin(url,wdth,hght) { t = (screen.height-hght)/2; l = (screen.width-wdth)/2; w=window.open(url,'cWin','nostatus,resizable=no,scrollbars=1,width=' + wdth + ',height=' + hght + ',top=' + t + ',left=' + l); }
writeAllNews() loops over the entire length of the news array and appends each index's value to mHTML and does a little HTML formatting with it. When the loop completes, it sets focus to the blank window and then uses a document.write(mHTML) to create the content on the page.
So where does this news array come from? It comes from a PHP script that is referenced as a javascript src. news.php looks like this:
<?header("Content-Type: text/javascript");?> news = new Array(); <?php $rFile = fopen("news.txt","r"); $contents = fread ($rFile, filesize("news.txt")); fclose($rFile); $contents = split(chr(10),$contents); for($i=0;$i<count($contents);$i++) { $tmp = split('~',$contents[$i]); $nDate = $tmp[0]; $nContent = $tmp[1]; $nContent = str_replace('\'','\\\'',$nContent); echo("news[" . $i . "]='<b>" . $nDate . ":</b> " . $nContent . "';" . chr(13)); } ?>
06.23.03~Added <a href="http://slayeroffice.com/content/code/swapNode.html" class="newsLink" target="contentServer">swapNode</a> to the code section as an example of how to rearrange objects in the DOM by using cloneNode, insertBefore and removeChild.
Why not just keep the news updates as javascript variables in something like news.js? Well, I used to do it that way -- but then I decided to do an RSS feed for the site, which would mean that I would have needed to update two seperate files with identical information. I am much too lazy for this sort of thing, so news.txt is used by /rss/index.php as well. One formats the text into javascript, the other into XML.
/rss/index.php:
<?header("Content-Type: text/xml");?> <?php echo('<?xml version="1.0" encoding="iso-8859-1"?>');?> <rss version="0.91"> <channel> <title>slayeroffice.com</title> <link>http://slayeroffice.com</link> <description /> <language>en-us</language> <webMaster>steve@slayeroffice.com</webMaster> <?php $rFile = fopen("../scripts/news.txt","r"); $contents = fread ($rFile, filesize("../scripts/news.txt")); fclose($rFile); $contents = split(chr(10),$contents); for($i=count($contents)-1;$i>count($contents)-10;$i--) { $tmp = split('~',$contents[$i]); $newsDate = $tmp[0]; $newsContent = $tmp[1]; $newsContent = str_replace('>','>',$newsContent); $newsContent = str_replace('<','<',$newsContent); echo('<item>'); echo('<title>' . $newsDate . '</title>'); echo('<description>'); echo(str_replace('\\','',$newsContent)); echo('</description>'); echo('<link>http://slayeroffice.com</link>'); echo('</item>'); } ?> </channel> </rss>
function stupidQuote() { document.getElementById('mQuote').innerHTML = quote[Math.floor(Math.random() * quote.length)]; }
function initCommentsArray() { for(i=linkArray.length-1;i>-1;i--) { x = linkArray[i]; x = x.replace(/http:\/\/slayeroffice.com\/content\//,""); x = x.split("/"); x[1] = x[1].replace(/\.html/,""); if(!comments[x[0]])comments[x[0]] = new Array(); if(!comments[x[0]][x[1]])comments[x[0]][x[1]] = new Array(); } initComments(); }
The function sets the value of each index in linkArray to a variable called x. It then strips out http://slayeroffice.com/content/ from the value and splits what remains on a forward slash. Finally it removes the .html from the 1st index of the newly created array.
To what end? Ah, this is coming to the good part now. The value of each index in x is to be used as primary and secondary index's in an array called comments. As you are sure to note, the very next line checks for the existence of comments[x[0]] and defines it as a new Array(), and then does the same for its subindex.
For the faint of heart, here is what this breaks down into:
function initComments() { <? include 'comments.txt'; echo(chr(10));?> }
Now is probably the best time to explain how the comment system works. You've probably guessed that much like the news, the comments arent actually javascript. If this is what you've guessed I am sorry to inform you that you are incorrect, especially given that you have read so far into this now. I feel as though I have lead you astray and for this I apologize. The comments, as they are stored, are javascript arrays. Very deep arrays, at that. Here is an example of what a comment array looks like:
comments["arcade"]["snake"][comments["arcade"]["snake"].length] = new Array("test","08.6.2003@11:59 AM","steve");
These arrays are stored in comments.txt, the file included via php in initComments(). How do they get there? The first step, obviously, is the HTML page that hosts the form in which the user enters their comment:
<html> <title>slayeroffice | add a comment</title> <style type="text/css"> @import:url("http://slayeroffice.com/scripts/styles_v2.css"); </style> <script language="javascript" type="text/javascript"> if(!window.opener)location.href = "http://slayeroffice.com"; function initHiddenFields() { zIndex = window.opener.getCommentIndex(); document.forms.frm.arrayIndex.value = zIndex[0]; document.forms.frm.arraySubIndex.value=zIndex[1]; } </script> </head> <body bgcolor="#E8F7FD" onLoad="initHiddenFields();"> <div id="content"> <form name="frm" method="POST" action="/scripts/addComment.php"> Your name:<input type="text" class="textField" size=30 name="userName"><br> Your comment:<br> <textarea class="textField" rows=8 cols=46 name="userComment"></textarea><br> <input type="hidden" name="arrayIndex"><input type="hidden" name="arraySubIndex"> <input type="submit" value="Add comment" class="btn"> </form> </div> </body> </html>
function getCommentIndex() { url = document.all?document.contentServer.document.location.href:document.getElementById("contentServer").contentDocument.location.href; if(url.indexOf("http")==-1 || url.indexOf("contact.html")!= -1 || url.indexOf("links")!=-1 || url.indexOf("about.html")!=-1)return 0; url = url.replace(/http:\/\/slayeroffice.com\/content\//,""); url = url.split("/"); url[1] = url[1].replace(/\.html/,""); return url; }
Once I have url, I check to make sure its value is something I want to deal with. If it doesn't contain "http" or does contain "contact.html", "links" or "about.html" I have the function return 0. On a return of 0, I know that I do not have something that a comment can be made on, so I disallow it.
If the value of url passes this condition, I then do to it what I do to the values of linkArray in initCommentsArray(), that is, strip it of everything we dont want and split it into two values. The function then returns to the calling function those two values. In the case of the example made for initCommentsArray(), these values would be arcade and alien.
Returning to the comment entry page and initHiddenFields() -- the return of getCommentIndex() is then set as the value of two hidden fields in the form, arrayIndex and subArrayIndex. The user does their thing, hits the submit button and the form POSTs to addComment.php.
<html> <head> <title>slayeroffice | comment added</title> <style type="text/css"> @import:url("http://slayeroffice.com/scripts/styles_v2.css"); </style> <script language="javascript"> setTimeout("window.close()",3000); </script> </head> <? $arrayIndex = $_POST["arrayIndex"]; $arraySubIndex = $_POST["arraySubIndex"]; $userName = $_POST["userName"]; $userName = htmlspecialchars($userName); $userName = trim($userName); $userName = stripslashes($userName); $userComment = $_POST["userComment"]; $userComment = str_replace(chr(10),'',$userComment); $userComment = str_replace(chr(13),'',$userComment); $userComment = htmlspecialchars($userComment); $userComment = trim($userComment); $userComment = stripslashes($userComment); $rDate = date("m.j.Y@h:i A"); if($arrayIndex != "" || $arraySubIndex != "" || $userComment != "") { if ($userName == "") $userName = "anonymous"; $js = chr(10) . "comments[\"" . $arrayIndex . "\"][\"" . $arraySubIndex . "\"][comments[\"" . $arrayIndex . "\"][\"" . $arraySubIndex . "\"].length] = new Array(\"" . $userComment . "\",\"" . $rDate . "\",\"" . $userName . "\");"; $rFile = fopen("comments.txt","a"); fwrite($rFile,$js,strlen($js)); fclose($rFile); } ?> <body bgcolor="#E8F7FD"> <div id="content"> Thanks for adding your comment. Reload slayeroffice to see it.<p>This window will close in a second or two. </div> </body>
So now that you know how the comments are stored, you might be wondering how they are served. Observe:
function showComments() { document.getElementById("mComments").innerHTML = ""; commentIndex = getCommentIndex(); if(commentIndex == 0)return; if(comments[commentIndex[0]][commentIndex[1]] && comments[commentIndex[0]][commentIndex[1]].length) { mHTML = "<b>Comments:</b><p>"; for(i=0;i<comments[commentIndex[0]][commentIndex[1]].length;i++) { mDate = comments[commentIndex[0]][commentIndex[1]][i][1]; mComm = comments[commentIndex[0]][commentIndex[1]][i][0]; mName = comments[commentIndex[0]][commentIndex[1]][i][2]; mHTML+= "<b>" + mDate + "</b> - " + mComm + "<br>" + mName + "<p>"; } document.getElementById("mComments").innerHTML = mHTML; return; } document.getElementById("mComments").innerHTML = "<b>No comments</b>. Click the comment button above to add one."; }
The first thing we do in showComments() is clean up the div that comments are written to, mComments. We then make a quick call to our good friend getCommentIndex(), explained just moments ago. We first check to make sure that the array in question exists AND that it has a non-zero length. If this condition is not met, we simply set the innerHTML of mComment to "No Comments", otherwise we set up our HTML to display the comments.
Looping over the length of the comment array, we access each sub-sub-index (yes, sub-sub) of the secondary index and set its value to a temporary variable. Array's as array index's are always hard to read, so here's how that would break down using the previous example of arcade and alien.
The values of these arrays are appended, as always, to mHTML and then set as the innerHTML of mComments.
The last remaining function is el, which is short for "expand list". Its a very simple function that simply change the display attribute of the top level menu elements from none to block.
function el(objID) { if(openMenu != objID) { closeLists(); openMenu = objID; document.getElementById(objID).style.display="block"; document.title = "slayer.office | " + objID } else { closeLists(); openMenu="objID"; document.getElementById(objID).style.display="none"; document.title = "slayer.office"; } }
Well, that about covers it. The inner workings of slayeroffice.com -- congratulations if you made it all the way down here, I'm proud of you. I really didnt think I could make it this far and I wrote this damned thing. Hopefully you were able to get some useful information from this rambling, or maybe it gave you some ideas for how to build your site. If you have any questions about anything I covered in this document or have an idea to improve any of it, feel free to send a mail to steve@slayeroffice.com.