XML Literals in JavaScript?

kick it on DotNetKicks.com submit to reddit

If you're a web developer and haven't heard of E4X (EMCAScript for XML), it's a shame, but you're likely in the majority. From the Mozilla Developer Center link above:

ECMAScript for XML (E4X) is a programming language extension that adds native XML support to JavaScript. It does this by providing access to the XML document in a form that feels natural for ECMAScript programmers. The goal is to provide an alternative, simpler syntax for accessing XML documents than via DOM interfaces.

Translation? E4X allows you create variables out of well-formed inline XML:

var thumbXHTML = 
    <div class="thumbContainer">
        <div class="thumbImg">
            <img src="summer.png" alt="Summer" />
        </div>
        <div class="thumbCaption">Summer</div>
    </div>;

While the specification (ECMA-357)—standardized and published in 2005—makes the JavaScript example above standardized, chances are that your browser blows up after evaluting it. And that's a shame.

XHTML and Templating with E4X

What's so great about E4X and in-lining XML or XHTML in JavaScript that makes the missing support such a disappointment? The ability to create JavaScript templates.

Not only can you inline XML with E4X, but you can insert variables into the XML literals:

// Thumbnail object
function Thumbnail(thumb) {
    return <div>
               <div class="thumbImg">
                   <img src="{thumb.imgsrc}" alt="{thumb.caption}" />
               </div>
               <div class="thumbCaption">{thumb.caption}</div>
           </div>;
}

Pass in a thumb object and out comes the XHTML markup that represents a thumb after calling toXMLString():

thumbElement.innerHTML = new Thumbnail({
    imgsrc: 'summer.png',
    caption: 'Mmm, summertime.'
}).toXMLString();

In this way, we can generate the markup for multiple JavaScript objects that represent thumbnails. For example, one could post an Ajax request to a service, fetching an array of server-side Thumbnail objects serialized to JSON, and update the UI by templatizing the array objects using Thumbnail():

ImagePage = {
    init: function() {
        ajax(..., function(thumbnails) {
            ImagePage.displayThumbnails(thumbnails);
        });
    }
 
    displayThumbnails: function(thumbnails) {
        var innerHTML = [];
 
        for (thumb in thumbnails) {
            innerHTML.push(new Thumbnail(thumb).toXMLString());
        }
 
        $('thumbContainer').innerHTML = innerHTML.join('');
    }
}

XML literals can also be manipulated with standard DOM node methods like appendChild(). Another way to create templates, this time with forms as an example, could like look:

// This is the template
function Table(fields) {
    var xmlTable = <table></table>;
 
    fields.forEach(function(field) {
        xmlTable.appendChild( 
            <tr>
                <td class="cellLabel">{field.label}</td>
                <td class="cellValue"><input type="{field.type}" name="{field.name}"/></td>
            </tr>
        );
    });
 
    // Convert XML table object into DOM nodes
    var shell = document.createElement('div');
    shell.innerHTML = xmlTable.toXMLString();
    return shell.childNodes[0].cloneNode(true);
}
 
// Using the template...
 
var addressTable = new Table([
    { label: 'Name, First:',   type: 'textbox', name: 'firstName' },
    { label: 'Name:, Last',    type: 'textbox', name: 'lastName'  },
    { label: 'Street Address', type: 'textbox', name: 'address'   }
]);
 
document.getElementById('adressTable').appendChild(addressTable);

There's a project that uses E4X for templating called seethrough that employs something similar to the rudimentary implementations of E4X templating I've shown.

There's tons of other cool features like the selectors in E4X and an XMLList, but being able to use XML literals as templates to dynamically create new DOM elements definitely stands out in my mind as its biggest strength. Apparently the great utility of E4X for templating has occurred to Mozilla too.

Support for E4X

Disappointingly, the only two organizations who have backed E4X by implementing the specification are Mozilla and Adobe: Mozilla has implemented E4X in SpiderMonkey and Rhino—their two JavaScript engines, and Adobe has implemented E4X in ActionScript for Flex, Flash, etc. All I can find on E4X for V8—Chrome's JS engine—are scattered posts in forums and usergroups asking about or requesting E4X support. (Visit Wikipedia's entry for a complete list of implementations.)

