Thursday, May 15, 2008

jsDump - Pretty dump of any Javascript data

Introduction

This script can dump any type of Javascript data (or most), thus, generating a string out of the received information.
It's specially useful when creating some kind of console or logger.
Also, it can be used for the development stage, when you need to inspect things on browsers that lack a good console.

How to use

Very simple:
var dumped = jsDump.parse( ... );
Instead of '...', you need to put the data that you want to dump.
That could be an array, a date, the document, an array of arrays, etc.
The variable 'dumped' will now contain a string with the dumped version of your data.

Settings

These are the things you can configure:
  • HTML
    (boolean) If true, the data will be escaped for html output, the default setting is false.
    Note that the script won't escape all the text, only the parts added by the script (indentation, new lines, dumped nodes), it won't alter original strings, etc.
  • indentChar
    (string) This is the unit of indentation, by default it is 4 whitespaces.
    Keep this agnostic from the HTML mode, it will escape spaces and tabs by itself.
  • multiline
    (boolean) Whether to generate multiline code. The default is true. This is specially relevant when dumping collections.
  • DOMAttrs
    (Object) This contains a map of attributes, that need to be dumped from HTML nodes.
    The keys must be the names to be showed. The values, the real name of the attribute contained in the node.
    By default, 'id', 'name' and 'class' are dumped (only if not empty).
To modify these settings, just do (f.e):
jsDump.HTML = true;
This needs to be done before calling .parse().

More methods

jsDump also contains a method, jsDump.typeOf, that is used internally to sniff the type of what you send.
You can use this function for your own code, it'll make an advanced typeof.
To modify the way a certain kind of data is dumped, you must set that using the method 'setParser'.
The 'parser' can be a string, or a function. Use a string if the output is always the same, otherwise, use a function. It will receive the object as argument and must return the parsed string.

You use it like this:(f.e)
jsDump.setParser( 'number', '[number]' );
jsDump.setParser( 'date', function( date ){
return date.toUTCString();
});
To see all the data types, check the source, no gain on listing them all here.
Lastly, you can create your custom types, by just adding them to the list, then, inside a parser function, you can call the method .parse() and pass, as second argument, the custom data type.
This will force the script to use that type, instead of sniffing it from the data.

Links

Downloads

Wednesday, May 7, 2008

Textnode translator for Javascript

Introduction

This is a generic JS Class, that allows you to translate(parse) the content of text nodes, and replace them for the new content.
You only need to specify the parsing function, and the starting (root) node.
All the text nodes inside it, will get parsed.

Modes

The class supports synchronous and asynchronous parsing.
The parsing function will receive the original text as first argument.
If you can parse the text right away, then just return it from the parsing function.
If it needs to be delayed (f.e: AJAX request), then the set the attribute 'sync' of the instance, to false. By doing this, you will get a second argument in the parsing function, which will be a function that you need to call, passing it the parsed text.

Returned data

The parsed data you return can be:
  • a string
    This is the standard, a string replacing the old one.
  • nothing
    new If you don't return data, or the same string, it will just get skipped.
  • a node
    new You can return an html node.
  • an array
    new You can return an array with nodes and/or strings.

Filteringnew

Optionally, you can pass the translator, a filtering function. This way you can exclude elements (and their descendants) from the parsing.
To use it, pass the function as second argument to the constructor.
It will receive the (element) node, and it must return true or false.

How to use

Check the demo to see both modes and filtering in action.
You need to call the method 'traverse' of the instance, passing it the root node.
Remember to call the method after the html document is parsed, so you can access all the nodes in it.

Links

Downloads

Update 5/26/08
Added 1.0.1, updated the docs and demo.

Monday, April 28, 2008

Doctorate on jQuery.SerialScroll

Introduction

After replying to a large amount of comments and emails about jQuery.SerialScroll, I decided to comment some more about this plugin, also to publish some snippets, to achieve commonly desired effects or behaviors.
This should save you (and me :)) some time. It might also show you some additions, that you haven't considered.
I'll also try to show some more model calls to the plugin, so you can unattach yourself from those in the demo.

A little bit of theory

