Showing posts with label namespace. Show all posts
Showing posts with label namespace. Show all posts

Friday, June 25, 2010

hijacking javascript functions part 1

Javascript is as you may know, a loosely typed language (ie,you can not specify if a variable will be an integer, a float or an array...).
This can be a strength as it offers highly dynamic options that typed languages can not offer but this can also be a real nightmare when you need to debug something that went plainly wrong...
Typed language can offer control at the compilation phase and therefore can ease debugging by tracking bugs at an early stage. Javascript does not offer that.

We will see in this article how we can use the highly dynamic nature of javascript to hijack functions and add control from outside. we will be able to check function parameters at distance and turn the process on/off easily.

Hijack function

What is so nice about javascript is that it is very dynamic and gives you enough power to manipulate functions at a very early stage.
Let's take a little library that deals with strings and add some utility functions: trim, hyphenize, camelize and firstToUpperCase.

The code will be like:

var StringToolBox = {
    camelize: function(str) {
        return str.replace(/-(.)/g, function(m, l){return l.toUpperCase()});
    },
    hyphenize : function(str) {
        return str.replace(/([A-Z])/g, function(m, l){return '-'+l.toLowerCase()});
    },
    firstToUpperCase : function(str) {
         return str.replace(/^([a-z])/, function(m, l){return l.toUpperCase()});
    },
    trim : function(str) {
         return str.replace(/^\s+|\s+$/g, '');
    }
};

For those of you who are new to javascript, this might be hard to understand.
we put all the functions in an object var StringToolBox= {...} in order to avoid conflict with other javascript scripts that could have the same exact function names. Therefore you will call the trim function like so: StringToolBox.trim('   my string     ');
Then we are using Regular Expressions to look for a pattern and change it the way we want.

So far so good!

But what happen if you do no pass the right parameter? If you do not pass anything or for some reasons, in the process an array gets passed??

In order to check that you do get what you want, you will need to add some control on the data itself.
First let's create an helper function (basic):

var DataController = {
    _isString : function(str){
        if(str===undefined) return false;
        if(str.constructor===String)
            return true;
        return false;
    }
};
We will use it this way:

DataController._isString();//should return false
DataController._isString(1);//should return false
DataController._isString("hi!");//should return true

So now let's implement this in our little toolbox:


var StringToolBox = {

    camelize: function(str) {
        if(!DataController._isString(str)) {
             console.log('the first parameter must be a String Object but seen:'+str);
            return false;

        }
        return str.replace(/-(.)/g, function(m, l){return l.toUpperCase()});
    },
    hyphenize : function(str) {
        if(!DataController._isString(str)) {
             console.log('the first parameter must be a String Object but seen:'+str);
            return false;

        }
        return str.replace(/([A-Z])/g, function(m, l){return '-'+l.toLowerCase()});
    },
    firstToUpperCase : function(str) {
        if(!DataController._isString(str)) {
             console.log('the first parameter must be a String Object but seen:'+str);
            return false;

        }
         return str.replace(/^([a-z])/, function(m, l){return l.toUpperCase()});
    },
    trim : function(str) {
        if(!DataController._isString(str)) {
             console.log('the first parameter must be a String Object but seen:'+str);
            return false;

        }
         return str.replace(/^\s+|\s+$/g, '');
    }
};
OK!
As a note, you may decide to handle the error in different ways. Here we just log a message to the console (Firefox will work but this code will fail in IE) but we could throw new Error or do whatever you think is relevant for your application flow.

Now we can be a little bit more confident about our code and where things might get wrong! But as you can see this is rather verbose, we need to copy/paste the same logic in 4 functions, it increases the file size and reduce readability...
If we were in a strongly typed language by defining the type of the first parameter, the interpreter/compiler could handle this for us for free...

But javascript is dynamic enough to allow us to change that!

HIJACK function in use

Before we see how we will build this function, let's see how we use it:

var StringToolBox = {

    camelize: function(str) {
        return str.replace(/-(.)/g, function(m, l){return l.toUpperCase()});
    },
    hyphenize : function(str) {
        return str.replace(/([A-Z])/g, function(m, l){return '-'+l.toLowerCase()});
    },
    firstToUpperCase : function(str) {
         return str.replace(/^([a-z])/, function(m, l){return l.toUpperCase()});
    },
    trim : function(str) {
         return str.replace(/^\s+|\s+$/g, '');
    }
};

DataController.addConstraint(StringToolBox,function(fName,parameters) {

    if(!DataController._isString(parameters[0])){
             console.log('the first parameter must be a String Object but seen:'+parameters[0]);
            return false;
    }
    return true;
});

So as we can see, we moved all the control checking logic outside of the function and
sum it up in one place. Way much less typing (and we do hate typing!), way much less bytes and therefore a smaller file size and better readability!
the addConstraint function takes an object containing the functions as the first parameter and a function that handles the checking. the function itself is a callback that receives the name of the function being hijacked and the parameters it receives.

so now, how do we implement that??

HIJACK function implementation

First of all we need to loop over all the functions in the object, this is going to be easy:

var DataController = {
    addConstraint : function(oPackage, fConstraint) {

        for(var prop in oPackage) {
            DataController.hijack(oPackage,prop,fConstraint);
        }
        return true;
    }
};

the addConstraint just loop through all the elements in the object (oPackage where o stands for object) and call the hijack functions that will get the package, the element being looped over and the callback (fConstraint where f stands for function).
So as you can imagine, the meat is in the hijack function, let's see its implementation:

var DataController = {

    hijack : function(oPackage,fName,fConstraint) {


        //store the original function in a temporary variable, ie, store trim
        var fCode = oPackage[fName];

       //if this is not a function return early, nothing to hijack here
        if(typeof fCode !=='function') return false;


        //redefine the function in the package by overwritting it, ie redefine trim
        oPackage[fName] = function() {

           //every functions have a 'arguments' variable that holds the parameters
           //we just change this into a real array
            var parameters = DataController.toArray(arguments);
          
           //we then use the callback functions that do the controlling
            var ret = fConstraint(fName,parameters);

           //if the callback return false, something went wrong, we do nothing
            if(!ret) return false;

          //the parameter checking was ok so we call our original function, ie trim
            return fCode.apply(oPackage,parameters);
        }
    }
};

Hum, this gets a little bit tricky...
I added comments to help grab the flow.
Basically, we use the very dynamic nature of javascript that allows us to redefine a function and overwrite it.
we first save the function in temporary variable, then we redefine the function by adding the checking callback. if the checking went wrong, we return but if everything was fine, we just call the original function held in the temporary function. This makes use of closure by the way.
the  DataController.toArray(arguments) loops over the arguments and push them into an array. that's all!
the fCode.apply allows us to keep the scope of the package in case the function was using it and allows us to send the parameters back to the original function.
all the functions were using only one argument but there could be several of them. Using apply allows us to solve the problem in one line!

Conclusion

We were able to add some checking onto the parameters sent to javascript functions by hijacking them. It keeps our code clean and reduce the file size.
As we hijack the function, we could easily turn the checking on/off by adding a flag too! Which could come in handy for live vs development environment.

Although the function allows us to check parameters at distance, you still need to know the function and their parameters. Here it was one parameter, all of them being a String. This will not be true for every functions!
If the function changes, you will need to change the checking too.
Could not it be nice if we could check the parameters automagically without even having to know about the funcions themselves??
we will see how we can do this in the next part of this article!

FULL CODE

var DataController = {

    hijack : function(oPackage,fName,fConstraint) {

        var fCode = oPackage[fName];
        if(typeof fCode !=='function') return false;

        oPackage[fName] = function() {
            var parameters = DataController.toArray(arguments);
            var ret = fConstraint(fName,parameters);
            if(!ret) return false;
            return fCode.apply(oPackage,parameters);
        }
    },

    addConstraint : function(oPackage, fConstraint) {
        for(var prop in oPackage) {
            DataController.hijack(oPackage,prop,fConstraint);
        }
        return true;
    },
    toArray : function (arg){

      var ret =[];
      for(var i=0,ln=arg.length;i < ln;i++){
          ret.push(arg);
      }
      return ret;
   },
   _isString : function(str){
        if(str===undefined) return false;
        if(str.constructor===String)
            return true;
        return false;
    }
};

var StringToolBox = {

    camelize: function(str) {
        return str.replace(/-(.)/g, function(m, l){return l.toUpperCase()});
    },
    hyphenize : function(str) {
        return str.replace(/([A-Z])/g, function(m, l){return '-'+l.toLowerCase()});
    },
    firstToUpperCase : function(str) {
         return str.replace(/^([a-z])/, function(m, l){return l.toUpperCase()});
    },
    trim : function(str) {
         return str.replace(/^\s+|\s+$/g, '');
    }
};

DataController.addConstraint(StringToolBox,function(fName,parameters) {

    if(!DataController._isString(parameters[0])){
             console.log('the first parameter must be a String Object but seen:'+parameters[0]);
            return false;
    }
    return true;
});


Monday, December 24, 2007

Javascript Namespaces : Import and Export methods

We have seen how important namespaces are and how to simulate them thru objects. If protecting ourselves and users from collisions is a good thing, writing 4 to 6 names before accessing the desire functions can be a real pain!
we will see how to shortcut things and make our life easier when needed!

Namespaces : the Namespace function


Let's sum up what we have seen in our last entry by taking the example of the JAME namespace (Javascript Animations Made Easy).
We won't put anything directly in the global namespace, that's why we will have to create our first namespace, JAME, without the Namespace function:
JAME = function () {};
JAME = JAME.prototype = function () {};
Now, let's attach the Namespace function onto the JAME namespace.
Let's rename the function to Package, because...
Well, I like Package and it's in the ES4 proposal.
Rename it to whatever you want (I will avoid x345SW2aqefje because it is overused)

JAME.Package = function (sName) {

    //split the name by dots
    var namespaces=sName.split('.') || [sName];
    var nlen=namespaces.length;
       
    var root = window;
    var F    = function() {};

    for(var i=0;i<nlen;i++) {
        var ns = namespaces[i];
        if(typeof(root[ns])==='undefined') {
            root = root[ns] = F;
            root = root.prototype = F;
        }
        else
           root = root[ns];
    }
}

Some packages definition


We have seen how to use it with the utility functions built so far to create the animation function.
Let's see the strings and colors related functions in their package:

  1. JAME.Package('JAME.Util.Color');
  2.  
  3. JAME.Util.Color = {
  4.   rgb2h:function (r,g,b) {
  5.     var Num=JAME.Util.Number;
  6.     return [Num.d2h(r),Num.d2h(g),Num.d2h(b)];
  7.   },
  8.   h2rgb:function (h,e,x) {  
  9.     var Num=JAME.Util.Number;
  10.     return [Num.h2d(h),Num.h2d(e),Num.h2d(x)];
  11.   },
  12.   cssColor2rgb:function (color) {
  13.      if(color.indexOf('rgb')<=-1) {
  14.         return this.hexStr2rgbArray(color);
  15.      }
  16.      return this.rgbStr2rgbArray(color);
  17.   },
  18.   hexStr2rgbArray:function (color) {
  19.      return this.h2rgb(color.substring(1,3),
  20.                        color.substring(3,5),
  21.                        color.substring(5,7)
  22.             );
  23.   },
  24.   rgbStr2rgbArray:function (color) {
  25.      return color.substring(4,color.length-1).split(',');
  26.   }
  27. };

  1. JAME.Package('JAME.Util.String');
  2.  
  3. JAME.Util.String={
  4.   camelize: function(str) {
  5.      return str.replace(/-(.)/g,
  6.                 function(m, l){return l.toUpperCase()}
  7.             );
  8.   },
  9.   hyphenize : function(str) {
  10.      return str.replace(/([A-Z])/g,
  11.                 function(m, l){return '-'+l.toLowerCase()}
  12.             );
  13.   },
  14.   firstToUpperCase : function(str) {
  15.     return str.replace(/^([a-z])/,
  16.                function(m, l){return l.toUpperCase()}
  17.            );
  18.   },
  19.   trim : function(str) {
  20.     return str.replace(/^\s+|\s+$/g, '');
  21.   }
  22. };

How do we use them?
  1. // display backgroundColor
  2. alert(JAME.Util.String.camelize("background-color"));
  3. // display 255,0,255
  4. alert(JAME.Util.Color.hexStr2rgbArray("#FF00FF"));

That's a lot of typing!
We have seen that we could create an alias very easily:
window["camelize"]       = JAME.Util.String.camelize;
window["hex2rgbArray"] = JAME.Util.Color.hex2rgbArray;
Now you can call the camelize and hex2rgbArray directly without having to worry about the namespaces!
But this is still quite cumbersome to type this! And if you want to import everything in your namespace?
Let's build a function that will allow to import everything by just specifying the desired namespace then!

The Import method


Let's see how we would like to use it first:
JAME.Import(JAME.Util.String,JAME.Util.Color,JAME.Util.Number);
So we will just specify the namespace we need and as many as we wish.
In order to allow as many parameters as needed, we are going to use the arguments keyword.
By the way, the arguments keyword is not a real array in that you cannot push, slice....

  1. JAME.Import = function () {
  2.  
  3.     //get the number of parameters
  4.     var nlen=arguments.length;
  5.  
  6.     //loop thru each package/namespace
  7.     for(var i=0;i<nlen;i++) {
  8.  
  9.         // get the actual package
  10.         var package=arguments[i];
  11.  
  12.         //test the existence of an Export method
  13.         if(package.Export) {
  14.  
  15.             //call the Export function
  16.             package.Export();
  17.         }
  18.     }
  19. }

The definition of this function is not very hard.
We just loop thru each package and test for the Export method.
But what is this Export method??

Need some privacy!


The Export function should be defined within the packages and should define what functions can be exported as their exported name.
It is easier and safier to let the package creator defines what he/she thinks can be exported safely.
A function name can be perfectly safe within the boundaries of a package but could be too obvious once in the global scope.
Therefore, renaming the function in order to avoid conflicts by prefixing it should be up to the package author.
So let's see the Export function within the string package :
JAME.Package('JAME.Util.String');

JAME.Util.String={
    camelize: function(str) {
        return str.replace(/-(.)/g,
               function(m, l){return l.toUpperCase()});
    },
    hyphenize : function(str) {
        return str.replace(/([A-Z])/g,
               function(m, l){return '-'+l.toLowerCase()});
    },
    firstToUpperCase : function(str) {
        return str.replace(/^([a-z])/,
               function(m, l){return l.toUpperCase()});
    },
    trim : function(str) {
        return str.replace(/^\s+|\s+$/g, '');
    },
    Export : function() {
        for(var method in this) {
           if(method==="Export") continue;
          window[method]=this[method];
        }    
   }
};
As you can see, in that case, it was pretty straightforward as we just loop thru each method in the string package and alias them onto the window object.
Obviously, we skip the Export method that is internal to the package!
But let's say that you want to rename the trim function to JStrim in order to avoid conflict? You will have to verify if it is the trim method then change it's naming.
Or let's say that you just want to export 2 functions among 5 or 6 functions?
In order to make lifer easier and keep things DRY, we are going to create an Exporter function that will ease the export process!

The Exporter method


Let's see its definition first:
JAME.Exporter = function (oName,sPref) {

    if(!sPref) sPref='';

    for(var method in oName) {
        if(method==="Export"
           || /^_.+/.test(method))
            continue;
        window[sPref+method]=oName[method];
    }
}

The function accepts two parameters:
an object that can be 'this' to export everything or a literal object defining the exported function name and its original version with the package path.
And a string that defines a prefixe to put on the exported function name.
An example will help:
JAME.Util.String= {
...
   _mymethod : function() {...} // will not be exported
   Export : function() {

       JAME.Exporter(this,'JS');
       //or 
       JAME.Exporter({"hyphenize":JAME.Util.String.hyphenize},"JS");
   }
}
As you can see in the exporter function, we skip the Export function and all methods or variables that start with an underscore.
This is just a convention used in many languages to specify private variables.
Even if it is possible to simulate private variables and methods in javascript, thisw won't be the case here, unless you wrap all the code within an other function.
Beware that we do not check against variables or functions already defined in the global namespace, which means that these functions will be overwritten!
It should be easy to add a flag to set it to overwrite mode or not.

The final script


And here we are with a basic namespace simulation and exporting/importing process.
Here is the code that recaps everything seen:
JAME = function () {};
JAME = JAME.prototype = function () {};

JAME.Package = function (sName) {


    var namespaces=sName.split('.') || [sName];
    var nlen=namespaces.length;
       
    var root = window;
    var F    = function() {};

    for(var i=0;i<nlen;i++) {
        var ns = namespaces[i];
        if(typeof(root[ns])==='undefined') {
            root = root[ns] = F;
            root = root.prototype = F;
        }
        else
           root = root[ns];
    }
}

JAME.Import = function () {
     var nlen=arguments.length;

     for(var i=0;i<nlen;i++) {

         var package=arguments[i];

         if(package.Export) {
               package.Export();
         }
     }
}

JAME.Exporter = function (oName,sPref) {

     if(!sPref) sPref='';

     for(var method in oName) {
         if(method==="Export"
            || /^_.+/.test(method))
            continue;
        window[sPref+method]=oName[method];
     }
}

JAME.Export = function () {
    JAME.Exporter({ "Package" : JAME.Package,
                    "Import"  : JAME.Import});
}


Conclusion


These 3 entries allowed us to experiment with namespaces in javascript!
Understanding their importances, object simulation advantages and creating functions to simplify our code and make it cleaner, organised have been seen.
Obviously, this is just a starter and many features could be added or improved.
If you have some ideas, don't hesitate to post them out!
The extension thru inheritance is not assumed here and should be developed though.
If you are interested in packages, namespaces way of managing your code, you can look at some other libraries that offer ways to deal with that:
-AJILE : library offering many import, include,namespaces functions.
-JSAN : a very good project idea that I invite you to collaborate to in anyway you can!
Happy new year!

Monday, December 3, 2007

Javascript : Simulating namespaces thru prototyping part 2

In the first part, we have seen how we could simulate namespaces very basically with one function used as a container.
We have seen that namespaces allow to create clean clode, that do not interfere or polluate the global space too much.
We will see here how to extend the namespace simulation thru a simple Namespace function

Namespace thru prototype


We have seen that a simple function used as a namespace could give us a lot of advantages but the drawback was that the main function,used as a namespace, wrapped all the other functions.
And so what?
It won't have too much overhead if this function is used only once in your application, but if you 'instantiate' the 'class' several times and that it contains hundreds of line, these hundreds of lines will be kept in memory several times.
In order to avoid this, we are going to use the prototype keyword.
Every functions get a 'prototype' attached to them to which you can add further functions.
Let's see the example we used in the first entry to see what's going on :
function Arrays() {
    this.toArray = function(elm){
         var tmpArray=[];
         for(var i=0;i < elm.length;i++){
            tmpArray.push(elm[i]);
         }
         return tmpArray;
    },
    this.search = function(elm,search) {
          for(var i=0;i < elm.length;i++){
              if(elm[i]===search) return i;
          }
          return false;
    }
}
Arrays = new Arrays();
alert(Arrays.toSource());#won't work in IE.
The above code create a 'class' containing all the functions.
We then instantiate an object and see what it contains thru the toSource function.
Executing this code, you will see that everything defined within the function Arrays, used as a namespace, will be present in the object.
If you were to create several instances, each instance we hold the entire computation.
Even if you only instantiate it once,you may not need the search function though.
Here comes the prototype!
The prototype allows us to attach functions afterwards.
Let's see how it works :
function Arrays() {
    this.toArray = function(elm){
         var tmpArray=[];
         for(var i=0;i < elm.length;i++){
            tmpArray.push(elm[i]);
         }
         return tmpArray;
    }
}
oArrays= new Arrays();
alert(oArrays.toSource());

Arrays.prototype = {
    search : function(elm,search) {
          for(var i=0;i < elm.length;i++){
              if(elm[i]===search) return i;
          }
          return false;
    }
};

alert(oArrays.toSource());
alert(oArrays.search(['bonjour','aurevoir','hi'],'hi')); 
#error, function search not defined
If you execute the code, you will see that the instance oArrays of Arrays only contains the toArray function and not search function.
You also see that adding the search function to the prototype of the Arrays function doesn't change what is contained in the oArrays instance.
But on the other hand, you see that the oArrays instance doesn't contain the search function at all and will throw an error when trying to call it!

How work the prototype?

In the above code, we have done the thing in this order:
1) define the namespace Arrays with one function
2) create an instance of the Arrays 'class' called oArrays
3) Add a new object containing the definition of the search function to the prototype
of Arrays
4) call the search function on the oArrays instance
There is one thing to remember about the prototype keyword and instantiation:
only defined prototype functions will be available to the instance.
In other word, if you call the Arrays function,create an instance of it and then define a new prototype to it, the prototype won't be available to your instance, here oArrays.
Here is how we should have done to make it available to the oArrays object:
1) define the namespace Arrays with one function
3) Add a new object containing the definition of the search function to the prototype
of Arrays
2) create an instance of the Arrays 'class' called oArrays
4) call the search function on the oArrays instance
And now, you will be able to use the search function!
Why ? Basically, your program is going to be analized in a top-bottom process.
Nothing really different form other languages! But everything happens at run-time, which means that when you instantiate the class, the object will contain the information only available at that time!(only the code that has been parsed- simplified explanation).
So let's rewrite the code so that we can access what we need:
function Arrays() {
    this.toArray = function(elm){
         var tmpArray=[];
         for(var i=0;i < elm.length;i++){
            tmpArray.push(elm[i]);
         }
         return tmpArray;
    }
}
oArrays= new Arrays();
alert(oArrays.toSource());

Arrays.prototype = {
    search : function(elm,search) {
          for(var i=0;i < elm.length;i++){
              if(elm[i]===search) return i;
          }
          return false;
    }
};
oArrays2= new Arrays();#re-instantiate the class here!
alert(oArrays2.toSource()); #same result as oArrays, just the toArray definition
alert(oArrays2.search(['bonjour','aurevoir','hi'],'hi')); #return 2

As you can see by creating a new object, oArrays2, after the prototype definition, you will be able to use the search function!
Even more!
You see that the instance, oArrays and oArrays2 just hold the toArray function!
In fact, by default, when calling a function on an instance (call a method in that case), javascript will try to see if it can find the definition within the prototype and goes back to all the prototype chain if necessary!
Thanks to the prototype chain, we can now create a simple empty namespace, ie a function and then attach the functions thru the prototype!
You will be then able to load only what you really need!

How to use namespaces : Choosing the root


In javascript, there is no real standard defining all the libraries and namespaces in use (like CPAN for perl - we will talk about jsan later).
In order to be sure that your functions and libraries are not going to polluate the global space too much , or enter in conflict with an other library, you should place everthing into a base namespace.
This namespace can be seen as the root of the tree or as the root of folders on your hard drive or an id, like an IP address.
We have been developping an animation function that had many helper functions in the global namespace.
We will then use the namespace concept to clean this out and play nice with other libraries!
The hardest thing to do is to find the root name, it should be very unique and we should make it not too evident!
Alright, we are going to use JAME, which is a name that I have found right now, so don't be to hard on me!
This stands for : Javascript Animations Made Easy
As you can see, this is not a too evident name like Animation, Tween...
As far as you are sure that your root namespace is not too evident, then you can always do:
JAME.Util.Strings
JAME.Util.Arrays
These are evident names, almost standards, but they are all attached to the JAME root namespace that protects them!

The Root Namespace by hand


We have seen that JAME is going to be the root of our namespace tree that is why it is not necessary to have any real definitions into it, we should see it as a place holder.
We can create the namespace very easily then:
JAME = function() {};
And here we are!
But we also know that we will need to extend the JAME namespace thru its prototype and not directly as we want to be able to load only what we need.
Therefore, we shoud do it this way:
JAME = function() {};
JAME = JAME.prototype=function () {};
Now any elements add to JAME will be added to the prototype.
We want to create an other namespace within JAME that will contain all the helper functions we've been using so far.
Therefore,we will add the Util namespace, that is an other place older.
So we just create an empty namespace here too.
JAME.Util = function() {};
JAME.Util = JAME.Util.prototype= function() {};
And now we have the JAME.Util namespace.
But as you can see this is quite a lot of typo!
We are going to create a function that will deal with creating namespaces for us.

The Namespace function


Before we define the function, we will see how to use it:
Namespace("JAME.Util.Strings");
As you can see this is pretty straightforward!
We just write a string containing each namespace seperated by a dot.
Let's see how to create a one depth namespacing function, that is just going to wrap what we have done above:
function Namespace(name) {
    window[name]=function() {};
    window[name]=window[name].prototype=function() {};
}
So now you can do:
Namespace("JAME");
JAME.Util=function() {};
We just put the name onto the window object and then create the shortcut to work directly with the prototype of the name.
If we follow the above namespace, in order to add a namespace of depth 2, we should write:
function Namespace(name1,name2) {
    window[name1]=function() {};
    window[name1]=window[name1].prototype=function() {};
    window[name1][name2]=function () {};
    window[name1][name2]=window[name1][name2].prototype=function() {};
}
you can see that we can add the depth thru [][].
If we had 3 elements, it will be [][][].
Let's create a general function that will deal with n depth elements.

function Namespace (sName) {

 var namespaces = sName.split('.') || [sName];
 var nlen = namespaces.length;
 var root = window;
 var F = function() {};

 for(var i=0; i < nlen; i++) {
  var ns = namespaces[i];
  if(typeof(root[ns])==='undefined') {
   root = root[ns] = F;
   root = root.prototype = F;
  }
  else
   root = root[ns];
 }
}
What's going on?
First we split our string by dots or put the sole name in an array.
Then we create an alias to the window object.
Then we loop thru each namespaces.
We first check that the namespace doesn't already exist.
If not, we assign F to the root and then assign the prototype to the root again.
If we do it step by step:
First loop root = window so:
root = window[namespace[0]] = F; window[root] = root.prototype = F; Then second loop:
root = root[namespace[1]] = F; window[root] = root.prototype = F; ...
And now you can create namespaces very easily!
Namespace("JAME.Util.Strings");
Namespace("JAME.Util.Colors");
Namespace("JAME.Util.Numbers");
Let's see all the helper functions we have seen so far:
function camelize(str) {
  return str.replace(/-(.)/g, function(m, l){return l.toUpperCase()});
}
function hyphenize (str) {
  return str.replace(/([A-Z])/g, function(m, l){return '-'+l.toLowerCase()});
}
function firstToUpperCase(str) {
  return str.replace(/^([a-z])/, function(m, l){return l.toUpperCase()});
 }
function trim(str) {
  return str.replace(/^\s+|\s+$/g, '');
}
These are all the helper functions related to strings we've been using so far to create the animation functions.
We are now, just going to put this into the JAME.Util.Strings namespace :
//in the Util folder, we add Strings.js 

//we define the namespace
Namespace("JAME.Util.Strings");


JAME.Util.Strings={
camelize: function(str) {
   return str.replace(/-(.)/g, function(m, l){return l.toUpperCase()});
},
hyphenize : function(str) {
  return str.replace(/([A-Z])/g, function(m, l){return '-'+l.toLowerCase()});
},
firstToUpperCase : function(str) {
  return str.replace(/^([a-z])/, function(m, l){return l.toUpperCase()});
},
trim : function(str) {
   return str.replace(/^\s+|\s+$/g, '');
}
};
Now let's define the colors and numbers:
//in the Util folder, we add Numbers.js 

//we define the namespace
Namespace("JAME.Util.Numbers");

JAME.Util.Number= {
    d2h : function(dec) { 
       return dec.toString(16);
    },
    h2d :function(hex) { 
       return parseInt(hex,16);
   },
   randomize : function(min,max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
   }
};
And the colors:
Package('JAME.Util.Colors');

JAME.Util.Colors = {
rgb2h:function (r,g,b) { 
    return [JAME.Util.Numbers.d2h(r),JAME.Util.Numbers.d2h(g),JAME.Util.Numbers.d2h(b)];
},
h2rgb:function (h,e,x) {  
     return [JAME.Util.Numbers.h2d(h),JAME.Util.Numbers.h2d(e),JAME.Util.Numbers.h2d(x)];
},
cssColor2rgb:function (color) {
if(color.indexOf('rgb')<=-1) {
   return this.hexStr2rgbArray(color);
}
   return this.rgbStr2rgbArray(color);
},
hexStr2rgbArray:function (color) {
return this.h2rgb(color.substring(1,3),color.substring(3,5),color.substring(5,7));
},
rgbStr2rgbArray:function (color) {
return color.substring(4,color.length-1).split(',');
}

};
We are not going to see them all for now but I guess you got the picture!
Now you can use them this way:
JAME.Util.Strings.trim(str);
So you may think, and you are right, that it is a lot of typo!
Indeed it is but you should not decide for the end user what to put in the global space, the end user can do it very easily:
window["trim"]=JAME.Util.Strings.trim;
trim(str);
The user can give it an other namespace if he/she wants!
It's up to the context they're in!
You should avoid functions/variables in the global namespace.
If you use the global namespace, be sure to make a list available to the end user!

Conclusion


We have seen how to create a very basic Namespace function (note namespace and package are reserved words so you can choose either Namespace or Package and be sure it will work).
This allow us to keep our code clean, structured and if we think of each namespaces as being folders as we have been doing above, we will be able to automaticly create a javascript file containing only the contents you have choosen!
We can import everything in the global namespace or rename the namespace very easily but we need to offer a tool to import namespaces.
Inheritance within the namespace hierarchy is not handled yet.

We will see in the next entry how to create an Import function that we'll allow to import functions by controlling what's going on(what if you have split in Arrays and split in Strings and that you import both in the global namespace??)

Sunday, December 2, 2007

Javascript : Simulating namespaces basic approach, part 1

we have seen in javascript scoping : things to keep in mind, that the window object was the global space englobing everything from variables to functions.
We have also seen that we could create private scoped variables and functions. Due to the 'last in, win' policy, we have also specified that we should avoid putting everything in the global space as it could lead to unexpected bugs.
We will see in this entry what are namespaces and how to simulate them in javascript.

Namespaces ?

We have seen that the window object was the global space.
We could also say that window is the name of a space, that happens to be the global one.
It doesn't really help?
Ok, in order to explain the namespace concept we are going to take an everyday life example:
You are in charge of two sports club named respectivly, Football and Basketball.
Every players receive an id and each ids are not related to each club.
If somebody came to you asking for the player 340, you will not be able to know which player it is.
if the same person asked for Football 340, you will know where to go.
Football and Basketball are therefore the names that represent a space in which you keep some related information.
the id 340 in the Football namespace doesn't represent the same player with the same id in the Basketball namespace but they do represent a person.

You could think of folders in your computer:

/usr/home/Football

and an other folder:

/usr/home/Basketball

Each of them could have a file called list.osd.
You won't have any problem as long as the files are located in different folders.
You can think of folders and their path, as namespaces too!
This is so true to life that some languages, like perl, translate packages (a way to create namespaces) into folder path!
we will see that it is a good idea and use it too in the next entry!

Namespace for what?

As you can see namespaces are a way to keep information in compartimented slot.
you can have the same id, the same variable or function names if you want:they won't run into each other as long as they are in their related namespace.
In javascript, if you don't specify anything, your variable or function will be attached to the window namespace.
If you load two libraries that define the same function name but that they don't do the same thing, it will be very hard to determine where the problem is. You might not even know that there is such a function and wonder why you don't get what you expect.

Let's see a stupid example:
// somewhere in your own set of functions
// take a pseudo array and turn it into a real javascript array
// something you could get from a DOM function
function toArray(elm) {
     var tmpArray=[];
     for(var i=0;i< elm.length;i++){
           tmpArray.push(elm[i]);
     }
     return tmpArray;
}

//somewhere in an other library, internal use:
//change a string into an array
function toArray(str) {
     return str.split();
}
Well,,, I guess you get the picture!
If you have several libraries and that you don't protect yourself from some global space pollution that may occur, you're good for hours of debugging!

Using namespaces is also a very good way to keep your code clean and organized!
Here we had Football and Basketball but in programming you could have Utilities.Strings, Utilities.Arrays,CSS.Border...
Naming and structuring your code in sub parts will allow to track easily bugs and make your life easier!
let's see how we can simulate a basic namespace.

First approach : prefix


A very easy way to simulate namespace is to simply prefix all your functions. This is a method that you can often find in language that do not support namespaces, like php for example:
__MyLibraryToArray();
Here you will prefix all your functions with '__MyLibrary'.
To some extend, this is a very basic and simple way to simulate namespace.
clash risks are decreased and one can easily see where the function comes from.
But this can start to be very cumbersome when your function names reach 30 letters length!
__MyLibraryUtilElementsToArray();
If you use this method, you are sure to type a lot and if you want to change the name..., no, you don't change your name!!

We are therefore going to see how we can make things a little bit simpler and more expandable with javascript objects!

Second Approach : functions as objects


We have seen in javascript scoping : things to keep in mind that functions allow to scope variables and other functions within them.
We could then define some private functions not accessible from outside of the function and public methods that we could call on the function name:
function Elements() {

    this.toArray = function(elm){
         var tmpArray=[];
         for(var i=0;i< elm.length;i++){
            tmpArray.push(elm[i]);
         }
         return tmpArray;
    }
}
//example of use:
var Elements = new Elements();
Elements.toArray(elm);
Let's say now that you want to put several functions that will deal with arrays, you can just rename the function to keep things coherent:
function Arrays() {

    this.toArray = function(elm){
         var tmpArray=[];
         for(var i=0;i< elm.length;i++){
            tmpArray.push(elm[i]);
         }
         return tmpArray;
    }
    this.search = function(elm,search) {
          for(var i=0;i< elm.length;i++){
              if(elm[i]===search) return i;
          }
          return false;
    }
}

//example of use:
var Arrays= new Arrays();
Arrays.toArray(elm);
Arrays.search(elm2,'hi');

but how do you do with all the code that refers to Elements.toArray??
Well, using functions to create namespaces is very powerful as you just need to create an alias that joins the old namespace with the new one:
Elements=Arrays;
And you can know create legacy code if necessary with just one line!
Something that simple prefixing will not allow you.

You could even be lazy and because you know that you are not going to use some other libraries, you want to access the function directly:
var Arrays=new Arrays();
window['search']=Arrays.search;
//now you can call it directly:
search(elm,'hi');
Here again, this shows how powerful and useful namespaces can be!

Conclusion

We have seen a very basic way of simulating namespaces in javascript thru a simple function used as a container for several related functions.
Using namespaces allow to make your code clean and organized but yet flexible as we have seen above!
This obviously allows you to protect your code or people using your code from global space pollution which can be very handy when dealing with several libraries!
Obviously, the above method is far to be the best one as we have put everything in one function.
Each time you will call the function or someone does it, all the computation within the function will be kept in memory. If you are sure that you will call the namespace just once, that can be ok but you never know!
We will see in an other entry how we can extend our namespaces functionnalities with objects!