So far we haven’t done much with strings except to add them together (“concatenate” them in programmers’ parlance) to make larger strings. We can also take them apart to make smaller strings, and look inside them to see what they contain.
It is useful to always show the user a “parent” page for the page they’re currently viewing. Often your viewers come to your site from a search engine, and they don’t necessarily come in to your site’s main page. By providing a parent page link, you give the viewer the option of switching “up” to a more general view of your site, without going all the way up to the home page and then navigating back down again (assuming they even know how to navigate back down).
Every page except for your home page has a “parent” page, and if you have designed your site well, the “parent” page is in the same folder as the current page, or is in one folder up if your visitor is already visiting your main page for that folder. For example, the parent page of http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson5.html is http://www.hoboes.com/NetLife/Web_Scripting/lessons/. And the parent page of that is http://www.hoboes.com/NetLife/Web_Scripting/. The parent page of that is http://www.hoboes.com/NetLife/ and that has no parent page, since it is the tutorial section’s home page.
Of course, we wouldn’t want to show the parent page if it is the same as the home page. (In looking at your filestamp, you might or might not want to show the “Home” link if you are on the home page. It is up to you, and I’ll leave the JavaScript code for checking it up to you as an exercise.)
What we can do is check the current page--the one we’re viewing--against the home page, the one we created in the “homePage” string.
Add a line to get the parent page’s URL just after you set the homePage string:
homePage = new String('/NetLife/');
parentPage = findParent(document.location, homePage);
This calls a subroutine called “findParent()” which will give us the parent URL of the document’s URL. We’ll write that subroutine in just a bit. Change the document.write line that prints the Home link to:
document.write('<tr><td align="right">');
if (parentPage) {
document.write('<a href="', parentPage, '">Parent</a> | ');
}
document.write('<a href="', homePage, '">Home</a>');
We’re going to put the “parent” link on the same line as the “Home” link. So we break out the part where we create the table cell, and if the “parentPage” string has anything in it, we put that on the line in front of “Home”, with a vertical bar to separate the two words.
function findParent(childPage, homePage) {
childPage = new String(childPage);
homeLocation = childPage.indexOf(homePage);
if (homeLocation >= 0) {
//first, chop off everything in front of the homePage
parentPage = childPage.slice(homeLocation);
//second, find the location of the final / and chop to it
//but first, if the last character is a /, get rid of it
if (parentPage.charAt(parentPage.length-1) == "/") {
parentPage = parentPage.slice(0, -1);
}
slashLoc = parentPage.lastIndexOf('/');
if (slashLoc > 0) {
parentPage = parentPage.slice(0, slashLoc+1);
} else {
parentPage = '';
}
} else {
//the home page is not contained in the child page
parentPage = '';
}
return parentPage;
}
This is probably looking pretty complicated. We haven’t done this sort of thing to strings before.
function findParent(childPage, homePage) {
We are creating a function called “findParent()” that takes two arguments: the page we’re trying to find the parent of (we’re calling it “childPage”) and the ultimate parent (the “homePage”).
childPage = new String(childPage);
First, we ensure that the “childPage” variable is in fact a string object, because we are going to need to use some serious string functions on it.
homeLocation = childPage.indexOf(homePage);
if (homeLocation >= 0) {
//first, chop off everything in front of the homePage
parentPage = childPage.slice(homeLocation);
When you ask for string.indexOf(otherstring), you are looking for the string “otherstring” inside of “string”. This returns a number, from zero to the length of the string, depending on where the “otherstring” is. Actually, the maximum location of “otherstring” is one less than the length of the string, because JavaScript starts counting from zero. The first location in a string is zero, and the last location is the length minus one. If you look for the string “a” in the string “Gila”, “indexOf()” will return 3. If you look for the string “Gi” in the string “Gila”, “indexOf()” will return 0, because that’s the location the string “Gi” starts at in the string “Gila”.
So when we ask for the location of “homePage” inside of “childPage”, we know that homePage exists if the result is zero or more (“indexOf()” returns a negative one if the needle does not exist in the haystack). Since URLs are relative, we can make things a little easier on ourselves by chopping off everything except the homePage portion and whatever exists after the homePage portion. That’s what the “slice()” function does. It slices the string, starting at the number we give it. So when we call “slice(homeLocation)”, we throw away everything that occurs before the homePage portion of the string. If the homePage is “/NetLife/” and the childPage is “http://www.hoboes.com/NetLife/Web_Scripting/” then after the slice, parentPage will be “/NetLife/Web_Scripting/”.
if (parentPage.charAt(parentPage.length-1) == '/') {
parentPage = parentPage.slice(0, -1);
}
We are going to find the parent by chopping off the last portion of the URL. URL portions are separated with the “slash” character. But some URLs have a slash at the end, so we need to get rid of that slash if it exists. The “charAt()” function tells us what character is at that location. Since locations begin at zero, the final location is length minus one. Here, we’re checking to see if the final location in “parentPage” contains a slash. If it does, we do the next line, which slices parentPage from 0 (the first location) to negative one (a special number that tells slice to chop one character off from the end).
slashLoc = parentPage.lastIndexOf('/');
if (slashLoc > 0) {
parentPage = parentPage.slice(0, slashLoc+1);
} else {
parentPage = '';
}
We set the variable “slashLoc” to the location of the last slash in parentPage. The function “lastIndexOf()” is just like “indexOf()” except that it starts looking from the end of the string instead of from the beginning.
Then we check to see if the slash’s location is greater than zero. If it is zero (or less), then there is no parent page, so we just set parentPage to an empty string. If it is greater than zero, we slice parentPage from the beginning to the location of the slash. When you give “slice()” a second number, “slice()” slices to the character in front of that number’s location. So if we want to actually include that character, we need to add one to the slash’s location. We want our parent to include the final slash, so we do the addition.
At this point, if childPage contained, for example, “http://www.hoboes.com/NetLife/Web_Scripting/lessons/” then parentPage now contains “http://www.hoboes.com/NetLife/Web_Scripting/”. The rest of the script just returns this to whoever called the function “findParent()”.
You can see an example of this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson11.html.
Rather than simply write “Parent” as the link’s text, we can write the name of the parent’s folder. This is relatively simple compared to grabbing the parent. In fact, it’s somewhat complementary: instead of grabbing everything before the current location, we want only the current location. If our parent is “/NetLife/Web_Scripting/” we want “Web_Scripting”.
In the “filestamp()” function, under where we set “parentPage”, add a line to set “parentName”:
parentPage = findParent(document.location, homePage);
parentName = getName(parentPage);
Change the line that writes the Parent link to:
document.write('<a href="', parentPage, '">', parentName, '</a> | ');
All we need do now is create the “getName()” function.
function getName(theURL) {
//find the location of the final slash if it exists
if (theURL.charAt(theURL.length-1) == '/') {
theURL = theURL.slice(0, -1);
}
slashLoc = theURL.lastIndexOf('/');
if (slashLoc > 0) {
theName = theURL.slice(slashLoc+1);
//get rid of trailing .xxx
dotLoc = theName.lastIndexOf('.');
if (dotLoc > 0) {
theName = theName.slice(0, dotLoc);
}
} else {
theName = 'Top';
}
return theName;
}
Most of this we’ve already seen. We check to see if the last character is a slash, and if so, we delete it. We then check to see where the final remaining slash is, and this time, we slice from that location instead of to that location. We also check to see if there is a period in the resulting name. This will happen if, for example, our name is “fred.html” or “current.cgi”. Usually, we wouldn’t want to see the “.html” or the “.cgi”, those are for the computer. So we chop anything after (and including) the final period.
If there is no name (for example, the page URL is “/”), we just return the name “Top”.
See this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson12.html.
Now that we can get the name and the URL for the parent, why not show all the parents, in order? This is like the “trail of bread crumbs” that Hansel and Gretel left to know where they came from. It shows the viewer what pages normally “contain” this page, and thus provide possible avenues of exploration for further information.
We already know how to “loop” with the “while” statement, but we didn’t pay much attention to it. Here, we’ll use the “while” statement to keep adding new parents onto the old parent until there is no more parent left.
First, change the “if/then” that prints the parent to:
if (parents) {
document.write(parents);
}
We will create a string called “parents” that contains the names and links to the parent pages. Now, we need to create that variable. Up at the top, where you set the parentPage and the parentName, replace those two lines with:
parents = '';
parentPage = document.location;
while (parentPage = findParent(parentPage, homePage)) {
parentName = getName(parentPage);
parentLink = '<a href="' + parentPage + '">' + parentName + '</a>';
parents = parentLink + " | " + parents;
}
On the first line, we set the “parents” variable to an empty string. We need to do this because, inside the while loop, we are going to be building this string up piece by piece. Each time we get a new parent, we will add the new parent to the pre-existing “parents” variable. But the first time through the loop, there is no pre-existing “parents” variable unless we have already created one. That’s what we do here. We created the “parents” variable, but left it empty.
We do the same thing for “parentPage”. We’ll be setting this in the loop, but we’re also using it the first time through the loop. So we need to set it to our starting point before we enter the loop. We set it to the document’s location. Our first parent will be the immediate parent just above our document’s location.
while (parentPage = findParent(parentPage, homePage)) {
Our “while” will repeat all of the JavaScript lines between the “{” and the corresponding “}” four lines down. It will continue to repeat those lines until the above function returns nothing, or zero. In this case, it will return nothing, since that’s what our “findParent()” function does when there are no more parents. As long as “parentPage” contains something, the “while” will make JavaScript loop through the next few lines. Remember that “parentPage” is getting smaller each time we call the “findParent()” command. Eventually, it will be empty, and the loop will stop.
parentName = getName(parentPage);
parentLink = '<a href="' + parentPage + '">' + parentName + '</a>';
parents = parentLink + " | " + parents;
For each parent, we get the name of the parent. Then we create the link. And then we “prepend” the link to the previously existing “parents” variable. When the loop is completed, the “parents” variable will contain each of the parents, separated by a vertical bar. See an example of this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson13.html.
I’m sure you’ve seen web pages that tell you how many people have visited a site. JavaScript can’t do that. But it can tell the visitor how many times their computer has visited the site. Your script can do this by storing a “cookie” on their computer, and then checking that cookie every time it runs. A “cookie” is like a variable that lasts even when the script isn’t running. The script can ask for it whenever the script starts up, and then modify it as well. We can use a cookie to keep track of how many times the current user has loaded our web page.
Cookies, like JavaScript, exist on the client computer. They can be turned off, and they can be modified by the client. Like JavaScript, you cannot trust cookie values. But you can use them to assist the user, to store information such as when they last visited and what their browsing preferences are if you allow a choice of preferences.
Try to keep the number of cookies you use down to one or two. If you need more, you probably need to keep a database on the server to store the user’s information.
We are going to use our cookie to keep track of how many times the visitor has been to our site. Using cookies comes in two parts: setting the cookie, and getting the cookie. Each cookie has a name, a path, and a value. The path is important. It ensures that we are the only ones who get to see this cookie (other than the client). If, for example, you are using geocities, and you set the path to “/”, you’ll end up giving that cookie information to anyone else at geocities.
In your scripts.js file, after setting “changeDate”, add three lines for getting, adding to, and setting your cookie.
changeDate = new Date(document.lastModified);
visitCount = getCookie('Visits', 0);
visitCount++;
setCookie('Visits', visitCount, homePage, 365);
There are two functions in there that we have not yet seen. One is “getCookie()” and one is “setCookie()”. We will need to create both of these.
We will also need to write this visit information to the web page. Add the following lines to the set of writes you’re doing:
document.write('<tr><td align="right">You have visited us ', visitCount, ' time');
if (visitCount != 1) {
document.write('s');
}
document.write('</td></tr>');
If visitCount is one, we tell the viewer that they have “visited us 1 time”. Otherwise, (for example, they’ve visited us four times) we tell them that they have “visited us 4 times”. We write the “s” in “times” only if visitCount is not 1.
Now on to our cookie functions. Our first function will get the cookie. If there is no cookie (which there won’t be, the first time we get it), it returns a pre-specified default value.
//get a cookie
function getCookie(cookieName, defaultValue) {
//see if the cookie is there already
cookieName = new String(cookieName + '=');
//get a list of all cookies
allcookies = document.cookie;
cookieLoc = allcookies.indexOf(cookieName);
if (cookieLoc >= 0) {
//the viewer has been here before
cookieStart = cookieLoc + cookieName.length;
cookieEnd = allcookies.indexOf('; ', cookieStart);
if (cookieEnd == -1) {
cookieEnd = allcookies.length;
}
cookieValue = allcookies.slice(cookieStart, cookieEnd);
cookieValue = unescape(cookieValue);
} else {
cookieValue = defaultValue;
}
return cookieValue;
}
function getCookie(cookieName, defaultValue) {
Our function will take two arguments: the name of the cookie, and the default value if the cookie doesn’t exist yet.
//see if the cookie is there already
cookieName = new String(cookieName + '=');
JavaScript gets cookies as one big string full of all the cookies (like a cookie jar...). In order to find the cookie we want, we have to look for it. Cookies always begin with the cookie’s name and then an “=” sign.
//get a list of all cookies
allcookies = document.cookie;
The string that has all the cookies in it can be gotten from “document.cookie”.
cookieLoc = allcookies.indexOf(cookieName);
if (cookieLoc >= 0) {
//the viewer has been here before
We use “indexOf()” to see if the cookieName exists in the cookie jar. If it does (if we get back any number zero or greater), then the cookie exists.
cookieStart = cookieLoc + cookieName.length;
The start of the cookie value will be write after the cookie’s name. So we add the length of the cookie’s name to the start of the cookie’s name to find out where the value starts.
cookieEnd = allcookies.indexOf(';’, cookieStart);
Cookie values always end with a semicolon, and semicolons cannot be in the cookie’s value, so we can find the end of the cookie by looking for the next semicolon after the start of the cookie value.
if (cookieEnd == -1) {
cookieEnd = allcookies.length;
}
But, oops, there is one case where the cookie value might not end in a semicolon: if it is the last cookie value in the list. Then, the end of the value is the end of the cookie list.
cookieValue = allcookies.slice(cookieStart, cookieEnd);
cookieValue = unescape(cookieValue);
We use “slice()” to grab the cookie value from start to end, and then we “unescape” the cookie’s value. There are special characters, such as the semicolon, that cannot appear in a cookie’s value. When placed there, they have to be “escaped”. We unescape them with the unescape function.
Then we have the cookie’s value, so we return it to whoever called the function.
We now are able to get cookies, but we still haven’t set any.
//set a cookie that will expire in x days
//and is valid on validPath
function setCookie(cookieName, cookieValue, validPath, expiration) {
//get the current time
expirationDate = new Date();
//add the requisite number of days by converting to milliseconds
expirationDate = expirationDate.valueOf() + expiration*24*60*60*1000;
//turn it back into a date
expirationDate = new Date(expirationDate);
expires = '; expires=' + expirationDate.toGMTString();
path = '; path=' + validPath;
theCookie = cookieName + '=' + escape(cookieValue) + path + expires;
document.cookie = theCookie;
}
We are doing a little work with dates here as well as with setting cookies.
function setCookie(cookieName, cookieValue, validPath, expiration) {
Our function is called “setCookie()” and it takes four arguments: the name of the cookie, the value that the cookie will store, the path on the server where the cookie will be returned (it will be returned for that path and for any lower paths), and the number of days the cookie will last before it expires.
//get the current time
expirationDate = new Date();
//add the requisite number of days by converting to milliseconds
expirationDate = expirationDate.valueOf() + expiration*24*60*60*1000;
//turn it back into a date
expirationDate = new Date(expirationDate);
We get the current time by asking for a new date. Then, we take the value of the date in milliseconds--that is, the number of milliseconds since the start of time on this computer--and add a day’s worth of milliseconds for every day we want this cookie to last. We then turn the variable back into a date by asking for a new date again, but this time given it our new number of milliseconds to create the date from. We now have a date that is the correct number of days in the future.
As a side note, remember that for this to work, the date on both the server and on the client must be correct. If one is off, cookies will expire before they are supposed to. Dates and times are extremely important to modern computers. Always set yours to a trusted central time server. See your computer’s “Date & Time” control for more information about how to do that automatically.
expires = '; expires=' + expirationDate.toGMTString();
The cookie is made up of parts separated by semicolons. Our expiration date will be a semicolon, a space, the word “expires”, an “=” sign, and then the expiration date in universal time.
path = "; path=" + validPath;
Similarly, the path is a semicolon, the word “path”, an “=” sign, and the path.
theCookie = cookieName + "=" + escape(cookieValue) + path + expires;
We construct the cookie by placing the cookie’s name, the “=” sign, and the escaped cookie value (remember above where we unescaped the value?) in front of the path and the expiration date.
document.cookie = theCookie;
Finally, we give the cookie back to the document. You might expect that the above line would erase all current cookies and replace them with our single cookie, but this is a special case. JavaScript takes the cookie we’re giving it and adds it to the list of cookies. If the cookie we’re giving it already exists, it replaces that cookie, but does not erase any other cookies.
See an example of this script in action at http://www.hoboes.com/NetLife/Web_Scripting/lessons/lesson14.html. Click reload to see the number increase by one. Remember that you’ll need to have cookies turned on in order to see it!
For an additional exercise, try storing the document’s last modification date in a cookie, and then let the viewer know if the document has been modified since they last visited.