Posts Tagged ‘javascript’

20090729 JSONP explained

I had heard about JSONP several times before, but as I didn’t need to understand it yet, I had just ignored any of the usual reference materials about it… until today. I was trying to access some feeds via ajax and noticed that even if they “loaded”, with readyState being 4 and all that, the response body was empty. I first wondered if it was because I was executing my script locally, and maybe the feeds host wouldn’t like to see clients using localhost servers, who knows?

I was wrong, it was just the usual cross domain problem. There are three solutions:

  1. use an internal proxy to fetch data and send it to your javascript code. For example: proxy.php will fetch all the external feeds on behalf of your code.js, which will call proxy.php for all its requests. This, apart from adding extra load in your server, defeats the whole purpose of doing client side scripting for retrieving the external feeds data.
  2. use a flash proxy and communicate with it from your javascript code. Not something I want to do, since I didn’t want to use flash for this.
  3. use JSONP

And what is JSONP? In essence, dynamically creating a new SCRIPT element in your document. Its URL will be the URL from the feed service; and when it finishes loading, it will execute. But here’s the trick: the URL has a special parameter, usually named callback. This parameter is used in the response from the server, in such a way that the response is actually a function call whose parameter is the JSON data you expect.

Confusing, huh? OK, it’s easier with an example. Let’s say you want to grab the latest pictures from the tortoises pool in flickr. It has an RSS feed: http://api.flickr.com/services/feeds/groups_pool.gne?id=78811852@N00&lang=en-us&format=rss_200

Notice the format=rss_200 parameter?

If you change it to format=json you get something like this:

jsonFlickrFeed({
                "title": "Tortoises, turtles and terrapins - zoo, wild or pets Pool",
                "link": "http://www.flickr.com/groups/tortoise/pool/",
                "description": "<a href=\"http://flickr.com/groups/78811852@N00/\"><img src=\"http://bighugelabs.com/flickr/profilewidget/group/random/000000/ffffff/78811852@N00.jpg\" alt=\"Tortoise. Get yours at bighugelabs.com/flickr\" title=\"Tortoise. Get yours at bighugelabs.com/flickr\" /><\/a> A group for, you guessed it, Tortoises, turtles and terrapins, they can be wild, zoo or pets. There is a group for pets only - <a href=\"http://www.flickr.com/groups/pet_tortoise/\">&quot;PET TORTIOSE&quot;<\/a> Comment code below, Seen in <a href=\"http://www.flickr.com/groups/tortoise/\">&quot;Tortoises, turtles and terrapins - zoo, wild or pets&quot;<\/a>",
                "modified": "2009-07-28T20:37:25Z",
                "generator": "http://www.flickr.com/",
                "items": [
           {
                        "title": "Schildkröten_2009",
                        "link": "http://www.flickr.com/photos/testudograeca/3762164698/in/pool-78811852@N00",
                        "media": {"m":"http://farm3.static.flickr.com/2573/3762164698_6f27527c81_m.jpg"},
                        "date_taken": "2009-07-26T12:54:13-08:00",
                        "description": "<p><a href=\"http://www.flickr.com/people/testudograeca/\">TestudoGraeca<\/a> has added a photo to the pool:<\/p> <p><a href=\"http://www.flickr.com/photos/testudograeca/3762164698/\" title=\"Schildkröten_2009\"><img src=\"http://farm3.static.flickr.com/2573/3762164698_6f27527c81_m.jpg\" width=\"240\" height=\"166\" alt=\"Schildkröten_2009\" /><\/a><\/p> <p><a href=\"http://www.testudograeca.de\" rel=\"nofollow\">www.testudograeca.de<\/a><\/p>",
                        "published": "2009-07-27T13:57:24Z",
                        "author": "nobody@flickr.com (TestudoGraeca)",
                        "author_id": "27736205@N04",
                        "tags": "shop online info terrarium heizung schildkröte warstein schildkröten gehege türkische ibera photovoltaik testudograeca landschildkröte frühbeet freigehege quarantäne maurische gartenbeleuchtung winterstarre auswinterung dirkvöllmeke gartengehege schildkrötenunterschlupf pflanzsteine frühbeetheizung danielastockhausen wwwschildkröteonlinede"
           },
...
});

We aren’t finished yet. Did you notice the jsonFlickrFeed( piece at the beginning? That is the function call I wrote about earlier. If we included this as an script in our page, for example like this:

var url = 'http://api.flickr.com/services/feeds/groups_pool.gne?id=78811852@N00&lang=en-us&format=json';
var script = document.createElement("script");
script.setAttribute("src", url);
script.setAttribute("type", "text/javascript");
document.body.appendChild(script);

when the script was loaded, it would execute and call the jsonFlickrFeed function. But the call would fail, because there isn’t any function with that name.

This is why the callback parameter is used in the URL. Let’s say you have a function like this:

function jsonphandler(data)
{
        alert("hey! jsonphandler was called ");
        // do something with data
}

If we append jsoncallback=jsonphandler to the previous pool URL, the response will begin with jsonphandler( instead of jsonFlickrFeed(, and our own function (i.e., jsonphandler) will be called when the script is loaded.

To sum things up: JSONP is all about loading a new SCRIPT element, which calls a function that you have specified beforehand. It still needs a cooperating server in order to work, but since SCRIPT tags aren’t subject to the same cross domain restrictions as XMLHttpRequest calls (AJAX for buzz words lovers), this is the only solution you have if you want to pull data from a different domain without using a proxy.

A word of caution, though: unlike simple RSS retrievals, this actually allows the remote server to inject any javascript code they wish in your page, because you’re liberally loading whatever they return to you. A malicious server could return other javascript code instead of the simple function call you expect. Therefore, you should use this only with servers you can really trust.

20090716 Proportionally resize images with CSS (and maybe JS)

With CSS only:

img { height: auto; max-width: 99%; }

I normally used only max-width: 99%;, but I found it tends to produce images which are too vertically stretched in some cases, and I am of the opinion that images should always preserve their aspect ratio, unless you’re trying to create some strange effect.

Tested in Firefox 3.5, Chrome 3, and Safari 3 — let me know if it works (or not) in other browsers!

If it doesn’t work, I have been playing with some javascript that you could use. In theory, something like the following could be used instead, although I can’t think of an scenario where CSS support was bad enough that JS had to be used… oh wait, I just remembered this script. Ha!

Anyway, here’s the javascript code:

if(document.getElementsByTagName)
{
        var imgs = document.getElementsByTagName("img");
        var i, el, nw, w, h, nh, cw, container;
        for(i = 0; i < imgs.length; i++)
        {
                el = imgs[i];
                nw = el.naturalWidth;
                w = el.width;
               
                container = el.parentNode;
                if(container == null) { continue; }
               
                cw = container.clientWidth;
               
                if(nw > cw)
                {
                        nh = el.naturalHeight;
                       
                        el.width = Math.round(0.99 * cw);
                        el.height = Math.round(el.width * 1.0 * nh / nw);
                }
               
        }
}

I’m not sure if I’m being extra cautious with the if(document.getElementsByTagName) check.

Of course, if you want to do things properly, this script should only be executed once the page and images have been fully loaded, because only at that point the real size of the images (also known as the naturalWidth and naturalHeight properties) can be accessed.

Corrections and suggestions are gladly welcome.

20080128 xplsv.tv embedding!

There’s a very interesting new feature here at xplsv.tv: video embedding!

If you love a movie and want to demonstrate publicly, just grab the embed code in the movie page and paste it in your homepage :-)

For example, here are two demos I love:

Isn’t it cool? :)

20070709 jQuery and Rails (and getting rid of prototype)

My first ajax-y attempts were done with prototype. It all seemed so natural and seamless: prototype came with rails and there were those nice javascript helpers like link_to_remote which generated the javascript code for calling prototype functions. Until you looked at the code! (and the weight of it). I’m not talking about the ruby code but the output HTML, which is heavy and plagued with very obtrusive javascript.

Since I’m a concerned person, I decided to ditch Prototype and its Scriptaculousy effects and use jQuery instead. And I thought: since jQuery is so cool, I’m pretty sure that somebody will have done a replacement for the default javascript helpers (based in Prototype). But I was wrong. Not completely wrong, but quite a bit.

There’s some work done in this area; a guy whose page is titled Katz got your tongue? is working hard in getting a jQuery On Rails plug-in up and running. Incidentally it seems he’s also a jQuery developer so that’s very good too. But since the plug-in is not finished yet, I kept looking for less obstrusive alternatives. I remembered about the unobtrusive plug-in that Luke Redpath & Dan Webb talked about in a LRUG meeting, UJS, and although it looked really cool, I realized am not sure if that’s what I want.

Right now, it looks easier to simply write the HTML and add some JS (not in the html itself) which behaviourizes the elements, using jQuery for this, rather than using UJS’s apply_behaviour. Specially because at the end, UJS is still using Prototype, and it is very slow compared with jQuery.

In my tests, which are so unscientific that I don’t have any result indicator more than my own subjective speed perception, effects made with Prototype+Scriptaculous ended with what I call the 1fps syndrome: the screen gets updated only 1 time per second, so you forget about concepts like smoothness, transitions, easing and all that quite quickly. Way more quickly than it takes the effect to execute, actually; when it finishes you have almost forgotten what you wanted to do. Meanwhile, jQuery kept always a decent framerate and didn’t feel like overloading the processor. And this happens in all types of machines.

So I think I’m going for jQuery, doing things in the style of this guy over here, and will keep an eye in jQuery for Rails development.

20070517 Array.indexOf in Internet Explorer

According to this document at Mozilla Developer Center, Javascript 1.5 has been implemented in a browser since at least the first releases of Mozilla as open source browser, which means, in other words, since around 1998. Let’s assume it was 2002 which is marked as the release of the 1.0 version.

And I was doing some interface programming lately and I needed to check if an element was in an array. Went to Gecko’s documentation (that’s the one I normally use, since it’s less overbloated with crappy ads) and checked that Array objects had an indexOf function. Cool! I used that function on my code. Then once I finished with all the development, went to The Horror (i.e. Internet Explorer) and tested it.

Surprise! It was broken! What had I done? (You know the debugger for Internet Explorer is not specially helpful)

I suspected of the indexOf function, and recalled vague memories about doing a custom search function in the past for looking into arrays and not having to write a for(i=0; i<ar.length;i++) thing each time…

Mmmm… did a alert(‘Array.indexO’) in ie and what did I get?: undefined

So they have been spent five years for releasing ie7 and still they didn’t implement Array.indexOf!

No worries, though. Javascript is flexible! Look, IE, I don’t care if you choke on the mere seeing of indexOf, you’re going to run it whether you like it or not!

	if(!Array.indexOf){
	    Array.prototype.indexOf = function(obj){
	        for(var i=0; i<this.length; i++){
	            if(this[i]==obj){
	                return i;
	            }
	        }
	        return -1;
	    }
	}

and voila! my script wasn’t broken anymore!