For web developers whose users may browse using IE or WebKit, that means E4X doesn't exist. There's always lag between committee development and market adoption, but the fact that it was standardized over four years ago does not bode well. Though I wish I were, I don't think I'd be mistaken by calling E4X stillborn offspring of Web 2.0. With XML being replaced more and more by JSON for web services and data interchange, the chances of E4X reemerging are even slimmer.

I showed just a couple ideas of how E4X could be used for templating—I'm sure that if E4X were ubiquitous, we'd see a lot more functional and elaborate templating systems. XML literals as primitives in JavaScript is quite the compelling and exciting idea, and I think I'm not the only one who dreams of writing JS with them.

Asana, a startup "building a Collaborative Information Manager that we believe will make it radically easier for groups of people to get work done," must think that the ability to inline XML is good idea too. The decorated fellows of Asana are developing LunaScript, an in-house language that makes XML literals first-class primitives:

  return <table>
    <tr><td>
      messages.map(renderMessage)
    </td></tr>
    <tr><td>
      <img src=(session.user.small_pic_url) />
      <div>
        <input data=session.user.name /> <b>' (your nickname)'</b>
        <form onsubmit=postMessage>
          <input data=session.new_comment hasFocus=true />
        </form>
      </div>
    </td></tr>
  </table>;

