JavaScript for C# developers: writing a library (part 2)

As I said last time, my first library object is going to be a cookie object.

Yemeni Market Datesphoto © 2007 Myscha Theriault | more info (via: Wylio)The browser API for this is remarkably simple. There’s a string property of the document object called cookie. Read from it and you get a semicolon-separated list of name=value pairs as a string. Each name is the name of a cookie and the value is its value. Write to it with a specially formatted string (again semicolon-separated) and you add or update a single cookie (you can only update a cookie one at a time). The string contains at least three parameters: a name=value pair, an expiry date for the cookie defined in UTC format, and a path from which the cookie is valid (usually everyone uses “/” for the root of the site, and this is what we’ll do). There are other parameters if you want to use them, but we’ll stick with these. Finally, to delete a cookie, you merely update it with an expiry date that’s in the past and the browser will delete it.

Things to note: the API does not give you a way to read a single cookie. You get them all and you have to parse the string looking for the one you want. You also don’t get back any ancillary information about the cookie (its expiry date, for example).

Also, it seems that we’ll need some date routines as well: we’ll need to calculate a date in the past to delete a cookie and it would be nice if we could make the expiry date optional in our API and the function calculate a date, say, a month in the future if so. Actually, for now, if you don’t mind I’ll shelve the addMonths() date function (it can be a pain to write and get right) and just have an addDays function.

Time for a jmbDate object in my library to take care of any date functions I may need. Start off with the usual base code that you’re used to by now:

(function ($) {

  var $j = $.jmbLibrary;
  var $jd = $j.date = {};

})(jQuery);

(Quickly now: an auto-execute anonymous function taking one parameter, jQuery, such that it’s known as $ inside the function. Create a new empty object called date in the jmbLibrary object and give it a cute local name. You’re getting the drift.)

First let’s write a now function to mimic .NET’s DateTime.Now property. Pretty easy:

  $jd.now = function () {
    return new Date();
  };

Now, let’s write the addDays function. We’ll make use of a couple of JavaScript’s Date functions here: first the valueOf function returns passed date as the number of milliseconds since midnight on 1st January 1970. Then we’ll use one of Date’s constructors that takes a number of milliseconds and returns a new Date object.

  var getMs = function (date) {
    return date.valueOf();
  };

  var dateFromMs = function (value) {
    return new Date(value);
  };

  $jd.addDays = function (date, days) {
    return dateFromMs(getMs(date) + days * 1000 * 60 * 60 * 24);
  };

As you can see I decided to wrap those “raw” JavaScript functions into two local functions: it makes the code perhaps a little easier to read in the addDays function and it also means I can talk about function scope again. These two new functions are local to (and visible throughout) this outer anonymous function, they will not be visible outside. The addDays function will be visible outside since I’m adding it as a function to the new jmbDate object (the two local functions are not added to this object). The closure formed by the outer function gives me the ability to have private functions just for use inside the function (and hence for use by that same jmbDate object since it can “see” them too).

Finally I decided to add an isDate function too. In the next part of this series, as I mentioned above I shall be needing to see if the expiry date has been passed in to a function or not. So, I might as well write it now.

  $jd.isDate = function (date) {
    return (!!date) && (typeof date === "object") && (date.constructor === Date);
  };

Let’s explain this code. The three parts to the boolean expression that’s returned first check that the passed in date is not undefined or null (or more rigorously is not a falsy value like 0, NaN, or the empty string, or false), that the parameter is an object, and then finally (since every object has a constructor) that it was constructed from the Date constructor.

The reason for the double negation in the first subexpression (which surely is the identity operator?) is a JavaScript quirk. It seems more obvious that I should be able to write

    return (date) && [...];

since the interpreter has to convert the date variable to a boolean in order to evaluate the expression. The quirk is that this conversion is merely temporary. With && (and || for that matter) the result of the expression may not be a boolean. In fact, if date is undefined/null, it’ll be temporarily evaluated as a boolean equal to false and the rest of the expression will be ignored. The temporary conversion is ignored and the result of the expression is date and not false, which is certainly not what we want. So I have to convert date manually to a boolean for the expression to be evaluated. One way is to split the code:

  $jd.isDate = function (date) {
    if (!date) { return false; }
    return (typeof date === "object") && (date.constructor === Date);
  };

So, if the parameter is undefined/null (or any falsy value), explicitly return false. Otherwise, the parameter has some value, and so we can do the rest of the tests on what that value might be. The method I chose is to convert the parameter to a boolean by applying the negation operator, and then apply it once again to get the sense right. A little tricky I’ll admit, but it’s very much equivalent to this kind of idiomatic code:

someText = someText || "missing";

This code says, in essence “set someText equal to itself if it’s not undefined/null, otherwise set it equal to ‘missing’”. As you can see, this code would not work at all if the temporary check for true/false were permanent.

The code that creates the jmbDate object now looks like this:

(function ($) {
  var $j = $.jmbLibrary;

  var getMs = function (date) {
    return date.valueOf();
  };

  var dateFromMs = function (value) {
    return new Date(value);
  };

  var $jd = $j.date = {};

  $jd.isDate = function (date) {
    return (!!date) && (typeof date === "object") && (date.constructor === Date);
  };

  $jd.addDays = function (date, days) {
    if (!$jd.isDate(date)) { return; }
    return dateFromMs(getMs(date) + days * 1000 * 60 * 60 * 24);
  };

  $jd.now = function () {
    return new Date();
  };

})(jQuery);

At this point, I‘ll stop until next time, when we really will start to write the cookie code.

Album cover for Friends of Mr. CairoNow playing:
Jon & Vangelis - I'll Find My Way Home
(from Friends of Mr. Cairo)


Loading similar posts...   Loading links to posts on similar topics...

5 Responses

 avatar
#1 Marcel Doru Popescu said...
16-Dec-10 11:56 PM

Hmm... I'm a bit confused. You're talking about a jmbDate object (twice), but you're showing a $j.date (or alternatively a $.jmbLibrary.date).

Also, you left off the portion of the code where you define $.jmbLibrary - I can't figure out whether that's intentional or not.

julian m bucknall avatar
#2 julian m bucknall said...
17-Dec-10 10:14 AM

Marcel: Nice catch. I must have had too much of the old vino last night: what I said was downright confusing. The JavaScript file this date code is found in is jmbDate.js. My bad.

Let me clear up the confusion in the article.

Cheers, Julian

julian m bucknall avatar
#3 julian m bucknall said...
17-Dec-10 10:26 AM

Marcel: Oh, and the jmbLibrary code is found in the previous article in this series: http://jmbk.nl/z7B4H

Cheers, Julian

 avatar
#4 Marcel Doru Popescu said...
17-Dec-10 2:52 PM

I knew about that article... I missed the point that there were two distinct files, sorry. (jmbLibrary.js and jmbCookie.js - which is why you didn't need to re-create the initial $.jmbLibrary object in the other sample code fragments.)

 avatar
#5 Marcel Doru Popescu said...
17-Dec-10 2:52 PM

To clarify: I had assumed that everything went into a single file, that's what made the absence of $.jmbLibrary confusing.

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response