Decoding jQuery – Callbacks Object

In the Decoding jQuery series, we will break down every single method in jQuery, to study the beauty of the framework, as an appreciation to the collective/creative geniuses behind it.

The jQuery.Callbacks() function, introduced in version 1.7, returns a multi-purpose object that provides a powerful way to manage callback lists. It supports adding, removing, firing, and disabling callbacks. (jQuery API Doc)

It provides many Supported Flags. It is structured as a list of space-separated strings that change how the callback list behaves (eg. $.Callbacks( ‘unique stopOnFalse’ )).

Callbacks Object can also be used to do pub/sub and it is also used to make jQuery.Deferred().

1. Structure of jQuery.Callbacks()

Below is how the structure of jQuery.Callbacks() function looks like in jQuery source(callbacks.js).

jQuery.Callbacks = function( flags ) {

  // Convert flags from String-formatted to Object-formatted
  // (we check in cache first)
  flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};

  var // Actual callback list
    list = [],
    // Stack of fire calls for repeatable lists
    stack = [],
    ...
    // Add one or several callbacks to the list
    add = function( args ) {
      //...
    },
    // Fire callbacks
    fire = function( context, args ) {
      //...
    },
    self = {
      //...
    };

  return self;
};

add function – adds a list of callbacks to the variable list array.

fire function – loops through the items in the list and fire them one by one.

self object – is the actual Callbacks object returned at the end of the function.

The most important methods in the self object are:
add() adds a callback or a collection of callbacks to the list, this is the one we use to build subscribe in pub/sub function
fireWith() method actually calls all callbacks with the given context and arguments
fire() method inside the self object executes fireWith with the object as the context, this is the one we use to build publish in pub/sub function

There are also many other essential and useful methods in the self object, they are:
has(): a boolean check to find out if a callback list contains a specific callback
remove(): Remove a callback from the list
empty(): a method for emptiying a callback list of all callbacks added so far
disable(): disables further calls being made to the callback list
lock(): lock the list in its current state

2. How to use Callbacks object
Suppose we have a Rails application, assume we have a function displayAlert and msgStatus like the ones below.

var displayAlert = function( msg ) {
      console.log( msg );
    },
    msgStatus = function ( msg ) {
      fn1(msg + '...feed is read!');
      return false;
    },
    callbacks = $.Callbacks();
callbacks.add( displayAlert );
callbacks.fire( 'new message 1' ); // outputs: new message 1
callbacks.add( msgStatus );
callbacks.fire( 'new message 2' ); // outputs: new message 2, new message 2 is read!

After creating a callbacks = $.Callbacks();, we can use add to add displayAlert to the callbacks variable as a list item, and later add postFeed as a new item. By using fire, the callbacks object is fired with the function/functions executed.

As explained above, fire is actually fireWith with the object itself as the context, you can also define/specify a context and arguments for callbacks being bound and fired.

var fn = function (argA, argB) {
  //..
}
callbacks.add( fn );
callbacks.fireWith( window, ['foo', 'bar']);
// outputs: foo, bar

Addy Osmani provided example code for some of these methods and some of the flags arguments in his Demystifying jQuery 1.7′s $.Callbacks

3. How does a pub/sub function work

Below is an example of building a jQuery.Actions for publish and subscribe when microposts are received/sent, as well as sent status.

var displayAlert = function(msg) {
  //...
}

var msgStatus = function(msg) {
  //...
}
var actionItems = {};

jQuery.Actions = function( id ) {
  var callbacks,
      method,
      action = id && actionItems[ id ];
  if ( !action ) {
    callbacks = jQuery.Callbacks();
    action = {
      publish: callbacks.fire,
      subscribe: callbacks.add,
      unsubscribe: callbacks.remove
    };
    if ( id ) {
      actionItems[ id ] = action;
    }
  }
  return action;
};

// Subscribers
$.Actions('micropostArrived').subscribe(displayAlert);
$.Actions('micropostSent').subscribe(displayAlert);
$.Actions('micropostSent').subscribe(msgStatus);

// Publisher
$.Actions('micropostArrived').publish('New Items arrived');
$.Actions('micropostSent').publish('Item posted');

In the above example, the publish and subscribe use fire and add for the pub/sub pattern, and remove for unsubscribe.

This is not the only way to use jQuery for pub/sub, Addy’s Four Ways To Do Pub/Sub With jQuery 1.7 and jQuery UI gist discussed more ways of doing it with jQuery and jQuery UI.

Posted in JavaScript | Tagged | 5 Comments

Decoding jQuery – domManip: DOM Manipulation

In the Decoding jQuery series, we will break down every single method in jQuery, to study the beauty of the framework, as an appreciation to the collective/creative geniuses behind it.

Inside manipulation.js within the jQuery source, there is an interesting internal method named domManip, this is mainly used for methods like: .append(), .prepend(), .before() and .after().

domManip: function( args, table, callback ) {
  //...
}

Using fragment
In domManip, jQuery first checks if we are dealing with fragment node. In DOM, there are 12 different node types, and the one jQuery is checking is type 11 (Node.DOCUMENT_FRAGMENT_NODE == 11). If we’re in a fragment, just use it, otherwise, building a new one using jQuery.buildFragment.

if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
    results = { fragment: parent };
} else {
    results = jQuery.buildFragment( args, this, scripts );
}

jQuery.buildFragment
Let’s take out jQuery.buildFragment and see what it does. The method comes with excellent explanation that talks about what each block of code does:

1. nodes may contain either an explicit document object, a jQuery collection or context object. The following checks if nodes[0] contains a valid object to assign to doc

if ( nodes && nodes[0] ) {
  doc = nodes[0].ownerDocument || nodes[0];
}

2. Ensure that an attr object doesn’t incorrectly stand in as a document object, Chrome and Firefox seem to allow this to occur and will throw exception

if ( !doc.createDocumentFragment ) {
  doc = document;
}

With the above check, the script will then fragment

if ( !fragment ) {
  fragment = doc.createDocumentFragment();
  jQuery.clean( args, doc, fragment, scripts );
}

3. To check if an element is cacheable. The method only caches “small” (1/2 KB) HTML strings that are associated with the main document. Cloning options loses the selected state, so it doesn’t cache them. IE 6 doesn’t like it when you put object or embed elements in a fragment. Also, WebKit does not clone ‘checked’ attributes on cloneNode, so it doesn’t cache them either. Lastly, IE6, 7, 8 will not correctly reuse cached fragments that were created from unknown elements.

if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
    first.charAt(0) === "<" && !rnocache.test( first ) &&
    (jQuery.support.checkClone || !rchecked.test( first )) &&
    (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {

    cacheable = true;

    cacheresults = jQuery.fragments[ first ];
    if ( cacheresults && cacheresults !== 1 ) {
      fragment = cacheresults;
    }
  }

The following script does the caching once it's determined cacheable:

if ( cacheable ) {
  jQuery.fragments[ first ] = cacheresults ? fragment : 1;
}

Appending script
I first discovered this while inserting a script tag using jQuery and inspect the element without seeing this. This is because within the domManip, there is a check for script elements.

if ( scripts.length ) {
  jQuery.each( scripts, function( i, elem ) {
    if ( elem.src ) {
      jQuery.ajax({
        type: "GET",
        global: false,
        url: elem.src,
        async: false,
        dataType: "script"
      });
    } else {
      jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
    }

    if ( elem.parentNode ) {
      elem.parentNode.removeChild( elem );
    }
  });
}

This check does 4 things here:
1. first it detects and loop through all the script elements
2. if the script element has a src attribute, then jQuery uses jQuery ajax to load the script
3. if the script is embedded, jQuery uses jQuery.globalEval to evaluate the script
4. it removed the child from the parent element

Posted in JavaScript | Tagged | 4 Comments

Quick Tip – Getting Paperclip to work on Windows

Nowadays Windows feels like Ubuntu a few years ago, without some hacks and tweaks, it can’t run many awesome stuff by default. If you use Ruby and Paperclip gem, you may have realized that the default gem doesn’t work because of issues with ImageMagick. To fix this, you need a little hack like the one below:

1. go to ImageMagick official website, download and install the latest Windows release of ImageMagick
2. create paperclip.rb in your /config/initializers/
3. enter the following lines in the file, remember to replace ‘ImageMagick-6.7.6-3-Q16′ with the version you have downloaded.

require "paperclip"

Paperclip.options[:command_path] = 'C:\Program Files\ImageMagick-6.7.6-3-Q16'
Paperclip.options[:swallow_stderr] = false

And now Paperclip should be working just like the way it does on Mac/Linux :)

Posted in Ruby | Tagged , | 9 Comments

Quick Tip – Compiling Bootstrap with SimpLESS

Many people must have already been aware of this problem/solution, but for those who don’t know, read on to know more.

Bootstrap has been a popular set of HTML, CSS and JavaScript for front-end development. The problem many developers face is that once too many people start to use Bootstrap, many sites start to look the same. So many start to look into CSS to customize the look and feel. The CSS portion is based on LESS. So SimpLESS is a default choice for many Windows users to compile LESS to CSS. SimpLESS doesn’t come with less frequent update (compare with LESS), therefore, compile errors (syntax error) often occur.

To fix it is rather easy:

1. download the latest version of LESS from the official website: http://lesscss.org/

2. go to C:\Program Files\SimpLESS\Resources\js (or where you have installed SimpLESS, replace less.js with the one you just downloaded

3. now if you try to close and open SimpLESS again and try to compile Bootstrap LESS CSS, it should be working :)

Hope this helps <3

Posted in CSS | 3 Comments