At the moment I'm trying to figure out if any work is being done around implementing XML literals in JavaScript besides with E4X, but no success so far. For now, I guess we'll just have to live with alternative JS templating techniques (some of which are very good and similar to the example E4X techniques I've shown).

Only time will tell whether XML literals will ever appear in JavaScript. If the success of E4X tells us anything though, it may be never.

submit to reddit kick it on DotNetKicks.com

Tags:

JavaScript & ASP.NET: Executing on an Event onclick

kick it on DotNetKicks.com

I've been doing a lot of front-end debugging with ASP.NET and JavaScript lately, and one thing has become very apparent: there's way too many ways to specify that something happens when you click on an link or other element.

First, you could specify what happens when an element is clicked using the classic but naive onclick HTML attribute. This would be a bad idea—you'd be mixing presentation (HTML) and behavior (JavaScript). The two are different modalities, and should not be mixed.

<a href="#" onclick="doSomething(); return false;">A</a>

If the element is created by an ASP.NET control, there's the OnClientClick attribute instead to differentiate from OnClick—which is a server-side, codebehind, method:

<asp:Button OnClientClick="doSomething();" />

This simply generates:

<input ... onclick="test();" />

Again—bad idea. Adding on OnClientClick as shown, is equivalent to adding on onclick attribute, and ill-advisedly couples presentation and behavior.

You're best served by keeping behavior in the behavior layer—in JavaScript. If you're using a framework, forget about onclick; instead, add your frameworks equivalent of events:

<script type="text/javascript">
    // MooTools example
    window.addEvent("domready", function() {
        $("element_name").addEvent("click", function() {
            alert("do something!");
        });
    });
 
    // Or use $(element).click() with jQuery 
</script>

Or, JavaScript without a framework:

<script type="text/javascript">
    document.getElementById("my_element_id").onclick = doSomething();
</script>

Be consistent. Think of the people debugging your mess before you go around being inconsistent with how you wire up your events:

  • If you're using a JavaScript framework, select your element then add an Event, and always place the <script> containing this at the end of your page. This will make it easy to find the click events for all your elements:
        // jQuery
        <script type="text/javascript">
            $(document).ready(function() {       
                $("#element_name").click(function() {
                    alert("do something!");
                });
            });
        </script>

  • If you're not using a JavaScript framework, select your element then specify its onclick, and always place the <script> at the end of your page. This will make it easy to find the click events for all your elements:
        <script type="text/javascript">
            // Not using a framework? Better hope the DOM is ready!
            document.getElementById("my_element_id").onclick = doSomething();
        </script>

  • If you're looking to make enemies, specify the event by using an inline onclick attribute in the tag. This will make it more difficult to find the click events for your elements:
        <a onclick="doSomething(); return false;">Clickity-click</a>

  • If you're a real pain, use the codebehind to access the element and attach an onclick via one of the dreadful options shown below. This will make it somewhat painful to find the click events for all your elements.
    /* A) */ this.HyperLink1.Attributes["onclick"] = "doSomething();return false";
    /* B) */ this.RegisterClientScriptBlock(...);
    /* C) */ this.RegisterStartupScript(...);

kick it on DotNetKicks.com

Tags:

.NET, Philosophically

kick it on DotNetKicks.com

Question: Who deserves a kick in the nuts more—the Microsoft BCL developer who brilliantly decided to name his Uri method TryCreate, or the "Sr. Engineer" on your team who neglected to ever use Intellisense and instead decided to drop this steaming gem of useless crap into your already-herniating codebase? (Snippet from a friend's workplace.)

public static class UriHelper
{
    public static bool TryParse(string s, out Uri uri)
    {
        try
        {
            uri = new Uri(s);
            return true;
        }
        catch
        {
            uri = null;
            return false;
        }
    }
}

Really, can you blame the guy? Who knows—maybe he knows better and is actually sticking a big FU to the MS man for deviating from the TryParse idiom. We can always hope.

Speaking of, .NET 4.0 is getting Guid.TryParse() and Enum.TryParse<T>()—a whole five years after someone in the community thought it was a no-brainer. About time. Really. Now, if we could just get Uri.TryCreate() renamed. :)

kick it on DotNetKicks.com

Tags:

Config files for DLLs

kick it on DotNetKicks.com

Every once in a while I'll write a DLL and wish I could easily externally configure settings for it like you one do with web.config files and ConfigurationManager in ASP.NET applications or with .exe.config files. The idea is to avoid hardcoding settings in the DLL and instead put them in a XML file where they can later be changed as needed.

I finally got around to doing something about not having this same functionality for class libraries. Now, with AssemblyConfig, you can access settings from a .config file for your DLLs just like you can with ASP.NET.

Config File Format

AssemblyConfig looks for config files that match the name of the assembly. If your assembly is Image.dll, create a config file called Image.dll.config. Use the same XML structure that you'd use with an ASP.NET application or .exe.configs:

<configuration>
	<appSettings>
		<add key="setting" vaue="settingValue" />
	</appSettings>
</configuration>

Retrieving Settings

Accessing settings is just as easy as with ConfigurationManager. You can either access settings by key:

string value = AssemblyConfig.Setting("key");

Or you can access settings via XPaths:

string copyright = AssemblyConfig.Setting("/configuration/site/copyright");

AssemblyConfig even lets you use generics to type the return value of a setting if the type you specify implements IConvertible*:

int cacheTimeout = AssemblyConfig.Setting<int>("CacheTimeOutInSecs");
DateTime copyStart = AssemblyConfig.Setting<DateTime>("/configuration/site/copyrightStart");

*Types that implement IConvertible are: Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char, and String.

One last thing AssemblyConfig lets you do is access the entire config file using LINQ by returning the config's root XElement. This can be especially useful if you include extra data in your config files besides just <add ...> elements. You could create a config file like this:

<configuration>
	<appSettings>
		<add key="setting" vaue="settingValue" />
	</appSettings>
	<people>
		<person>
			<name...
		</person>
	</people>
</configuration>

The people data can then be accessed using the Root property of AssemblyConfig:

var people = AssemblyConfig.Root.Descendants("person");

Implementing AssemblyConfig

To use AssemblyConfig, add a reference to AssemblyConfig.dll in your project and call AssemblyConfig.Setting(), AssemblyConfig.Setting<T>(), or AssemblyConfig.Root as demonstrated from within your DLL class library.

Be on the lookout for a few exceptions when using AssemblyConfig. Instead of silently failing, like ConfigurationManager does by returning null if a setting is missing, AssemblyConfig will throw a SettingNotFoundException. This is preferable because a missing setting should be an unrecoverable scenario. A few other exceptions (all, including SettingNotFoundException, deriving from AssemblyConfigException) to look out for are:

  • DuplicateSettingException—if there are two settings with the same key or more than one element at a specified XPath
  • ConfigFileMissingException—if an attempt is made to retrieve a setting and there is no config file
  • ConfigFileMalformedException—if the config file is not a valid XML document or is missing the <configuration> root

Pretty straight forward, no? It's a small and simple enough tool that I haven't created an open source project out of it, but if you have any good suggestions, I'd love to implement them and update the post here to reflect any changes.

kick it on DotNetKicks.com

Tags:

Short GUID

kick it on DotNetKicks.com

If you've used Guid.NewGuid().ToString(), you've probably seen a GUID like this:

AAD5E2E7-AD78-487a-9B37-458B9F1EE897

That may suite your purposes, but if you're case-sensitive, you can actually get a shortened version of the same GUID. Here's how:

string shortGuid = Convert.ToBase64String(Guid.NewGuid().ToByteArray())
            .Substring(0, 22)
            .Replace("/", "_")
            .Replace("+", "-");

The output:

b14I850fIE6cuyv7VnP6WA

Or, as an extension method:

public static class GuidExtensions
{
    /// <summary>Get a 22-character, case-sensitive GUID as a string.</summary>
    public static string ToShortString(this Guid guid)
    {
        return Convert.ToBase64String(guid.ToByteArray())
            .Substring(0, 22)
            .Replace("/", "_")
            .Replace("+", "-");
    }
}

Used like:

string shortGuid = Guid.NewGuid().ToShortString();

That's 32 characters vs. 22. Nothing magical happens here: Substring(0, 22) cuts off two trailing == characters that always appear, then the GUID is made URI and filename friendly by replacing / and +.

Hope this helps if you're looking for a shorter global identifier!

Update: 11/16/09

zm asked how this could be converted back to a valid Guid. If you'd like to do that, it's best to create a class out of this "short GUID" idea. Using the ShortGuid class at the end of this post, you can do that.

With ShortGuid, to get a short, 22-character GUID:

string shortGuid = new ShortGuid(Guid.NewGuid());

To convert that short GUID back into a Guid:

Guid regularGuid = ShortGuid.Parse(shortGuid);

When you compare that they are equal, it's true:

Guid regularGuid = Guid.NewGuid();
string shortGuid = new ShortGuid(regularGuid);
 
// Writes "True"
Console.WriteLine(regularGuid == ShortGuid.Parse(shortGuid));

Here's the ShortGuid class that makes this magic happen:

public class ShortGuid
{
    private readonly Guid guid;
    private readonly string value;
 
    /// <summary>Create a 22-character case-sensitive short GUID.</summary>
    public ShortGuid(Guid guid)
    {
        if (guid == null)
        {
            throw new ArgumentNullException("guid");
        }
 
        this.guid = guid;
        this.value = Convert.ToBase64String(guid.ToByteArray())
            .Substring(0, 22)
            .Replace("/", "_")
            .Replace("+", "-");
    }
 
    /// <summary>Get the short GUID as a string.</summary>
    public override string ToString()
    {
        return this.value;
    }
 
    /// <summary>Get the Guid object from which the short GUID was created.</summary>
    public Guid ToGuid()
    {
        return this.guid;
    }
 
    /// <summary>Get a short GUID as a Guid object.</summary>
    /// <exception cref="System.ArgumentNullException"></exception>
    /// <exception cref="System.FormatException"></exception>
    public static ShortGuid Parse(string shortGuid)
    {
        if (shortGuid == null)
        {
            throw new ArgumentNullException("shortGuid");
        }
        else if (shortGuid.Length != 22)
        {
            throw new FormatException("Input string was not in a correct format.");
        }
 
        return new ShortGuid(new Guid(Convert.FromBase64String
            (shortGuid.Replace("_", "/").Replace("-", "+") + "==")));
    }
 
    public static implicit operator String(ShortGuid guid)
    {
        return guid.ToString();
    }
 
    public static implicit operator Guid(ShortGuid shortGuid)
    {
        return shortGuid.guid;
    }
}

kick it on DotNetKicks.com

Expiring Cache in C#

kick it on DotNetKicks.com

Ever need a simple cache that automatically expires items, removing them after a given time has elapsed? Well now you've got it: ExpiringValueCache and ExpiringObjectCache. ExpiringValueCache is used to store value types. ExpiringObjectCache is used to store reference types.

A background thread continuously runs, checking for expired items in a generic Dictionary. The size of the collection does not affect the productivity of this background expiration process. The scalability of the caches is the same as that of a generic Dictionary of the items that you're storing.

First, to create one of these expiring caches, pass in a TimeSpan for which items should live before being automatically removed. All objects in the cache use the same TimeSpan as thier lifespan. Here are caches with one-hour expirations:

var peopleCache = new ExpiringObjectCache(new TimeSpan(1, 0, 0));
var idCache = new ExpiringValueCache(new TimeSpan(1, 0, 0));

Next, to add an item to the cache, call its Add method. The first argument is the key for the item, and the second argument is the item.

peopleCache.Add("chris", people[0]);
idCache.Add("chris", people[0].Id);

To check for the existence of an item or retrieve it, use the cache collection's indexer. For ExpiringObjectCache, the object will be returned if the item exists; null if it's not or no longer (i.e. it expired) in the cache:

var chris = peopleCache["chris"];
 
if (chris == null)
{
    // Not in cache. Expired?
}
else
{
    // In cache, do something...
}

Use ExpiringValueCache the same way—it also returns null if the item doesn't exist. It does this by wrapping the result in a System.Nullable:

var chrisId = idCache["chris"];
 
if (chrisId == null)
{
    // Not in cache. Expired?
}
else
{
    var id = chrisId.Value;
}

Lastly, since the caches have a background thread expiring stale items, remember to Dispose() of the cache when you're done with it. The thread is a background thread, so it won't prevent the process from terminating, but you should always cleanly dispose of your resources.

That's it. Suitably simple, I hope.

kick it on DotNetKicks.com

Recursively processings XML elements in C#

Recursion can be tricky. But when you've nailed it, it's damn sexy. Below is a basic outline for recursively processing an XML document for all its child elements, and keeping track of what depth the element is at (how many ancestors, or parents, are above it):

void Process(XElement element, int depth)
{
	// For simplicity, argument validation not performed
 
	if (!element.HasElements)
	{
		// currently at child element with no descendants
	}
	else
	{
 
		// currently at a parent element with children
 
		depth++;
 
		foreach (var child in element.Elements())
		{
			Process(child, depth);
		}
 
		// currently leaving a parent
 
		depth--;
	}
}

To begin processing a document, pass in the root element and a depth of 0:

Process(XDocument.Load(@"C:\test.xml").Root, 0);

Here's an example of recursively displaying an XML document in a console program:

using System;
using System.Linq;
using System.Xml.Linq;
 
class Program
{
    static void Main(string[] args)
    {
        XDocument.Load(@"C:\test.xml").Root.RecursivelyProcess
        (
            Program.ProcessChild,
            Program.ProcessParentOpen,
            Program.ProcessParentClose
        );
 
        Console.Read();
    }
 
    static void ProcessChild(XElement child, int depth)
    {
        Console.WriteLine
        (
            string.Format
            (
                "{0}<{1}>{2}</{1}>",
                "".PadLeft(depth, '\t'), // {0}
                child.Name.LocalName,    // {1}
                child.Value			     // {2}
            )
        );
    }
 
    static void ProcessParentOpen(XElement parent, int depth)
    {
        Console.WriteLine("".PadLeft(depth, '\t') + "<" + parent.Name.LocalName + ">");
    }
 
    static void ProcessParentClose(XElement parent, int depth)
    {
        Console.WriteLine("".PadLeft(depth, '\t') + "</" + parent.Name.LocalName + ">");
    }
}
 
/// <summary>
/// Extension methods for the .NET 3.5 System.Xml.Linq namespace
/// </summary>
public static class XExtensions
{
    /// <summary>
    /// Recursively perform operations on a XML element.
    /// </summary>
    /// <param name="element"></param>
    /// <param name="childAction">
    /// What to do when an element with no children is reached.
    /// The XElement is the child element, the int is the depth.
    /// To do nothing, pass null.
    /// </param>
    /// <param name="parentOpenAction">
    /// What to do when an element with children is reached.
    /// The XElement is the parent element, the int is the depth.
    /// To do nothing, pass null.
    /// </param>
    /// <param name="parentCloseAction">
    /// What to do when finished processing element with children.
    /// The XElement is the parent element, the int is the depth.
    /// To do nothing, pass null.
    /// </param>
    public static void RecursivelyProcess
    (
        this XElement element,
        Action<XElement, int> childAction,
        Action<XElement, int> parentOpenAction,
        Action<XElement, int> parentCloseAction
    ){
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }
 
        element.RecursivelyProcess
        (
            0,
            childAction,
            parentOpenAction,
            parentCloseAction
        );
    }
 
    private static void RecursivelyProcess
    (
        this XElement element,
        int depth,
        Action<XElement, int> childAction,
        Action<XElement, int> parentOpenAction,
        Action<XElement, int> parentCloseAction
    ){
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }
 
        if (!element.HasElements)
        {
            // Reached the deepest child
 
            if (childAction != null)
            {
                childAction(element, depth);
            }
        }
        else
        {
            // element has children
 
            if (parentOpenAction != null)
            {
                parentOpenAction(element, depth);
            }
 
            depth++;
 
            foreach (XElement child in element.Elements())
            {
                child.RecursivelyProcess
                (
                    depth,
                    childAction,
                    parentOpenAction,
                    parentCloseAction
                );
            }
 
            depth--;
 
            if (parentCloseAction != null)
            {
                parentCloseAction(element, depth);
            }
        }
    }
}

