Click here to Skip to main content
Click here to Skip to main content

DHTML Hierarchical Web Tab

By , 23 Sep 2005
 

Introduction

My previous DHTML Web Tab article implemented a simple client-side web tab using HTML, JavaScript and CSS. Using the same methods of configurability and implementation style as the previous web tab, this article extends the web tab by providing a hierarchical system of web tabs with visual DXImageTransform filter effects. That is, each tab can consist of further sub tabs. For UI simplicity, the hierarchy of tabs is limited to one level deep.

Configuration

Parallel to the previous implementation, the tabs and their respective local/remote content sources are specified in an array; each string item in the arrays represent a tab and details of it separated by a “|” pipe character. Note that each string item as well as an array is followed by a “,” comma, except the last. The difference from the previous implementation however, is that the first string element in each array defines the parent tab and the subsequent string elements in that same array define the child tabs. For example, the tabs in the above screenshot were constructed using the following configuration:

var tabs = new Array
(
 new Array("Google|http://www.google.com/", 
   "News|http://news.google.com/", "Froogle|http://froogle.google.com/"),
 new Array("Microsoft|http://www.microsoft.com/|*", 
   "MSDN|http://www.msdn.com/", 
   "Office|http://office.microsoft.com/home/default.aspx"),
 new Array("Yahoo!|http://www.yahoo.com/", 
   "News|http://news.yahoo.com/", 
   "Finance|http://finance.yahoo.com/", 
   "Mail|http://mail.yahoo.com/")
);

In general, every first element in each array defines a parent tab and the subsequent elements as corresponding child tabs. The following template summarizes:

“Parent tab-text | URI | [*]”, [“Child1 tab-text | URI”], 
    [“Child2 tab-text | URI”], [“ChildX tab-text | URI”], ...

Square brackets “[…]” denote optional parameters, thus, child tabs are optional. The following screenshot example illustrates how to use the template:

A second “|” pipe character followed by a “*” wildcard character after specifying the URI of a parent tab will make that specific parent tab a default selected tab when the page loads, example:

"Google | http://www.google.com/ | *"

Internet Explorer DXImageTransform filter effects can be applied to child tabs via assigning a global variable named DXImageTransformFilter to one of the many DXImageTransform filters. A few examples of DXImageTransform filters included:

DXImageTransform.Microsoft.Wheel(duration=2, spokes=5)
DXImageTransform.Microsoft.Barn(duration=2, orientation=horizontal)
DXImageTransform.Microsoft.Blinds(duration=2, bands=5)
DXImageTransform.Microsoft.CheckerBoard(duration=2)
DXImageTransform.Microsoft.Fade(duration=2)
DXImageTransform.Microsoft.GradientWipe(duration=2, wipeStyle=0)
DXImageTransform.Microsoft.Iris(duration=2, irisStyle=STAR)
DXImageTransform.Microsoft.Iris(duration=2, irisStyle=CIRCLE)
DXImageTransform.Microsoft.Pixelate(duration=2, maxSquare=40)
DXImageTransform.Microsoft.Wheel(duration=2, spokes=5)
DXImageTransform.Microsoft.RandomDissolve(duration=0.5)
DXImageTransform.Microsoft.Spiral(duration=2)
DXImageTransform.Microsoft.Stretch(duration=2, stretchStyle=push)
DXImageTransform.Microsoft.Strips(duration=2, motion=rightdown)

For more on DXImageTransform filter effects and their configuration, see Introduction to Filters and Transitions in MSDN. All of the above listed example DXImageTransform filter effects are stored into an array named DXImageTransformLibrary; thus, a library of filters can be built-up. Assigning the global variable DXImageTransformFilter to null will use a random filter effect from the library of filters. For example:

A visual “fade” effect DXImageTransform filter:

var DXImageTransformFilter = 
  “DXImageTransform.Microsoft.Fade(duration=2)”

Again, the same visual “fade” effect DXImageTransform filter, but specified from the library:

var DXImageTransformFilter = DXImageTransformLibrary[4];

Or, use a random DXImageTransform filter effect:

var DXImageTransformFilter = null;

That completes the configuration of the web tab in script; the final step is to declare HTML <DIV> container objects in the body of the web page as:

<DIV ID="divTabStrip">
<DIV ID="divTabFrame">

Finally, the web tab is initialized via calling the tabOnLoad function:

<BODY onLoad="tabOnLoad()">

Look & Feel

Borrowing from the previous implementation of the DHTML Web Tab, the look & feel of the tabs are adjusted entirely with CSS. For completeness, the basics are described. When a tab is selected by being clicked upon, its look & feel is defined by the .tabOn style. Conversely, a tab’s neutral look & feel in its unselected state is defined by the .tabOff style. The attributes that are configurable for each .tabOn & .tabOff style are as below, .tabOn style shown in this example:

 FONT-FAMILY: Verdana;
 FONT-SIZE: 11;
 FONT-WEIGHT: 700;
 TEXT-ALIGN: CENTER;
 COLOR: #FFFFFF;
 BACKGROUND-COLOR: #FF9900;
 BORDER-BOTTOM: #FF9900 1PX SOLID;
 BORDER-TOP: #FF9900 1PX SOLID;
 BORDER-LEFT: #FF9900 1PX SOLID;
 BORDER-RIGHT: #000000 1PX SOLID;
 HEIGHT: 20;
 CURSOR: HAND;

Likewise, the look & feel of the tab frame, which just essentially is an <IFRAME>, is adjusted via the CSS .tabFrame style; attributes configurable are:

 BORDER-RIGHT: #FF9900 9PX SOLID;
 BORDER-TOP: #FF9900 9PX SOLID;
 SCROLLBAR-FACE-COLOR: #6699CC;
 SCROLLBAR-HIGHLIGHT-COLOR: #FFFFFF;
 BORDER-LEFT: #FF9900 9PX SOLID; WIDTH: 100%;
 SCROLLBAR-SHADOW-COLOR: #6699CC;
 SCROLLBAR-ARROW-COLOR: #FFFFFF;
 BORDER-BOTTOM: #FF9900 9PX SOLID;
 SCROLLBAR-DARKSHADOW-COLOR: #6699CC;
 HEIGHT: 95%;

A point worthy of reiteration from the previous article is that the BORDER-TOP color style attribute of the <IFRAME> main tab content area, .tabFrame, has to be the same as the BORDER-BOTTOM color style attribute of the tab buttons when they are selected, .tabOn. This is so that when the tab button is selected, it will look as part of the <IFRAME> and provide a continuous and consistent look.

Design

Just as the previous implementation, this web tab is again composed of HTML buttons for the tabs and an <IFRAME> for the main content area. However, in addition to this implementation, every child set of tabs reside in an individual table division:

The first table division is where all the parent tabs reside. A parent tab can consist of zero or more child tabs. Each of those child set of tabs belong to a subsequent unique table division.

All child sets (table divisions which house the sub tabs) are by default hidden from view. Aside the main operation of populating the <IFRAME> with content upon clicking a parent tab, the unique table division which houses the corresponding sub tabs in the tab hierarchy appear visually with a DXImageTransform filter effect.

Since only one table division (child set of sub tabs) is visible at a time, in theory, this should eliminate any potential horizontal scrolling that may be caused by a tab strip consisting of a large number of tabs/sub tabs.

Implementation

The hierarchy of tabs are managed by using an array of arrays; - each item in the array is an array itself. This is purely so to simplify the construction of the web tab – rather than looping through one user-defined array at a time, a nested loop can be implemented to loop through nested arrays; thus referencing only one user-defined array.

The web tab is constructed when the tabOnLoad function is called, it’s the “main” entry point of the web tab construction. This declares a HTML table, but before the table is completed, calls are made to the tabLoadParents() and tabLoadChildren() functions:

HTML += "<TABLE BORDER='0' CELLPADDING='0' CELLSPACING='0' WIDTH='100%'>";
HTML += "<TD ALIGN='LEFT'>";
tabLoadParents();
tabLoadChildren();

The tabLoadParents() function seeks out the parent tabs by looping through each array, picking out the first string element in each. Every first string element in each array is then split at every “|” pipe character to extract various parts of the string, creating a HTML button with the .tabOff CSS style as well as assigning the buttons with a unique ID and an onClick event handled by the tabOnClick function:

for (var i = 1; i < tabs.length; i++)
{
 var tab = tabs[i][0].split("|");
 HTML += "<INPUT TYPE='BUTTON' ID="+ i + 
         " CLASS='tabOff' VALUE="+tab[0] + 
         " onClick='tabOnClick("+i+", "+i+", 0)'>";
}

Every HTML button created in the tabLoadParents() function is added to a table division which in turn is appended to the HTML table created earlier.

Next, the tabOnLoad function calls the tabLoadChildren() function. The tabLoadChildren() function first establishes what DXImageTransform filter effect has been selected to be used on child tabs when they appear. If no filter has been selected, then a random filter is applied from the DXImageTransformLibrary:

if (DXImageTransformFilter == null)
{
 DXImageTransform = DXImageTransformLibrary[Math.round
                      ((Math.random()*DXImageTransformLibrary.length-1)+0)];
}

The tabLoadChildren() function then performs its main task of seeking out child sets of sub tabs from the array of arrays via a nested for-loop. Since every first element in each array defines a parent tab, the subsequent elements define the parent tab’s corresponding child tabs; thus, the nested for-loop begins at the second position of each array and loops until the last element in each array is reached.

for (var i = 1; i < tabs.length; i++)
{
 HTML += "<TD STYLE="+DXImageTransform+" ID=child"+i+">";

 for (var j = 1; j < tabs[i].length; j++)
 {
  var tab = tabs[i][j].split("|"); 

  var childID = i + "" + j;
  HTML += "<INPUT TYPE='BUTTON' ID="+ childID + 
          " CLASS='tabOff' VALUE="+tab[0] + 
          " onClick='tabOnClick("+childID+", "+i+", "+j+")'> ";  
 }

 HTML += "</TD>;
}

Every string element representing a child tab is given the same treatment of being split at every “|” pipe character to extract various parts of the string, creating a HTML button with the .tabOff style as well as assigning them with a unique ID and an onClick event handled by the tabOnClick function. After the nested for-loop has completed an iteration, that is, seeked out a child set of tabs, then that child set is allocated its own table division which is then appended to the HTML table created earlier. Every table division is also allocated a unique ID representing a child set of tabs.

After making calls to the tabLoadParents() and tabLoadChildren() functions to construct the respective parent and child tabs, the tabOnLoad() function then completes the HTML table constructed earlier and binds it to the divTabStrip <DIV> container object:

divTabStrip.innerHTML = HTML;

The tabOnClick function is called when any tab, either parent or child is clicked. The purpose of this function is to change the style of the clicked tab to the .tabOn style and change all other tabs to the .tabOff style as well as updating the <IFRAME> content area. The function requires three parameters; ID, parent and child. The first parameter represents the ID of which tab (HTML button) has been clicked – either parent or child so that the .tabOn CSS class can be applied to it. The last two parameters pinpoint the exact location of the clicked tab in the nested arrays so that further information can be sought-out about that tab to update the contents of the <IFRAME>:

var tab = tabs[parent][child].split("|");
divTabFrame.innerHTML = "<IFRAME SRC="+tab[1]+" CLASS='tabFrame'></IFRAME>";

If the tabOnClick function is called by a tab with the last parameter child set to 0, it signifies that the caller is a parent tab which has been clicked and therefore instructs all child sets of sub tabs (table divisions) to disappear except the corresponding child set of sub tabs that belong to the clicked parent tab:

if (child == 0)
{
 for (var i = 1; i < tabs.length; i++)
 {
  tabHide("child"+i);
 }

 tabShow("child"+ID);
}

The tabShow and tabHide functions respectively show and hide table divisions which house sets of child tabs. The functions are called with a parameter defining the ID of which table division to show or hide. A call to each of these functions apply the selected DXImageTransform filter to the table division in question, using:

var e = document.getElementById(ID);
e.filters[0].Apply();

When a call is made to show a particular table division to show a set of child tabs, the table division is made visible playing the DXImageTransform filter effect, using:

e.style.display = "block";
e.filters[0].Play();

Likewise, if a call is made to hide a particular table division to hide a set of child tabs, the table division is made invisible and the DXImageTransform filter effect stopped:

e.style.display = "none";
e.filters[0].Stop();

