Preamble:
It's probably time for a re-introduction of the concept here and what I'm all about. What I'm documenting here is the Microsoft specific implementation of ECMAScript/Javascript, and that's the title: JScript. This stuff won't work on any browser except IE. Right now IE 6.0 is the best IE there is. Now that might be akin to having the nicest trailer in Brighton (a local suburb of questionable repute), but that's just the way it is. I spend all day writing applications that use the IE browser as their execution container and IIS as the deployment server. Some people call that web development, some call it web applications, I prefer to call them web enabled/web deployed applications. Some really smart people call these Zero Footprint Apps...and there are lot's of good names for them. Honestly, I'm not a very good web page or web site developer, but I do enjoy messing around with that from time to time. But if you hate IE (and perhaps you have a good reason to, perhaps...) and politically/religiously/personally refuse to develop for anything Microsoft, then this might not be your cup of tea.....
Introduction:
Back in the old days, before the turn of the century (remember Y2K?), we had IE 4.something and it was a fine web browser. And in 1999, the intranet was a big, new, fresh concept and people were turning on to the whole internal applications delivered via the browser. Microsoft introduced IE 5 with great fanfare and introduced (among other things) the notion of Behaviors. At the time, they introduced 2 different types, Default and DHTML. With IE 5.5, a couple of years later, they introduced a third, Element Behaviors and renamed DHTML to Attached...got it? I'm going to spend the next few weeks unpacking a couple of nice features of all three, because I use them everywhere.... This week we'll look at IE's Default Behaviors. For the most complete reference on all things Behaviors, the best (and virtually only) resource out there is MSDN. If you're a book person, I've found only two books (there might be more, let me know..) that cover anything regarding IE Behaviors. Both are out of print and I picked them up used for a couple of bucks on eBay. They are:
Windows Web Scripting Developer's Guide by Dan Heflin & Todd Ney
or
IE5 Dynamic HTML Programmer's Reference by Brian Francis, Alex Homer & Chris Ullman
First, what is a behavior? According to Microsoft: "behaviors are components that encapsulate specific functionality or behavior on a page. When applied to a standard HTML element on a page, a behavior enhances that element's default behavior". Ummm...okay, so what's a behavior? I define a behavior as a way to improve the behavior of something on an HTML page. Or even better, Behaviors are simply Dynamic HTML components that enhance an HTML elements behavior. I've also heard it described "behaviors have been available as a means for Web developers to extend the IE object model". All this means is that using behaviors you can do things easier or maybe even things that you never dreamed possible.....lofty statement indeed...
The first way to improve the behavior of things it to provide a list of potential improvements and implement them. Microsoft did this back in '99 and provided us with Default Behaviors, which are not to be confused with default behaviors. Default behaviors (all lower case) are the behaviors that something has natively, or by default. Default Behaviors are Behaviors (improvements) that the browser vendor (Microsoft) provides to you free of charge, no assembly required (by Default). These Behaviors are self-contained, so how they work is unknown. Honestly, in the true spirit of hiding their implementation, you don't want to know. Next week we'll work on Behaviors that you will create the implementation, so that's a teaser as to the difference between the types. There is a whole bunch of Behaviors and I'm actually only going to talk about one (cherry picking is my claim to fame). The complete list can be found at MSDN, but here's the list in no certain order. This list might be incomplete, but hey, I'm far from perfect: anchor, anim, animation, audio, clientCaps, download, homePage, httpFolder, img, media, par, saveFavorite, saveHistory, saveSnapShot, seq, time, userData and video.
Full disclosure time: other than messing around with these to see what their use is (something akin to "Hello World") I've only used two: download and userData. I'm going to explore userData in this article. Maybe I can somehow convince my buddy, Brian R. James, to scribble something for AlphaFilter regarding his amazing use of the download Behavior in popup menus, then we'd have both covered. But for now, if one of the other Default Behaviors seem interesting, hit MSDN or buy a book. That's just the way it is.
Microsoft or somebody defines the userData behavior as "Persists data in the user data area". OK, sounds like a cookie or something. If that's what you're thinkin', then you'd be right. The browser will store an XML file to the file system and you can access this thing just like a cookie. Why use this over a cookie? The best reason is size. UserData can store up to 64K of data per page and 640K per domain. Does anybody know the limits on a cookie? You can only store 20 crumbs (name/value pairs) in a cookie. Now I'm not a cookie-guy (but I do like Thin Mints...), but an XML file with 100 name/value pairs is only about 2K. So a userData maxes out at merely 3000 or so name/value pairs. That ought to be enough! Plus, it's XML and I think XML is cool. Okay, that's not a great reason, but seriously, XML can store hierarchical information so simply, it just makes sense. I realize I've brought this up before and I'll continue to pound on it: understanding and appreciation of XML and its relatives is a must have to thrive on this planet. Here's the Bat Phone straight to the userData section of MSDN .
Let's get one going. You first start by creating a style in css that references the userData behavior either like
.UserData{behavior:url(#default#userData);}or directly on the element using a style attribute
<div id="IDiv" style="height:100%;margin:1px 2px 0px 1px;behavior:url
(#default#userData);" tabindex="-1">
That's it. You now have userData on your page. My example page, Example 1 has one all ready for you to examine.
Certainly you want to do something with it. Well, it's not much harder than that first section. Now we jump into JScript. We'll need a save function and some sort of retrieve function. The save is very simple.
function Save()
{
var what = theDoc.getElementById("txtWhat").value;
var oUD = theDoc.getElementById("theBody");
oUD.setAttribute("theValue",what);
oUD.save("Episode4v1");
}
What this will do is take the contents of the txtWhat textbox and save it to this page's userData, affectionately labeled Episode4v1. It will save it as the value of the attribute node "theValue". UserData operates similar to the Windows Registry, using name/value pairs. UserData objects have a .save method, with the parameter being the name of the userData. So if you examine the XML (hit ShowMe the XML), you'll see this XML:
<ROOTSTUB theValue="whatever" />
Now if you go fishing in your filesystem, like in C:\Documents and Settings\mdshaffer\Application Data\Microsoft\Internet Explorer\UserData (I doubt you have mdshaffer as a user), you'll see one or more cryptically labeled subdirectories. On my machine, I've got a couple, the one I'm interested in is R0IZMCK1, but I'm betting yours will be labeled differently. Inside it, you should see a file Episode4v1[1].xml. You might have a different index, but there's your userData, sitting in the Windows filesystem, put there by a browser! And all you did was set an attribute and .save it.
Now to read the data, we just hit the userData object, theBody, then use the .load. What we have now is a fully compliant MSXML DOM node, so we slam a .getAttribute on him and voila, you can see your new value.
function ShowMe()
{
var oUD = theBody;
oUD.load("Episode4v1");
alert(oUD.getAttribute("theValue"));
}
We can view the XML just as easy. Here's my function
function ShowTheXML()
{
var oUD = theBody;
oUD.load("Episode4v1");
alert(oUD.xmlDocument.xml);
}
As XML goes it's very simplistic. But it's very effective. For every attribute, you have a value. Again, not earth-shattering, but it seems like when you use cookies in a moderately complex way (like more than one value), you end up building a whole strategy to delimit the string and stuff. Now with userData, all of that plumbing is done for you. And how do you do hierarchy with a regular cookie? I'm sure some one very clever has done it, here's how I do it with userData.
The greatest deficiency in userData is that the XML is locked in to the <ROOTSTUB attribute1="value1" attribute2="value2" ... />. But I want complicated XML down there! If only Microsoft would let me define the XML....oh to dream. But, there's a way...it's a bit of a hack (by my own admission) but it works and works well. I store the entire "blob" of XML in a single attribute. So it effectively looks like:
<ROOTSTUB theValue="<top><row><field name="txtFirstName"
value="Mike" /><field name="txtLastName"
value="Shaffer" /></row></top>"/>
But wait! XML that looks like that will blow the userData up, can't have <> and quotes, so you need to escape all those nasty characters. You cobble together an EncodeXML() and DecodeXML() function or steal mine (that were stolen from another project, thanks Brian!). You run your XML through this and you can then save the XML into the userData. When you extract it out, you run it through decode, push it into an XML DOM object and do with it what you will. See Example 2.
There is a lot going on in this little page and I won't cover everything. Pull it apart and look at it. A disclaimer: I build nifty little (and sometimes not so little) interfaces all day. This page(and all my example pages) is intended to demonstrate the functionality not show you how skilled I may or may not be at interface design. So I know you can't edit the pet records once they're in there and stuff like that....I know.
The first thing I do on this page is load my global XML object with XML, either from userData or from my base skeleton island. Doing this ensures that I don't leave the first time user hanging, but I'm certain that all subsequent trips to this page will be talking from userData. You can save your name and color, or add an infinite number of pets and colors hierarchically under you. This is a trivial example, but one with a purpose. Not to put to fine a point on it, but imagine instead of your name and pets, you were storing colors and meta information about a series of tabs on an interface. Imagine an application that has a series of tabs, with each tab having a series of tabs on it and so on. If you were successful in imagining this, you just pictured "the Monster". On the Monster, I store individual tab information, including color, keyboard shortcuts, labels, default information per user on the client machine. I have a snappy little color picking utility (hat tip: Houser) that changes the colors for each tab. I'll be dragging that little project out in a couple of weeks.... You don't like the first tab being orange? Change it to gray, it doesn't matter to me. Skinning an application is hardly new, but by using userData to store XML information that is easily and rapidly accessed and modified, I'm able to allow the user to colorize the whole application custom to their desires, effectively placing no limits on them instead of a standard group of "skins".
There are couple of additional topics on userData that I want to briefly cover. The first is expiration. Like any good "cookie", these guys can be set to expire. And it's real simple: you use the .expires property on the userData object. Or in our examples, oUD.expires = SomeDate. SomeDate needs to be UTC format (easy with the JScript Date object) and there you go. IE will check if the expire date is present and destroy the userData, just like a good persisted data store should. The other item is a warning. userData is stored in plain XML on the file system. If you'd prefer to secure this information, userData is not a good choice. While it is buried deep in the bowels of Windows, remember the basics of computer security: Security by Obscurity isn't Security. Meaning: this stuff isn't safe from prying eyes. Putting Password="HiBob" or AccountNumber="454512341234" would be a bad idea. I've used userData to store encrypted information, then shipped it up to an ASP page for decrypt and authentication, that can be done. But don't be relaxed here. You've been warned....
An Aside:
If you are really scouring my source on this page (on line 80), you'll see a reference to a bug that I ran into in MSXML 3.0. Firstly, I'm not a big "it's a bug, it's a bug" kind of guy. I used to work with a guy that always found bugs in commercial software (Lotus Notes was his claim to fame) or at least that's what he told the users. What it usually turned out to be was that he didn't know what he was doing and had mistakenly misconfigured or misused something. So that being said, if I'm wrong, so be it. The issue appears only if you are using the MSXML 3.0 parser. MSXML 4? No problemo. But it appears that when you modify the XMLDOM object using .cloneNode and then .appendChild, something happens to your XML. If you perform a .selectNodes on the newly created nodes, the nodes are only usable if you access their .length property first. In my case, if you comment out the var Raid line, the Node Set returned only has two nodes. Put the Raid line back in, 3 nodes! Doesn't matter which two nodes, but it's only the first two that are visible. It's an amazingly twisted story as to how I determined this, but suffice it to say, that if you instantiate the MSXML 4 object, var oXML = new ActiveXObject("MSXML2.DOMDocument.4.0"); you have no problem. Yet another good reason to use MSXML 4 I suppose. You'll not run into this problem if you're not doing .cloneNode and then .appendChild as far as I can tell. If anyone has any input on this, I'd love to hear it.
The Finish Line:
That's about all I want to talk about userData for now. (That's all?) userData will pop back up in a future episode but only after I wax poetically about DHTML or Attached Behaviors, as they are called now. That's the plan for next week. Hopefully work will cooperate and I can get it done......
If anyone has any comments or questions, email me at ms_@_mybluecoat.com (remove the underlines first...) or use the nifty comment deal that Foo has added to AlphaFilter...and keep those cards and letters coming.