Get the XPath to an XML Element

Problem

Given an XElement, what is its XPath?

Preface

Many XPaths can be used that will select an XML element. To select one and only one element, indexes need to be used. For example, given the following XML data:

<people>
	<person>
		<name>
			<first>Chris</first>
		</name>
	</person>
	<person>
			...
</people>

The XPath "/people/person" will select all the "person" elements that are children to "people." To refer to the first "person" element, its index needs to be used in the XPath.

Like an array, an [index] can be used to refer to a specific node. In XPath, indexes begin at 1, not 0. To select the first "person" element, then, the XPath "/people/person[1]" must be used.

I think of this as an "absolute" XPath, because it points directly to one specific element, and is not relative. Therefore, when I ask, "given an element, what is its XPath?", I am referring to its absolute XPath—an XPath expression that will always return a specific element and its children, if it exists.

Solution

The following code snippet uses extension methods to get an absolute XPath to an XElement. Call the AbsoluteXPath extension on any given XElement.

using System;
using System.Linq.Xml;
 
/// <summary>Extension methods for the .NET 3.5 System.Xml.Linq namespace</summary> 
public static class XExtensions
{
    /// <summary> 
    /// Get the absolute XPath to a given XElement
    /// (e.g. "/people/person[6]/name[1]/last[1]").
    /// </summary> 
    /// <param name="element"> 
    /// The element to get the index of.
    /// </param> 
    public static string AbsoluteXPath(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }
 