Before the snippets, I'll go through the basics, to refresh your mind.
Calls to the plugin
It can be done on two different kind of elements
  • Element to be scrolled: This is what you normally do when you want one single instance of SerialScroll in a page.
    $('#pane').serialScroll({
    //...
    });
    
  • Container: You might want to create many "SerialScrolls" in the page, you don't need to call it many times, just put the option 'target' to work.
    $('div.container').serialScroll({
    //...
    target:'div.pane',
    //...
    });
    
    This will allow you to have many divs with class container, which contain a scrollable div with class pane. They don't need to be divs.
    When doing this, the selectos for the arrows also become relative to the container, instead of absolute.
  • Global call: If by chance, you want to scroll the window, you'll need to use this approach.
    $.serialScroll({
    //...
    });
    
    If, for some reason, you need to retrieve this implicit element, call:
    $.scrollTo.window();
    This will return you the element, don't use window or document.
onBefore
This setting, which is a callback, will empower some snippets, so you better learn about it.
$('div.container').serialScroll({
//...
onBefore:function( e, elem, $pane, $items, pos ){
//...
},
//...
});
Note that:
  • The 'this' is the element that triggered the event.
  • e is the event object.
  • elem is the element we'll be scrolling to.
  • $pane is the element being scrolled.
  • $items is the items collection at this moment.
  • pos is the position of elem in the collection.
  • if it returns false, the event will be ignored.
  • Those arguments with $ are jqueryfied.
The onAfter, only receives 'elem' as the first argument, and the 'this' is the element being scrolled ($pane but not jqueryfied).

The snippets

Now, what you really want, the code required to do all those fancy things, that the plugin doesn't do natively.
One note, all the snippets are wrapped with a document ready and they use symbolic ids. Needless to say, you don't need to copy the ids, or even USE ids.
Only the relevant settings are specified, so you will see "..." reminding that it's incomplete code.
You configure the selectors and the rest of the settings, according to your needs.

  • Hide the arrows when the limits are met. get
  • Stop the auto scrolling on hover. get
  • Scroll from right to left (or bottom to top). get
  • Manipulate the widget using the keyboard. get
  • Generate a pager based on the items. get

Concluding

I hope this helped and saved you some time, I also hope you learnt something new.
I plan to add more snippets as they come up from requests.
If you have any doubt, don't hesitate to ask.

Thursday, April 17, 2008

jQuery.Modularize

Introduction

This small plugin(673 bytes!) enables you to have modular methods on jQuery.
It allows the developer to apply the module pattern, to the functions of jQuery.fn.
They can be used as namespace/module for more functions.
This helps keeping the rule of only claiming one name from the jQuery namespace.

Example

It takes a module declared like this:
$.fn.foo = function(){ ... };

$.fn.foo.bar = function(){ ... };

$.fn.foo.baz = function(){ ... };
And enables you to use it like this:
$(...).foo().bar( ... );

$(...).foo( .... );

$(...).foo().baz( ... ).foo().bar( ... ).foo( ... );
As showed above, the method that acts as module, can also be used as function.
To avoid using it as module, the function must receive arguments.

How to use

Basic call
$.modularize( 'foo' );
If $.fn.foo already exists, it will be used when $(...).foo(...) is called with arguments.
If $(...).foo(...) won't be used as method, then you don't need to declare it, just call the plugin before adding the sub-functions.

Providing a default method
$.modularize( 'foo', function(){ ... } );
The given function will be used when $(...).foo(...) is called with arguments.
This is a shortcut for declaring it, and then calling the plugin.

Nested calls
$.modularize( 'baz', null, $.fn.foo );
This allows:
$(...).foo().baz().foobar( ... )
Instead of null, you could send the default method.
Before doing this call, make sure you created $.fn.foo.

Miscellaneous
The 'this' (scope) of the methods, will always be pointing to the calling jQuery object, $(...).
The methods will be gathered on the first call to the function.
Thus, you can "modularize" before adding the sub-methods to the module, or after, it doesn't matter.

If you need to call the module and then add methods, or you just want the methods to be gathered each time, set
$.fn.foo.lazy = true;
You can set this to true, call the methods, and then reset to false.

Links

Downloads

Monday, April 7, 2008

Benchmarking Javascript variables and function scopes

Introduction

The other day, I was doing a quick templating plugin for jQuery. It would split long strings into arrays of tokens, and then parse these, based on a map(associative array) of handlers for the different "commands" in the tokens.

The Background

The thing is, that the tokenization was done on the first call, and only the actual parsing was executed each time the template was applied.
Surprisingly, the parsing function of the plugin was always taking around 2.5 seconds!
This was a recursive function, called around 10 times. It had a short loop of 3-4 iterations per call.
This shouldn't be SUCH a heavy task, certainly not enough to take that long.
I was really confused so I started experimenting.

The Situation

There were 3 types of tokens in the template:
  • Tokens that open/start a block, f.e: <?if $foo?>.
  • Tokens that close/end a block, f.e: </if?>.
  • Tokens that are their own block, f.e: <?elseif $bar/>.
I called this, the mode of the token. So I had 3 symbolic constants declared. All the code was wrapped with a self-executing function, to create a local context. It looked like this:
(function( $ ){
 var
     OPEN = 1,
     CLOSE = 2,
     CLOSED = 3;
 
 /* the rest of the code */
 
})( jQuery );
These constants, were compared to a 'mode' attribute of the token. This caused 1 reference to each of them, on every loop in the parse function.

A Small Change

I tried tried any possible optimization, but Firebug's stopwatch was stuck at 2500ms.
I was nearly desperate, so I started changing things at random.
One of those changes, was modifying the way I was saving the mode of a token. Instead of storing the integer in a 'mode' attribute of the token, I used 'open','close' and 'closed' as boolean attributes.

There's no way to describe my happiness, when Firebug's stopwatch showed ~230ms instead of ~2500ms.
I must say I was really surprised and confused, this was such a small change, and one that I'd never had guessed.
After this, I decided to clear my doubts with a consistent benchmark.

The Benchmark

The first thing I did, was to make my "own Firebug". I created a script with a stopwatch, a logging method and an easy way to benchmark many sequential calls to a function, with many attempts, generating an average and finding the lowest.
After that, I set up the environment. Declared a global variable, and a function similar to the one showed above. Then nested another equal function inside it, and nested another one inside the latter.
In the end, it looked like this:
var global = 1;

(function(){
   var local1 = 1;

   (function(){
      var local2 = 1;  

      (function(){
         var local3 = 1;

         /* here I executed the benchmark */  
  
      })();  
   })(); 
})();
This created 4 variables, 1 global, and 3 local. All of them could be accessed from where the benchmark was being ran, but they belonged to different scope chains. I benchmarked 4 functions, each of them accessed one of these variables 400,000 times with a loop.
Each benchmark was tested 5 times, in order to find an average and the minimum. The latter should be the one that really matters. I tested on all 4 major browsers, on my Windows XP.

The Results

The numbers were surprising, but not in all browsers. Opera 9 seemed to be really indifferent to the scope chain, all took the same time. Firefox 2 was really the slowest. Safari 3 and IE6 were in the middle.
These are the numbers, I'll only mention the lowest from the 5 attempts.
Firefox 2
* global: 969ms * local1: 672ms * local2: 359ms * local3: 78ms
IE 6(=7)
* global: 187ms * local1: 171ms * local2: 125ms * local3: 94ms
Safari 3
* global: 125ms * local1: 93ms * local2: 78ms * local3: 62ms
Firefox 3 beta 5
* global: 41ms * local1: 48ms * local2: 48ms * local3: 37ms
Opera 9
* global: 31ms * local1: 31ms * local2: 31ms * local3: 31ms
Opera is the clear champion, for some reason, the scope doesn't affect the timing at all.
It does affect the rest of the browsers. It will be specially noticeable in Firefox. Note that Firebug was turned off while benchmarking Firefox, it did affect the perfomance but not THAT much.
UPDATE:Added IE7 and Firefox 3 beta, congratz Mozilla, much better!

The Conclusions

Referencing might take time.
Using variables that were declared on different scope chains, takes longer than those that are local to the executing function.
If you are going to use one very often inside a deeply nested function, you should save it in a local variable first.
One common situation is when using the global variables window and document.
It does matter, but it's not always crucial.
Although it does make a difference, you shouldn't become paranoid about this.
These numbers were produced by 400,000 iterations. You probably don't access a variable that many times. Optimizing won't yield a noticeable improvement most of the time.
There are some situations where you should be careful though
  • Within loops, if you have a long loop, caching the variable could improve the perfomance greatly.
  • Inside frequently called functions. This is similar to loops.
  • Inside functions, that are deeply nested inside others. If these reference a distant variable, many times, it could hit on perfomance too.
Benchmarking is fun!
I always love running benchmarks, feel free to try this yourself. You'll find the link below.
The tool used for the benchmark (benchmark.js) can be taken for your own experiments.
If you try other browsers/OS, feel free to post the results, I'll include them so others can see.

I tried to find links about this, but none showed up. I'll update if I get to find any. Same for you, reader. If you can find articles about this, I'll add the links to this post.

Thanks for reading.

Links

Downloads