Tuesday, October 26, 2010

image height, width with Chrome

If one wants to get the height and width of an image, the simple syntax would be;

var img = document.getElementById("image");
alert("width = "+img.width);
alert("height = "+img.height);

now suppose, i am executing the above javascript code ondomready event. It will work perfectly fine with Opera, firefox and even with IE, however, this won't work under Chrome. The reason is simple, Chrome fires domready event before loading the images. So if you place some images in a html page and some text, you'll see that domready event is fired as soon as text is loaded. Now, since the images are not loaded yet, the above javascript code will return 0 in case of both height and width. The solution is simple, in order to make sure that the above code works under Chrome, execute the code on window.load. Hence, in mootools code, following won't work under chrome (it will display 0 for both height and width);

window.addEvent('domready', function(){
        alert("(in domready)width = "+$('image').getStyle('width') + " height = "+ $('image').getStyle('height'));
       //alert("(in domready)width = "+$('image').width + " height = "+ $('image').height);
    });

The following code will work under Chrome i.e. giving proper height and width of the element;

window.addEvent('load', function(){
        alert("(in load)width = "+$('image').getStyle('width') + " height = "+ $('image').getStyle('height'));
        //alert("(in load)width = "+$('image').width + " height = "+ $('image').height);
    });   

mooPager... a mootools based generic js pagination script

Almost on every web related project, there is a need for displaying data in pages. So, i thought of writing a simple yet generic pagination script which can apply pagination on any type of data including div, images etc.

checkout the demo at following url;

http://saadnawaz.github.com/mooPager/

The source can be downloaded from http://github.com/saadnawaz/mooPager/

Mootools using Events in Class

Following is a simple tutorial on how to use events in a mootools class. Mootools class provide a method to use events in your class. You can simply do that by using the "Implements: Events" in your class declaration. Hence, for new class it should be like following;

var myClass = new Class ({
              Implements: Events,
              initialize: function(elements){
                         //your code
              }
});

and if you want to add the support for events in an existing class, you can use the following syntax;

myClass.implement(Events);

Now, once we have added the support for the events in the class, it's time to see how to add and fire event from a class. Let's assume there is a div with id "el" and there are multiple divs inside this particular div. At the time being, we just want to hook an event on this div "el" and we want the function to fire when mouse is clicked inside the div.

var clicked = function(){
         alert("here");
}
var myClass = new Class ({
              Implements: Events,
              initialize: function(elements){
                         //your code
                         $('el').addEvent('click', this.click.bind(this));

              },
              click: function(){
                       this.fireEvent('click');
              }
});

window.addEvent('domready', function(){
    var obj = new myClass();
    obj.addEvent('click', clicked);
});

the function fireEvent fires all events of the specified type in the Class instance, in our case, it will fire all the events hooked on click. When you run the above code, you'll see that whenever you click inside the "el" div, the function is clicked is called.
Now, let's say we want to change this a bit. We want to get the id of the inner div on which the mouse is clicked. Now, remember we have event delegation, so we don't need to go through each and every element to hook that event. Consider the following code;


var clicked = function(myObj){
     alert("You clicked on div with id = "+myObj.id);
    }
    
    var myClass = new Class ({
          Implements: Events,
          initialize: function(elements){
                     //your code
                     $('el').addEvent('click:relay(div)', this.click.bind(this));
          },
    
          click: function(ev, divObj){
                   this.fireEvent('click', divObj);
          }

});

window.addEvent('domready', function(){
    var obj = new myClass();
    obj.addEvent('click', clicked);
});

Now, in above method, we have passed parameter divObj (the object of the div on which mouse is clicked) through fireEvent. if you want to pass more than one parameters to a function through fireEvent, you'll have to use array i.e. this.fireEvent('click', [arg1, arg2]);. When the above code is executed, it displays the id of the div on which mouse was clicked. See the above script in action on the following url;

 http://jsfiddle.net/vCYmk/1/

Monday, October 25, 2010

How to write a Mootools class

Following is a very nice and detailed article on how to write a mootools class. A very good article for beginners.

http://mootorial.com/wiki/mootorial/09-howtowriteamootoolsclass

Thursday, October 21, 2010

Applying same style on multiple elements in mootool

I have been lately trying to see how to apply same style on multiple elements using mootools. Now, mootools does provide a method for that known as Fx.Elements. Now, if you want to apply only 1 style, use tween on Fx.Elements and if you want to change multiple styles, then use morph. The issue is if you visit the official documentation here you will see that it requires you to know the total number of elements and you have to mention the index number as well. However, what if you want to apply same effect on all the elements and you don't know how many elements there are. The method to do that is fairly simple. Use the following code;

$$('.simpleBox').morph({
height: 50,
opacity: 0.3
});



where simpleBox is the name of the class which is applied on all the elements on which you wanna apply the style/effect. See the following url to see it in action;

http://www.jsfiddle.net/xwUFN/2/

Friday, October 15, 2010

Event Delegation with Mootools

Ever stuck in a situation where you have hundreds of elements and you want to execute same function for each one of them on some event? One possible solution to that approach is simply going through each and every element and hooking the event. However, that is low in performance and doesn't look that elegant. Consider a simple scenerio, there are a lot of images on your page and you want to apply a simple effect that when the mouse is moved on any image, the opacity of the image should be set to 1 and when the mouse is taken out, the opacity is set to 0.4. Let's say that the image is contained in a div with the id "parentDiv". Consider the following code to achieve in a traditional way;

var increaseOpac = function(image){
     image.set({'opacity': 1});
};

var decreaseOpac = function(image){
     image.set({'opacity': 0.4});
};

$$('#parentDiv > img').each(function(img, index){
   img.addEvent('mouseover', increaseOpac.bind(this, img));
   img.addEvent('mouseout', decreaseOpac.bind(this, img));
} );

The above code will work like a charm, however, it's not a good way to do it. This method makes the browser keep track of a lot of work. And if you want to add/remove elements dynamically, then you'll have to hook event on the newly created element as well. Hence, we should go for Event delegation. With event delegation you simply need to add the event on the parent element and it'll delegate it down to it's children. Now, even if you add new element dynamically, since it'll be under the same parent, the event will be automatically apply on it as well. Let's try to achieve the same thing with event delegation;


var increaseOpac = function(event, image){
     image.set({'opacity': 1});
};

var decreaseOpac = function(event, image){
     image.set({'opacity': 0.4});
};

window.addEvent('domready', function(){
    $('parentDiv').addEvent('mouseover:relay(img)', increaseOpac);
    $('parentDiv').addEvent('mouseout:relay(img)', decreaseOpac);
}); 

Now, with event delegation, when the mouse will be moved on any image inside the parentDiv, the event will be fired, however, it will be relayed to the matching selector, which in our case is "img". Another interesting thing is that we don't need to bind any value, the object of the image on which the mouse is moved is sent to the event handler automatically.

For event delegation in mootools, you'll need to download "mootools more" along with mootools core.

Thursday, October 14, 2010

Mootools Function.Bind

There are alot of cases when you need to pass some value to the function you have hooked on an event. For example, consider there is an image. you want to hook an event on the image that when the mouse is moved on the image, a value is passed to the function by which the opacity of the image should be increased. Consider the following code for this;

var opacVal = 0.9;
$('someImage').addEvent('mousemove', 
                        function(element, opacVal){
                            element.set({'opacity': opacVal});
                        }.bind( this, [$('someImage'), opacVal])
                       );

However, there is a problem with above approach. Since, we have forcefully bound the parameters of the function, therefore, the event object is not passed to the function. If you want to pass the event object aswell, then use the bindWithEvent function of mootools. Consider the following code;


var opacVal = 0.9;
$('someImage').addEvent('mousemove', 
                       function(element, opacVal){
                          element.set({'opacity': opacVal});
                          event.stop();
                       }.bindWithEvent( this, [$('someImage'), opacVal])
                       );

Tuesday, October 12, 2010

Change url dynamically through javascript

If you want to change the value of some attribute in the url dynamically through javascript without reloading the page, keep in mind that you can't change the complete url. However, there is one way around. You can change the anchor part of the url dynamically through javascript.

The window.location.hash property sets or returns the value from the anchor part of the url. Hence, if you want to change the value of the anchor, simply use the following syntax;

window.location.hash = "something";

One of the reasons for placing some value in the anchor part of the url dynamically is to make the url shareable. For example, consider you are making a slide show of images. The images are shown on the page without reloading the page and the id of the image being displayed is placed in the anchor part of the url, so that the user may bookmark/share the url and when the url is opened the slide show starts from the image whose id is placed in the url.

Wednesday, October 6, 2010

Oracle sequence and cache option

Let's first see what is the syntax of a sequence;

CREATE SEQUENCE sequence_name
    MINVALUE value
    MAXVALUE value
    START WITH value
    INCREMENT BY value
    CACHE value;

In order to get the current value of a sequence, we have currval and in order to get the next value of a sequence, we have nextval.

Let's say we create a sequence using the following syntax;

CREATE SEQUENCE pubnum
    MAXVALUE 1000
    START WITH 1
    INCREMENT BY 1;

now, if I try to get the current and next sequence number using a single query, one might use a query similar to following;

select pubnum.currval, pubnum.nextval from dual;

However, the above query simply increments the sequence and gives you the incremented value for both current value and next value. This shows that we have to use some other mechanism to do this. Fortunately, oracle stores the details of sequences in a table called user_sequences which stores the details of all sequences created. This table has one column by the name of "LAST_NUMBER" which tells you the next number to be generated from the sequence. Now, let's say the last sequence number generated from pubnum sequence 2 and we execute the following query;

select last_number from user_sequences where sequence_name = 'PUBNUM';

The above should return 3, however, the above returns 21 which is surprising. The reason is hidden in the create statement of the sequence. Remember, the CACHE option of the sequence create statement. The CACHE option pre-allocates a set of sequence numbers and keeps them in memory so that sequence numbers can be accessed faster. When the last of the sequence numbers in the cache has been used, Oracle reads another set of numbers into the cache. However, when you don't mention any value for the CACHE option, the default value of 20 is used. Now, read the bold part again. What Oracle does is that it stores the next 20 numbers in cache(assuming cache size is 20). The current value of the sequence was 2, however, due to the 20 numbers placed in cache, the last_number in the user_sequences table gave us 21 instead of 3. The value of last_number in the user_sequences will remain 21 till the actual sequence reaches 20. As soon as the current value of the sequence becomes 21, the last_number will start pointing to 41 (it will prefetch next 20 numbers). Hence, if you want this to work, you have to mention NOCACHE option during the sequence creation. Look at the following updated syntax;

CREATE SEQUENCE pubnum1
    MAXVALUE 1000
    START WITH 1
    INCREMENT BY 1
    NOCACHE;

Let's suppose the current value of the pubnum1 is 3. If you run the following query;

select last_number from user_sequences where sequence_name = 'PUBNUM1';

Now, the above will return 4 which is the correct value. So, in order to get current value and next value of the sequence using a single query, we can use the following query;


select last_number - 1 as "Current value", last_number as "Next Value" from user_sequences where sequence_name = 'PUBNUM1';

Saturday, October 2, 2010

how google, yahoo, bing retrieve results sooo quickly

The other day i was working with multi curl in php to retrieve the results from google, yahoo and bing.. now remember how it's soo surprising that these search engines retrieve results containing millions of documents within less than a second... everyone thinks it's because of powerful and distributed servers but there is also a very simple thing that all of the above search engines return only top 1000 documents.. if you try to go beyond that it just doesn't allow. for example, try the following urls;

http://www.google.com.pk/search?q=nirvana&hl=en&client=firefox-a&hs=v61&rls=org.mozilla:en-US:official&prmd=vli&ei=LCinTP7ENZKiuQPAp8SADQ&start=640&sa=N

http://www.bing.com/search?q=nirvana&go=&qs=n&sk=&sc=4-7&first=1596&FORM=PERE7

http://search.yahoo.com/search;_ylt=A0oGdUnHKKdMFTkAMGhXNyoA?p=nirvana&ei=UTF-8&fr=yfp-t-963&xargs=0&pstart=1&b=1101&xa=cymfuMaNmNDApYTZxBtEDg--,1286109767


now in google search the term "start = 640" means return documents starting from 640th document...
in bing search the term "first = 1596" means return documents starting from 1596th document...
in yahoo search the term "b=1101" means return documents starting from 1101th document...

now, if you visit the google url mentioned above, you'll see that the pages beyond 65 are not even visible and if you try to go beyond that, it simply comes back to the 65th page. similarly, yahoo and bing don't show page links for more than 100

now, this shows that google doesn't return documents more than 650 if you use "show 10 results per page" and 700 at max if you use "show 100 results per page" whereas bing and yahoo both only go till 1000...

another interesting thing is that even google shows an error page if you try to go beyond 1000, try the following url;

http://www.google.com.pk/search?q=nirvana&num=100&hl=en&lr=&client=firefox-a&hs=Mqi&rls=org.mozilla:en-US:official&prmd=b&ei=Lj2nTPDODYTovQOI5d37DA&start=2000&sa=N

the above simply displays the error that "Sorry, Google does not serve more than 1000 results for any query. (You asked for results starting from 2000.)".


what these search engines are doing is that during indexing, they store the count of documents containing a specific term.. so all they do is that they take the union of the number of documents containing the terms and show that in the top (referring to ... out of 1,554,874 results).

I remember building a simple search engine based on reuters dataset (http://www.daviddlewis.com/resources/testcollections/reuters21578/) and on a 3.02 GHz 64 bit machine with 2 GB ram, it returned 1000 documents in 250 milliseconds. Now, I wonder the reason behind the quick retrieval by google, bing and yahoo is distributed servers and complex algorithms or just a simple thought that humans won't even go through 100 results for a search query so let's just retrieve only top 1000 results.