        Func<XElement, string> relativeXPath = e =>
        {
            int index = e.IndexPosition();
            string name = e.Name.LocalName;
 
            // If the element is the root, no index is required
 
            return (index == -1) ? "/" + name : string.Format
            (
                "/{0}[{1}]",
                name, 
                index.ToString()
            );
        };
 
        var ancestors = from e in element.Ancestors()
                        select relativeXPath(e);
 
        return string.Concat(ancestors.Reverse().ToArray()) + relativeXPath(element);
    }
 
    /// <summary> 
    /// Get the index of the given XElement relative to its
    /// siblings with identical names. If the given element is
    /// the root, -1 is returned.
    /// </summary> 
    /// <param name="element"> 
    /// The element to get the index of.
    /// </param> 
    public static int IndexPosition(this XElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }
 
        if (element.Parent == null)
        {
            return -1;
        }
 
        int i = 1; // Indexes for nodes start at 1, not 0
 
        foreach (var sibling in element.Parent.Elements(element.Name))
        {
            if (sibling == element)
            {
                return i;
            }
 
            i++;
        }
 
        throw new InvalidOperationException("element has been removed from its parent.");
    }
}

JavaScript Date Ago

kick it on DotNetKicks.com

In case you hadn't noticed, one of the hip things to do with user-visible timestamps these days is to format them as a how long ago the time was. Instead of seeing a date and time like "08/02/2009 2:41 PM PST," these so-called passed/relative/pretty/friendly dates appear like "2 hours ago" or "1 week ago."