Notes

  • As a quick bug/logical error fix, an empty nested array is required to be declared before any other tab hierarchy arrays are defined:
     var tabs = new Array
     (
      /*BUGFIX*/ new Array(),
      new Array("Google|http://www.google.com/|*", 
          "News|http://news.google.com/", 
          "Froogle|http://froogle.google.com/"),
      new Array(…)
     ); 
  • Extended from DHTML Web Tab Control.
  • Tested only with IE6.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Steve Puri
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionSave Cotent in tabsmemberembo128 Oct '07 - 16:26 
Very handy script, nice job!
 
Is there any way to cache the content of the tabs?
 
So that clicking off and then back shows teh same data that was there before.
 
page 1 is a whois lookup
page 2 is a dns dig
 
I run whois then click off and run dig, click back to whois and the results of the script still show ?
 
Thanks in advance.
 
Embo
Generaleasy question!memberroninzyf18 Sep '07 - 23:03 
hi,i like it. but i have a easy question!I want to chang the link-page for myself.
example:
new Array("Google|http://www.google.com/", "News|http://news.google.com/", "Froogle|http://froogle.google.com/"),
changed:
new Array("Google|default.asp")
but the result was wrong!
Can you tell me how to do?
thank you!
GeneralRe: easy question!memberSteve Puri18 Sep '07 - 23:50 
The following works for me:
 
// Tab Name | URL | * (Default Selected Tab)
var tabs = new Array
(
/*BUGFIX*/ new Array(""),
new Array("Google|Default.asp"),
new Array("Microsoft|http://www.microsoft.com/|*", "MSDN|http://www.msdn.com/", "Office|http://office.microsoft.com/home/default.aspx", "Windows|http://www.microsoft.com/windows/default.mspx", "MSN|http://www.msn.com"),
new Array("Yahoo!|http://www.yahoo.com/", "News|http://news.yahoo.com/", "Finance|http://finance.yahoo.com/", "Mail|http://mail.yahoo.com/")
);

 
Make sure that your file Default.asp exists and that the path to it is correct.
GeneralSTOP RELOADINGmemberDelver7 Jun '06 - 8:36 
How Can I Stop the reloading of the pages in the TAB. Suppose I have an app running on a tab I go to the next tab...and POOF when I get back....its Gone because it has been reloaded!!!
QuestionUsing this with C#?memberHyperX1 May '06 - 13:07 
Steve,
 
Did you try using this template to generate the links/buttons/iFrame dynamically from the database?
 
Thanks for such a neatly-explained article,
 
HyperX.
Generalchange colors of scrollbar of .aspx page(asp.net)sussfalgun modi15 Oct '05 - 0:04 
hi,
 
i want to change color of scrollbar of my .aspx page (asp.net).so there is specific code for it.
 
Nilesh Gambhava,
Life is NP-Complete so don't use Algorithm but use Heuristics
QuestionIFrame background color?membergogetsome13 Oct '05 - 8:23 
Hello all, I would like to change the Iframe background property? At least that is what I think I need to do. The index page's background property is black and each of the pages loaded in have their background property set to black as well, BUT each time a new page is loaded WHITE flashes until the page being loaded in appears. Any help on how to change this white to black would be great.
 

GeneralParent - child visual referencememberjohn de sousa16 Jul '05 - 13:14 
Hi,
Anyone know how i can maintain the parent/child tab visual reference thru colors or borders?
GeneralHide or Show tabs based on user rolesmembercoriolanx16 Jun '04 - 6:17 
How to implement the hiding or showing of the tabs based on user roles?
For instance, Administrators will see "Administration" tab while others don't.
Thank you much in advance.
GeneralRe: Hide or Show tabs based on user rolessussAnonymous28 Jun '04 - 6:54 
Yeah,
 
I had the same question in mind... just logged on to post it since I had done my role based tabs using Arrays...
 
First define an Array that holds your tab definitions:
 
I.E.
Array(0) = "new Array("Entities|entity.aspx"),"
Array(1) = "new Array("Facilities|Facilities.aspx")"
 
<% if UserType = "admin" Then %>
Tabs = Tabs + Array(0) + Array(1)
<% else %>
Tabs = Tabs + Array(1)
<% end if%>
 
Thats the general idea...
 
Brendan
 

