Archive for the 'javascript' Category

Serializing Javascript Objects into Cookies

Tuesday, November 25th, 2008

A while ago I mentioned that my school gives students and faculty Novel NetDrive accounts. This means we can all publish simple websites on their service, but get no server side scripting. This makes that space great for teaching students HTML but relatively useless for everything else.

O previously described how to get a semi-presentable website constricted without server side includes. This is a sort of follow up to that article showing you more tricks. The big issue you get working in a client-side only environment is persistence. The only way to accomplish it is to use cookies. So I set out to see what exactly can I store in a cookie.

It turns out I can store just about anything in there. For example the code below generates a Javascript object with few fields and functions, and then stores it in a cookie:

<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.cookie.js"></script>
<script type="text/javascript">
	$(document).ready(function() {
 
		var obj = new Object();
		obj.a = "foo";
		obj.b = "bar";
		obj.ab = function(){return this.a+this.b;};
		obj.show = function(){alert(this.ab());};
 
		var src = obj.toSource();
 
		$.cookie("testcookie", src);
 
		temp = $.cookie("testcookie");
 
		nobj = eval(temp);
		nobj.show();
	});
</script>

The toSource function does most of the work here. It serializes an object into a string which can be then deserialized using the eval function. Note that this will work on stand alone functions too, since in Javascript they are first class objects. Neat, eh?

I’m using Klaus’ Hartl’s jQuery cookie plugin because the native Javascript handling of cookies is it is retarded and error prone. It basically returns all the cookies you set as a single string containing semi-colon separated list. As you can imagine, retrieving serialized objects which are bound to contain lots of semi-colons can be problematic. Klaus’s code was not designed for this sort of thing but it uses the encodeURIComponent method to escape special characters. Which works out great because it escapes the ‘;’ character inside of the serialized objects making them easy to retrieve.

I probably don’t need to tell you that the example above is a very, very bad coding practice. You really don’t want to eval any code that might have been tampered with. Since we are storing our object in a cookie which can then be modified on the client machine, we are really opening ourselves for abuse. So while storing functions inside cookies is possible, I would not recommend it.

What you want to do is to serialize your objects into JSON, and the safely parse it back while making sure you are actually getting back JSON object rather than random code. There is a really good plugin that does this for you. So your code will look something like this:

<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.cookie.js"></script>
<script type="text/javascript" src="jquery.json.js"></script>
<script type="text/javascript">
	$(document).ready(function() {
 
		var obj = new Object();
		obj.a = "foo";
		obj.b = "bar";
 
		var src = $.compactJSON(obj);
 
		$.cookie("testcookie", src);
		temp = $.cookie("testcookie");
 
		nobj = $.secureEvalJSON(temp);
		alert(nobj.a + nobj.b);
 
	});
</script>

The secureEvalJSON method is much safer than just running eval on arbitrary code. The pluging also has an “unsafe” eval version, but I would not recommend using it unless you can guarantee the cookies haven’t been tampered with (and in most cases you can’t).

There is a small caveat you need to keep in mind. The space you have to work in is very limited. Fore example, IE only allows you to store around 4KB of data per domain. This is not per cookie, but a total space you have for all your name-value cookie pairs. This means that sticking a huge JSON object (or many smaller ones) into a cookie just won’t work. IE will silently drop cookies that exceed this limit. So use this technique sparingly, and if you can, compress the data as tightly as possible.

Post a Comment, Win a Free Book

Thursday, November 6th, 2008
the_good_parts.jpg

Recently Adam Kahtava had a free book giveaway on his blog. I was the lucky winner! The prize was a copy of Javascript: the Good Parts by Douglas Cockford (you can read Adam’s review here). I don’t think I have ever won anything in my life so naturally I proceed to screw up my first time by giving Adam the wrong address. Or rather I gave him the address of the building where my office is located, but forgot the suite number or the company name. Only after the book was on it’s way I realized that omitting these bits of info could become a problem. After all there are like 10 other companies located here and I only know the people who work on my floor - not all of them at that. There is also no main reception or front desk where the package could be dropped off. Things get delivered directly to individual suites. Likelihood of the book being bounced back to Adam was very high.

Somehow the book ended up reaching me though. It took longer than expected (almost two weeks) but it got here. The receptionist lady in the law office across the hall actually remembered my name from other miss-delivery incidents and she kept it for me. I guess sometimes it pays to have a name that is hard to pronounce. People may never know how to spell it, but if they it on an envelope they will likely recognize it and pass lost mail your way. Unless it’s candy or other edible stuff. Then they just steal it. And yes, the ladies who work the front desk over there have like a sixth sense for detecting chocolate products. Packaging it up in non-descript or hermetically sealed box wont help. We have tried. P

Anyway, few days have passed and was ready to forget about the whole thing. Then I got another copy in the mail. Total confusion. It was the same book, sent by the same merchant. What happened?

I immediately started checking recently queued blog entries to see if any of them are missing. Then I scrolled through my Google Reader paying close attention to the dates of the unread entries. You see, this is how I check to make sure I’m not stuck in a groundhog day like time loop of some sort. I don’t really read newspapers or watch news in the mornings. I get my news from the internets and that’s after I get some caffeine in my system. When I’m getting ready in the morning my TV is usually tuned into History Channel, Comedy Central or Cartoon Network (cause I was watching Adult Swim late at night and never shut it off before going to sleep and now I’m to lazy to change it) - where repeats happen all the time. So I wouldn’t know that I was looping in time until I started reading my RSS feeds on my lunch break. And yes I take these things very seriously. I’m always prepared for unrealistic events like time loops and Zombie outbreaks. I have things planned out for these unlikely events - like what I would do, where would I go and etc. P

Fortunately all was well. The dates were correct and I have concluded that I avoided being stuck in time once again. Yes, it happened before. I swear I almost got stuck in a time loop just like this around 4 or 5 times now. Some people say these things are just glitches in the matrix but I know better. The feeling of Déjà vu is telling you that you just narrowly escaped a time loop.

Apparently, what happened is this: Adam went and yelled at the merchant because they were taking their sweet time. It took them over two weeks to ship this book to me. So apparently they and Adam got in a little fight and their mom got scared said “you’re moving with your auntie and uncle in bel-air”. Er… I mean, they assumed the book got lost and sent me another copy via priority mail. Or something like that. I still don’t completely comprehend the reasoning behind this. All I know is that they sent a second book.

In effect I got their rushed “backup” copy first, and then the original copy a few days later. So I now have 2 identical books sitting on my desk. I asked Adam if he wanted the second copy back but he didn’t. He was only charged for one book, and since he already owns a copy he graciously suggested I pass it along to some of my readers just like he did. So this is exactly what I’m going to do. I’m going to give it away to one of you guys! This is what you need to do:

  1. Post a comment in this thread indicating that you want the book
  2. Make sure you put a valid email in the email field when posting the comment so I can contact you if you win
  3. Make sure that you have an address located somewhere in continental US that you could give me
  4. ???
  5. Profit

Yeah, sorry about the continental US thing. It’s because I want to be nice and pay for the postage. But you know, shipping oversees costs extra money and may require me to do extra stuff and etc. So I’m limiting this to US residents only.

If this goes well, and there is a good turnout I might do it again at some point in the future - maybe with a different book prize. )

Oh, and don’t forget to thank Adam, and the clueless Amazon.com book-merchant he used for making it all possible. I’m going to wait like a weak and see how many comments I can collect here and then pick one person at random. I’ll announce the winner sometime around next Thursday.

Identifying External Links With CSS

Monday, October 6th, 2008

As you may have noticed, recently I started identifying external links on this website with a little arrow symbol: ⇗. Most people agree that this is a neat idea - little visual cue that tells you the link points to an outside resource. It distinguishes the links to interesting articles from references to my older and less sophisticated rants from the past. I’m doing this via really simple CSS trick which I actually stole from underscorebleach.net:

a[href^="http"]:after {
	color: gray;
	content: "\21D7";
	font-size: 1.2em;
}
 
a[href^="http://terminally-incoherent.com"]:after,
a[href^="http://www.terminally-incoherent.com"]:after {
	content: "";
}

Simple regexp like pattern matching. First rule applies the style to all absolute links (ones that start with ‘http’) while the second one clears it of the more specific links which point back to this website. Any relative links will remain un-styled. Simple isn’t it?

Unfortunately I’m running into an ugly problem with this. If you look at the sidebar for example, you will notice that some of the small button links also have the little arrow applied to them. I’m not really happy with this behavior but I can’t figure out how to prevent it from happening. Of course I could simply put these buttons in a div, and use an overriding CSS rule to disable the formating there but that’s not the point. I just don’t want to apply this style to any image links. My initial instinct was to simply do:

a img:after {
	content: "";
}

Unfortunately that doesn’t work. Can you tell me why? It doesn’t work because the content style is applied to the A element while the above selects the IMG element. In CSS you always drill down the DOM tree - each time you specify a child element, the style gets applied to that child. Ideally, you would want to have some sort of a parent selector - something like:

a < img { /* stuff */ }

This would mean select element A whose child is an element IMG. But no dice. This is not the way things work. People have been asking for this feature since the beginning of time and it is unlikely that it will ever get implemented. I can sort of see why too - it would just be messy forcing the rendering engine to reverse up the tree and re-cascade all the styles below. Obviously this is not the way to go.

I wish there was a way to select a text only node, since this is precisely what I need here. I want to put the little arrow on all text links, and ignore all image links. So having a text-only selector would be perfect. I’m not the only one who thought about it either. Alastair Campbell for example proposes the following syntax:

a[href^="http"]:text-only { /* stuff */ }

I can totally get behind that notation, and I don’t really see huge problems with implementation. Unfortunately, no one at W3C seems to be considering this. It is a problem that is constantly resurfacing. I believe that every time someone decides to identify external links with CSS (which is probably quite often) they bump into this problem and realize that there is no way to solve it in css.

There are workarounds of course. You could for example define a custom class for image links and then make sure you use it every time you actually put one on your page. Something like:

<a href="http://example.com" class="imglink">
	<img src="/img/foo.png"></a>

Then you just define a.foo to have blank content and you are home free. Unfortunately if you are like me and you have a website full of image links, you probably won’t want to go back and change all of them to use that particular class. In fact, while you are doing this, you might as well just quit using that regexp solution I posted above and simply start marking your external links as class=’external’ instead and avoid this whole issue all together.

Slightly more elegant solution would be to use JavaScript. If you are using JQuery for example you could just use the :has selector:

$("a:has(img)")

But, do I really want to run a tight loop checking all the links on every single of my pages? It is a performance hit, and yet another script that needs to run before your page is ready for consumption. I’d rather avoid this sort of thing if I can.

So what is your solution? Do you have a clever trick to get around these issues? Do you make your external links with CSS but do not run into the image problem? Let me know. I’d love to see if this can be done properly. )

JQuery Tablesorter: List of Builtin Parsers/Sorters

Monday, September 29th, 2008

On of my users tried to explain a bug to me today. Apparently the results on the search page would not sorting properly. Or they were sorting but not by date but by half or a quarter of the date. Or at all. Or they would sort correctly, but sometimes they didn’t and only half sort them. Needless to say, I was thoroughly confused and since this was not one of those “just read the error message to me” issues, I decided to visit the desk of the person who was complaining.

When I got there, I got a small demonstration. “Watch this!” she said, as if she was going to do some trick and I got scared for a second that she will do something that we could not have predicted, and inadvertently crash the whole application due to some hidden bug. You know the type of the bugs - the ones that are completely missed in code review, overlooked in testing and only come out when a user starts clicking buttons you didn’t even put into the design somehow. Fortunately she just typed a query into a search form, and tried to sort the results by date. Then she triumphantly pointed at the screen: “See! That’s what I mean”.

She was right. The results were indeed getting sorted according to the date column, but the algorithm was wrong. Instead of sorting by date, the table was sorted alphabetically/numerically with the obvious results. So I went back to my desk to figure out what went wrong.

The sorting was done by the Tablesorter Plugin so I assumed that the algorithm was right started digging in our code first. I soon figured out what was causing the issue: blank date values!

It’s simple, to avoid clutter missing dates are simply not displayed. So a table will look a bit like this:

<tr><td>05/25/08</td> <!-- snip --> </tr>
<tr><td>08/01/08</td> <!-- snip --> </tr>
<tr><td></td>         <!-- snip --> </tr>
<tr><td>12/11/07</td> <!-- snip --> </tr>
<tr><td>11/10/07</td> <!-- snip --> </tr>

If there are not blank cells in the column, tablesorter script correctly recognizes it as a date column. If it sees blank cells, it reverts back to a text sorting algorithm. Since only some queries would produce blank cells like that, this issue went unnoticed for quite a while. I guess we trusted tablesorter to do the identification thing properly.

The fix was trivial - force the tablesorter to treat date columns as date columns no matter what they contained. You pretty much have to specify the data type for each column:

  $(document).ready(function() 
  { 
    $("#myTable").tablesorter(
    {
       headers:
       {  
         0 : { sorter: "shortDate"  },
         1 : { sorter: "shortDate"  },
         2 : { sorter: "shortDate"  },
         6 : { sorter: "shortDate"  },
         13 : { sorter: "shortDate"  },
         14 : { sorter: "shortDate"  },
         16 : { sorter: "shortDate"  },
         17 : { sorter: "shortDate"  },
         19 : { sorter: "shortDate"  }
       }, 
       widthFixed: true,
       widgets: ['zebra']
    }); 
  });

Btw, guess how I knew that I needed to use the “shortDate” keyword? Because I looked at the tablesorter.js code naturally. Initially I tried “date” but of course that did not work. Next I stared at the online documentation for 20 minutes before I decided it was pointless, so I just downloaded un-minified version of the script, and scanned through it looking for parsers and their names.

Tablesorter is a great plugin, but it really could use more in-depth documentation. While I was digging around in the code, I decided to write down the names of all the parsers for future reference. Here they are:

  • text
  • integer
  • currency
  • floating
  • ipAddress
  • url
  • isoDate
  • percent
  • usLongDate
  • shortDate
  • time

The auto detection in the script works pretty well most of the time. It can however fail for simple reasons such as blank lines. I’m surprised that the list above was nowhere to be found on the page. Oh well… It’s here if you need it.

Making a Better Use of Script Tags

Wednesday, September 10th, 2008

Admittedly, I’m a certified Stage 3 Javascript fanboi. There is a lot of things I love about the language, but one thing I do not particularly care for is the script tag syntax. Let me show you what I mean. Pretty much every page I created recently has something like this near the top:

<script type="text/javascript" src="jquery.js"></script>
 
<script type="text/javascript">
 
   $(document).ready(function(){
      // Your code here
   });
 
</script>

In other words you have two script tags in your code. First one imports the external js file (in this case the JQuery library) and the second one calls some functions from that file. If the first script tag fails to import the external code for some reason (for example due to a network problem) then the second tag will blow up in your face immediately. Granted this is how embedding of javascript in HTML was done since the begging of time, but you can’t say this is a graceful behavior.

John Resig (the man behind JQuery) noticed this, and he proposed to fix this behavior. He proposed to change the behavior of your script tags in order to allow you to write your code this way:

<script type="text/javascript" src="jquery.js">
 
   $(document).ready(function(){
      // Your code here
   });
 
</script>

Embedding the code operating on the library in the very same script tag has many benefits. For one, it logically groups the code so the calls to library functions occur in the same place that library is imported. Furthermore, it prevents network related failures. If you can’t import jquery.js, the code related to it will not get executed. This is a much more graceful than what we have now.

The best part is that these are not just some theoretical musings that will never be implemented by any of the mainstream browsers. John achieved this effect by adding 3 lines of code to the end of jQuery.js file ultimately proving two things:

  1. That John Resig is the man
  2. That jQuery is FUCKING AWESOME

In fact he takes it one step further. For example, roughly 99% of code you will write against the framework will be inside the $(document).ready closure. So while we are using shortcuts, why not just make that code optional in our modified tags:

<script type="text/javascript" src="jquery.js">
      // Your code here
</script>

Doesn’t that look much clearer, and straightforward? I love it! But don’t get too excited yet. This functionality is not included in jQuery yet and there is no guarantee that it will ever make into it. John says it will need some through testing before it gets deployed.

Once more I’m amazed, and humbled by what you can do with Javascript, and exactly how much power is packed into that 16kb jQuery script. Mind boggling!