Being a hip person myself (hah), I've written a few bytes of code to get these type of dates. One is JavaScript (here), and another is a C# class.

Examples

dateAgo() can take either JavaScript Date object, the number of milliseconds that have passed since 01/01/1970 at midnight (which is a unix time in ms), or a date string to parse.

Call Output
dateAgo("05/15/1983");
dateAgo(new Date().setMilliseconds(0));
dateAgo("Tue Nov 03 1992 19:16:10 GMT-0800");
dateAgo(1249524995962);

When the time ago is less than a second, "X ms ago" is be displayed; when the time is less than a minute, "X.XX secs ago" is displayed; anything longer is rounded to the nearest minutes, hours, days, weeks, months, or years.


kick it on DotNetKicks.com

Tags: , ,

Fade background color in JavaScript


Background

A project I've been working on recently is a light-weight AJAX messageboard that looks a lot like Reddit's or Hacker News' comments pages. The spiffy part though is that there's no refresh needed to see new replies since the page has loaded—new posts aysnchronously appear in the thread as users post them.

To make it obvious when a reply is posted, I planned to have the background of the reply be colored so it stands out, then slowly fade to transparent. jQuery's UI core can accomplish this via animate:

$("#element").css("background-color", "lightblue")
             .animate({ backgroundColor: "white" }, 30000);