GeneralRe: Hide or Show tabs based on user rolessussAnonymous28 Jun '04 - 7:20 
Oh ofcourse your JavaScript would print out the Tabs variable...
 

GeneralIFRAME Background ColorsussAnonymous18 May '04 - 10:40 

Excellent work on the tabbed interface. I am using it with Coldfusion to build an application. My problem is that I have changed the tabFrame style some to make the iframe's background color the same as the tabs using the ThreeD colors and shadows. I'm not sure if its possible but can the hosted page inherit the IFRAMES colors/properties? In other words it becomes the same color as the iframe so it looks like its part of the tab instead of having the borders.
 
Hope I've explained what I want to accomplsh clearly... Thanks for ne help in advance.
 
Brendan
Generalpassing variables between frames in your given examplememberkishor kumar12 May '04 - 0:05 
Could you please provide an example of passing variables between frames in your given example?
 
Suppose I click on another TAB I want a variable to be passed and retreive it. I dont want to use session object to pass values in your example.
 
thanks in advance
kishor kumar

GeneralPersistent iFramesmemberdmoses9 Feb '04 - 5:26 
Hi Steve,
 
First off, Nice Job! Big Grin | :-D I really like your project. I have always wished IE could have tabbed pages like other browsers. You also have an eye for design.
 
I have a couple recommendations:
 
1) As my subject title suggests it would be great to have persistent pages, so that when you browse one page and then go back to another (tab), it does not loose your page/position.
 
I accomplished this by creating an iFrame inside a div for each page and then inserting all of them into a table ....
 
function iframeOnLoad() {
   var myhtml = '';
   for (x=0; x<num; x++) {
      myhtml += ' <div id="Layer'+x+'" style="position:absolute; width:100%; height:100%; z-index:1"><IFRAME name="iframe'+x+'" src="'+links[x]+'" width="100%" height="100%" frameborder="1"></IFRAME></div>';
   }
   browseLayer.innerHTML = myhtml;
}
 
Now I wouldn’t want such a feature with a slow Internet connection! Each site loads when I open this page. I have a fast connection, so it’s not a problem. A solution to this is to delay the load of each frame until its initial viewing.
Something like: if(iframe0.src =='') ..set iframe0.src="..";
 
2) Another tweak I would suggest, is to not bother rebuilding the whole set of tabs each time one is clicked. In programming sense its kind of a waste. The only thing that changes is the tabs style, so why not just re-assign the style with something like:
 
somename.className='tabOn';
somename.className='tabOff';
 
that simple!

PS: Does anyone know a simple way I can highlight my code above, but still disable HTML tags. If I enable HTML Tags my code breaks they HTML layout. I also don't want to translate all my characters by hand into '&xxx;' type chars! thanks!
GeneralRe: Persistent iFramesmemberSteve Puri9 Feb '04 - 7:38 
Hi dmoses,
 
1. Persistence is a great idea – certainly makes for a faster UI and time efficient navigation for users. About half-way through I remember I wanted to add some sort of persistence/caching of web pages but at the time I was using the tabs for a project where session & cookie data was being manipulated a great deal so I always wanted the user to have an updated view of a particular web page. However, I still think persisting web pages for other projects can be very useful; – I think I could have an option in terms of a global boolean variable which switches persistence on and off.
 
2. Maybe I've overlooked, but are you talking about the tabOnClick function? If so, I don’t rebuild a whole set of tabs each time one is clicked. What I am doing however is looping through each tab (even the one clicked on) and setting its style to .tabOff, and then finally once the loop exits, I set the style of the tab that’s been clicked to .tabOn:
 
var oElement = null;
 
for (var i = 1; i < tabs.length; i++)
{
	oElement = document.getElementById(i);
	oElement.className = "tabOff";
 
	for (var j = 1; j < tabs[i].length; j++)
	{
		var childID = i + "" + j;
 
		oElement = document.getElementById(childID);
		oElement.className = "tabOff";
	}
}	
 
oElement = document.getElementById(ID);
oElement.className = "tabOn";
 
You suggested that all I need to do is:
 
somename.className='tabOn';
somename.className='tabOff';
 
In essence, that is exactly what I'm doing except I have more than one tab to set to .tabOff and the only way of doing that I reckon is by looping through them all. The first for-loop sets the parent tabs to .tabOff, and then the nested for-loop sets the child sub tabs to .tabOff aswell.
 
You’ve got some cool improvements. I like the persistence/caching of web pages thing. If you can, email me your ideas and at some point I’ll try to incorporate them into an updated download. Thanks! Smile | :)

GeneralRe: Persistent iFramesmemberdmoses9 Feb '04 - 9:43 
Steve,
 
2.) Sorry, my mistake. I should take a closer look before I post Wink | ;)
Yeah, my example code was meant to be run in a loop to change styles for all tabs, I was just giving a brief example. For some reason I thought that you did something like "divTabStrip.innerHTML = ..." each time. Confused | :confused:
 
I'm going to try to create that delayed loading code some day, then I will email you what I got.
 
Cheers

Generalonly IE - not goodmemberWinfried Heitmann8 Feb '04 - 23:04 
You did a very good job. The tabs are nicely realised.
 
However, running Mozilla I see nothing at all Cry | :(( It is a bad thing designing web pages only for a certain browser. One should test all projects with the three or four most common browsers as well the old and new versions of them.
GeneralRe: only IE - not goodmemberSteve Puri9 Feb '04 - 6:42 
Unfortunately, I don’t have the means to test with other browsers (is there a third-party app out there that simulates other browsers & versions?). Also, I guess the nature of the project using an IE IFrame makes this only viewable with IE – I think it may be possible to replace the IFrame with a DIV and that may make Mozilla happy.
GeneralRe: only IE - not goodsussPhillip Temple8 Apr '04 - 23:04 
Why not just install the other browsers? Unlike IE, they work on all platforms and take little disc space. Go to http://www.mozilla.org/ and download the win32 version of Firebird. Opera also do a free (ad-driven) version you can install.
 
Phillip.
GeneralRe: only IE - not goodmemberDevKM14 Jan '07 - 23:38 
Why dont you find another place for stupid comments like this?
 
Dev

GeneralRe: only IE - not goodmemberwopper28 Sep '05 - 5:49 
What you just described could take someone a ton of time. I wish all browsers were standard compliant but they are not. So I'm not going to spend 5 hours debugging something or tying to find a compromise on format on the off chance that some user is going to be surfing with a imac using ie 4 or ns 4 etc...

 
Chad
Generalscrollbar colormemberFiend15 Feb '04 - 9:37 
I've tried changing the color on the scrollbar-face-color CSS property for .tabFrame and it doesn't work. Anybody else have this problem? My scrollbar is always grey.
GeneralRe: scrollbar colormemberSteve Puri6 Feb '04 - 3:27 
IE <IFRAME>’s don’t have scrollbars, the scrollbars that are appearing belong to the web page hosted in the <IFRAME>. Change the scrollbar colours of the web page that you wish to host in the <IFRAME>.
 
BODY
{	SCROLLBAR-FACE-COLOR: #fbedbb;
	SCROLLBAR-HIGHLIGHT-COLOR: #ffffff;
	SCROLLBAR-SHADOW-COLOR: #ff9900;
	SCROLLBAR-ARROW-COLOR: #ff9900;
	SCROLLBAR-DARKSHADOW-COLOR: #ffffff;
}
 

GeneralTables are dead, long live CSS!!!memberRui Dias Lopes2 Feb '04 - 10:23 
Check it out why, at:
 
http://glish.com/css/
http://www.alistapart.com/articles/practicalcss/
http://www.bluerobot.com/web/layouts/
 
I think it will help Smile | :)
 
Rui
 
is this a sig?
GeneralRandom filter issue/bugmemberSteve Puri29 Jan '04 - 9:09 
Somewhere on or near line 203 I’m using:
 
Math.round((Math.random()*DXImageTransformLibrary.length-1)+0)
 
This is supposed to generate a random number between the limits of the DXImageTransformLibrary array so that it can be used to pick out a random filter effect from it. However, it turns out that the random number sometimes doesn’t work or overshoots the array; a segmentation fault. You’ll know when this happens when all the tabs including the child ones appear altogether. A simple fix would be to select a particular filter rather than use a random one.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 23 Sep 2005
Article Copyright 2004 by Steve Puri
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid