Friday, October 31, 2008

Viewport, Column Container and nested layouts

I have added a viewport class and a column container class to JX. Please read my earlier posts if you havent.

The viewport takes over the document body element. So you can have only one viewport instance in your application. If you have content in your body element the viewport will hide it. So it is unobtrusive also. Clients with no javascript will see your content. Viewport extends JX.Container so lays out its components vertically.

The column container works similar to the container in my previous post except that it lays out its components horizontally. You can set fitWidth: true to one of its components and the component width will expand to the remaining width of the parent minus its sibling widths.

You can nest the containers within each other and create complex layouts. I will create a a border layout using the viewport and column container. The border layout will resize accordingly when you resize your browser.

Here is the code to create the border layout.
$(document).ready(function() {
    var viewport = new JX.Viewport({
        css: {padding: '0px', margin: '0px'},
        items: [{
            height: 50,
            css: {backgroundColor: '#aaaaaa', padding: '20px'},
            text: $('#north').text(),
            fitWidth: true
        },{
            jxtype: 'columncontainer',
            fitHeight: true,
            items: [{
                text: $('#east').text(),
                width: 150,
                css: {backgroundColor: '#cccccc', padding: '20px'},
                fitHeight: true
            },{
                text: $('#center').text(),
                fitWidth: true,
                css: {backgroundColor: '#eeeeee', padding: '20px', overflow: 'hidden'},
                fitHeight: true
            },{
                text: $('#west').text(),
                width: 150,
                css: {backgroundColor: '#cccccc', padding: '20px'},
                fitHeight: true
            }]
        },{
            height: 50,
            css: {backgroundColor: '#aaaaaa', padding: '20px'},
            text: $('#south').text(),
            fitWidth: true
        }]
    });
});                               


I have added a new jxtype 'columncontainer' for column containers. Other jxtypes i have added are 'container' and 'component'. You need not specify jxtype if you are creating the component with 'new'. Also default is component ('div').


Thursday, October 30, 2008

A jQuery Container Class

Further to my previous post I have created a container class called JX.Container. The container lays out components vertically just like you would if you append. However it has a few extra features.
In the config options it has a 'items' config which is an array of components or component configs.
You can set 'fitWidth' to each component and the component expand to the width of the container. You have to also call doLayout() on the container, this is required because of 'fitWidth'.
We will use the container class to create a login box. Here is the code.
$(document).ready(function() {
    var loginbox = new JX.Container({
        width: 200,
        css:{
            background: 'lightcyan',
            border: '1px solid darkblue',
            padding: '20px',
            fontSize: '12px',
            fontFamily: 'Arial, Helvetica',
            fontWeight: 'bold',
            color: 'darkblue'
        },
        appendTo: document.body,
        items: [{
            text: 'Enter your User Name'
        },{
            jxtype: 'input',
            attr: {type: 'text'},
            fitWidth: true
        },{
            text: 'Enter your Password'
        },{
            jxtype: 'input',
            attr: {type: 'password'},
            fitWidth: true
        },{
            jxtype: 'input',
            attr: {type: 'button', value: 'Login'}
        }]
    });
    loginbox.doLayout();
});                               

Wednesday, October 29, 2008

Configurable jQuery Components

I wanted to create jQuery components easily by passing in config options, that would also take in jQuery method parameters in the config option. You have to read my previous post to understand what is going on here. First I will show you how it works. In the code below I create a button object by passing config options to JX.Component.
$(document).ready(function() {
    var mybutton = new JX.Component({
        jxtype: 'div',
        text: 'Click Me',
        width: 100,
        css:{background: 'darkblue', color: 'lightblue', textAlign: 'center'},
        appendTo: document.body,
        click: function() {
            alert("You clicked a button with text: " + $(this).text());
        },
        hover: [
            function() {
                $(this).css({cursor: 'pointer', opacity: '0.5'})
            },
            function() {
                $(this).css({cursor: 'default', opacity: '1'})
            }
        ]
    });
});                               

You will notice that all config options except jxtype are actually jQuery method names whose values are the parameters to the jQuery method. jxtype tells the component what type of dom element to create. If you dont specify jxtype default is 'div'. Also note the the 'hover' config option takes in an array as its value. So where ever the corresponding jQuery method takes more than one argument you must use an array here.
Here is the code for the new JX.Component.
JX.Component = function() {
    if (JX.isObject(arguments[0])) {
        var config = arguments[0];
        config.jxtype = config.jxtype ? config.jxtype : 'div'; // default type is div
        JX.Component.superclass.init.call(this, document.createElement(config.jxtype));
        this.applyConfig(config);
    } else
        JX.Component.superclass.init.apply(this, arguments);
};

JX.extend(JX.Component, jQuery, {
    applyConfig: function(config) {
        for (var key in config) {
            var a = JX.isArray(config[key]) ? config[key] : [config[key]];
            eval('this.' + key + '.apply(this, a)');
        };
    },
    jxtype: function(jxtype) { 
        this._jxtype = jxtype;
    }
});

To make reusable components you can create a factory function like this.
function buttonFactory(config) {
 var buttonconfig = jQuery.extend({
        jxtype: 'div',
        width: 100,
        appendTo: document.body,
        click: function() {
            alert("You clicked a button with text: " + $(this).text());
        },
        hover: [
            function() {
                $(this).css({cursor: 'pointer', opacity: '0.5'})
            },
            function() {
                $(this).css({cursor: 'default', opacity: '1'})
            }
        ]
 }, config);
 return new JX.Component(buttonconfig);
};

$(document).ready(function() {
    var mybutton = buttonFactory({
        text: 'Click Me',
        css:{background: 'darkblue', color: 'lightblue', textAlign: 'center'},
    });
});


Monday, October 27, 2008

Extending jQuery the Object Oriented Way

Though I have used quite a few Ajax libraries in the last couple of years, nothing has impressed me like jQuery. However jQuery is not object oriented in the traditional sence. You cannot instantiate a jQuery object like this
var myobject = new jQuery();

or extend jQuery like this.
extend(MyClass, jQuery);

So what if you could object orientify jQuery? Imagine all your classes/widgets as extensions of jQuery. You could call all the jQuery functions from within your own 'this' like
this.addClass('classname');
this.click(function() {alert('you clicked me')});

But then you cannot extend jQuery because it does not have a constructor! It is itself an object. But thats where javascript comes to your rescue. Javascript does not differentiate an object from a class. And the jQuery object has some features that will come as a help. It has a prototype object and though it does not have a constructor it does have an init method. Some of you are already figuring where I am headed. But before we go ahead we need a function that will do a proper object oriented extend. And I will also add a namespace and call it "JX" for jQuery Extend. Here is the code.
var JX = {
    extend: function(bc, sc, o) {
        var f = function() {};
        f.prototype = sc.prototype;
        bc.prototype = new f();
        bc.prototype.constructor = bc;
        bc.superclass = sc.prototype;
        for (var m in o)
            bc.prototype[m] = o[m];
    }
};

JX.extend() is a function that will take in three parameters, a baseclass constructor, a superclass constructor and an object of functions to override any superclass methods. Now let us use this method to create a new Class called JX.Component that will be a base class for all our widgets.
JX.Component = function() {
JX.Component.superclass.init.apply(this, arguments);
};
JX.extend(JX.Component, jQuery, {});

The arguments passed can be any argument you pass into the jQuery $() function. Voila! You have a class that extends jQuery!
Now try this.

$(document).ready(function() {
    var mydiv = new JX.Component(document.createElement('div'));
    mydiv.text("Hello World").click(function(){alert("You Clicked Me!")});
    mydiv.appendTo(document.body);
);

Now let us get down to something more usefull. Let us extend JX.Component to make a button class.

JX.Button = function() {
JX.Button.superclass.constructor.apply(this, arguments);
this.initialize();
};
JX.extend(JX.Button, JX.Component, {
    initialize: function() {
        var component = this;
        this.hover(
            function() {
                component.css({cursor: 'pointer', opacity: '0.5'})
            },
            function() {
                component.css({cursor: 'default', opacity: '1'})
           }
        );
        this.click(function() {
            alert("You clicked a button with text: " +component.text());
        });
    }
});

Now try your button class with this code.

$(document).ready(function() {
    var mybutton = new JX.Button(document.createElement('div'))
        .text("Click Me")
        .css({background: 'darkblue', color: 'lightblue', textAlign: 'center', width: '100px'})
        .appendTo(document.body);
});    

If you noticed the Button constructor called its "superclass.constructor". However JX.Component when it extends jQuery calls its "superclass.init". And thats the trick in extending jQuery.