But the background color fails to fade about 10% of the time when I have 10 or 20 elements that I'm fading all at once. Unfortunately, that error rate is too high for use in a production, released product. Instead of trying to debug the API and find where the bug is in jQuery, I thought I'd try to whip up the JavaScript for the fade effect myself.

It turned out to be pretty simple. The basic outline of the algorithm is to get a starting color and an ending color, find the difference between the colors, and divide that difference into incremental steps based on how long it should take to fade the background color. Then at very short intervals (ms), take steps toward the final color until the total time for the transition has elapsed.

Demonstration

Here's two demonstrations of the effect. The left fades from light blue to white over 10 seconds, and the second fades from red to blue over 3 seconds.



Script Usage

Here's the script. (It's also shown at the end of this post.) To use it, invoke the following:

fadeBackground('box1',[98,180,232],[255,255,255],5000);

Where box1 is the element whose background should be faded, [98,180,232] is the starting color in order of red, green blue values, [255,255,255] is the final color, and 5000 is the time in ms that the fade should occur over.

The script has no external dependencies like jQuery.

Customization

The code is simple enough to modify to fade other elements besides background color. Locate the two lines lines below in stepFade():

document.getElementById(elementId).style.backgroundColor
	= "rgb(" + step[0] + "," + step[1] + "," + step[2] + ")";
 
...	
 
document.getElementById(elementId).style.backgroundColor 
	= "rgb(" + endColor[0] + "," + endColor[1] + "," + endColor[2] + ")";

And change style.backgroundColor to whatever style element you want to chance instead.

Script

function fadeBackground(elementId, startColor, endColor, timeMs) {
    var startTime = new Date().getTime();
 
    function stepFade(elementId, startColor, endColor, startTime, endTime) {
        var now = new Date().getTime();
 
        var step = new Array(3);
 
        var progress = (now - startTime) / (endTime - startTime);
 
        step[0] = Math.round(startColor[0] + ((endColor[0] - startColor[0]) * progress));
        step[1] = Math.round(startColor[1] + ((endColor[1] - startColor[1]) * progress));
        step[2] = Math.round(startColor[2] + ((endColor[2] - startColor[2]) * progress));
 
        document.getElementById(elementId).style.backgroundColor
            = "rgb(" + step[0] + "," + step[1] + "," + step[2] + ")";
 
        if (now <= endTime) {
            setTimeout(function() {
                stepFade(elementId, startColor, endColor, startTime, endTime);
            }, 20);
        }
        else {
            document.getElementById(elementId).style.backgroundColor
                = "rgb(" + endColor[0] + "," + endColor[1] + "," + endColor[2] + ")";
        }
    }
 
    stepFade(elementId, startColor, endColor, startTime, startTime + timeMs);
}

Tags: