вторник, 15 июля 2008 г.

Defining and Changing Image Size in JavaScript

How can one define the size of the image in JavaScript?

I've come up with the approach of handling onload event for image element like this:

imageElement.onload = function() {
var width = imageElement.width;
var height = imageElement.height;
...
}

Once image is loaded browser knows for sure the width and height of this image and developer can reference actual values from width and height properties of image DOM element.

By changing image height or width it will be proportionally resized. BUT it is important to remember that corresponding image element properties are changed only when image is displayed!

Let's say there is an image 10 pixels long and 20 pixels high. JavaScript code sets width to be 50 pixels, thus demanding height to be increased 5 times, resulting in 100 pixels. If image was visible at the moment width value was overriden, height property will be updated as well, with 100 pixels. And if image was invisible, height property is not updated and left as it is before image si actually displayed!

четверг, 10 июля 2008 г.

Attaching/deattaching DOM elements in IE

Today I've faced an interesting issue with attaching and deattaching DOM elements in Internet Explorer.

The issue was around the creation of so called Thumbnail Bar component: visual representation of photo strip which supports scrolling with buttons:
All thumbnails to be displayed are loaded and stored in the array. Initially each thumbnail was represented by separate object, defined as follows:

ThumbnailBar.prototype.Thumbnail = function(imageAttachment) {
this.imageAttachment = imageAttachment;

this.containerElement = document.createElement("div");
this.containerElement.className = "eyestride-Thumbnail";

this.imageElement = document.createElement("img");
this.imageElement.src = imageAttachment.image.thumbnailFile.url;
this.containerElement.appendChild(imageElement);
}

So, thumbnail instance held a reference to container element and then used this reference to attach thumbnail container to Thumbnail Bar's image container. Each time Thumbnail Bar needs to be refreshed, inner HTML of imageContainerElement is first cleared:

this.imageContainerElement.innerHTML = "";

...

for (var i = startPosition; i < endPosition; ++i) {
this.imageContainerElement.appendChild(this.thumbnails[i].domElement);
}

Code works fine in Mozilla Firefox but refuses to work properly in Internet Explorer. In Internet Explorer appending of child div element (see containerElement in Thumbnail) actually works, but image is appended only once. So, if container element of Thumbnail was removed by clearing innerHTML of imageContainerElement the link between div and img is lost: div is appended without its child elements.

In order to fix this I've stored two references in Thumbnail: one for div and another for img, then added a method which performs attaching of thumbnail to specific container, in such a way:

EyeStride.UI.Viewer.ThumbnailBar.prototype.Thumbnail = function(imageAttachment) {
this.imageAttachment = imageAttachment;

this.containerElement = document.createElement("div");
this.containerElement.className = "eyestride-Thumbnail";

this.imageElement = document.createElement("img");
this.imageElement.src = imageAttachment.image.thumbnailFile.url;

this.attachTo = function(domElement) {
domElement.appendChild(this.containerElement);
this.containerElement.appendChild(this.imageElement);
}
}

...

for (var i = startPosition; i < endPosition; ++i) {
this.thumbnails[i].attachTo(this.imageContainerElement);
}

Another solution for the problem is to clear the contents of
imageContainerElement not by setting empty string for innerHTML property, but using removeChild method to remove elements from the image container, one by one. This solution works in both browsers too.

четверг, 3 июля 2008 г.

Redirect Browser to Custom URL with HTTP POST Method

Recently I've investigated how to redirect browser to custom URL using HTTP POST method.

This problem is not new, many people looked for the solution. The one I've come up with my colleague, Yury Povhanych, is a bit different from others. But inly in details, not in idea.

In order to be more specific let me describe the case in more detail. Now our company is building application for Facebook. Well, there are two ways to build applications for Facebook:
  • use FBML and Facebook-specific JavaScript
  • implement IFrame-based application
For a number of reasons we've gone the second way and our application is being displayed as internal frame.

Now, we want to get use of Facebook's friend selection system, i.e. use similar UI to select friends and send them message or request. There are specific controls which help to achieve this goal, but they are FBML-based, so they are of no real value in our case. Basically, Facebook developed a solution for this situation. They have a page called Multi Friend Selector. This page is hosted on Facebook. In order to use the page, it should be fed with parameters. Some of these parameters are messages to be displayed on the selector itself, others represent application API key and call signature. All these staff may be the part of URL, but this is BAD for at least two reasons:
  • URL may become long, thus it may be handled badly by some browsers
  • URL is bloated with data that should not be seen so easily by the user
In order to resolve that POST request must be sent instead of GET. Parameters in POST request become the part of the request body, not the URL.

First thing I wanted to know is whether I can achieve such redirection by using only server-side capabilities. In Java EE stack I've found two methods which deal with redirection: request.forward and response.sendRedirect() but both of them has nothing to do with POST. First one is used to forward the request within the same servlet container while sendRedirect can be used to perform GET based redirection to any URL, both internal and external.

Well, there is no way to do it in pure server-side. It is because of redirection nature, which is implemented in the following way. When server wants to say browser "User is boring, send him to xxx site", it means that HTTP response status code must be set to 302 (which means "Moved Temporarily") and URL of "xxx site" must be specified under "Location" header entry in that response. "Location" is just a string. It doesn't have some POST-specific format. Pure URL, nothing more. Browser should open "xxx site" and will use GET method in order to achieve that.

Solution for the problem is easy: use DOM forms, on the client side. Forms may be given a method to be used for request sending, including POST. Forms may be created dynamically, like any other DOM element. So, what you need to do, is:
  1. Create form
  2. Set URL to send request to in action parameter
  3. Set HTTP method in method parameter
  4. Optionally specify target
  5. Insert hidden input elements inside the form, give them the name and values which correspond to parameters to be sent via POST
  6. Make form the part of DOM by appending it to one of the DOM nodes
  7. Submit the form
  8. Destroy the form by
I've implemented this functionality as the part of my open-source framework, called Digr2iD. Here is the code that does the trick, dynamically (comments are removed):

Digr2iD.Controller.PostRedirector = {

go: function(url, parameters, target) {
if (!url) {
return;
}

var form = document.createElement("form");
form.id = "org_flushpongle_digr2id_js_controller_postredirector_form";
form.name = form.id;
form.action = url;
form.method = "post";

if (!target) {
target = "_self";
}
form.target = target;

document.body.appendChild(form);

for (var parameterName in parameters) {
var input = document.createElement("input");
input.name = parameterName;
input.value = parameters[parameterName];
input.type = "hidden";
form.appendChild(input);
}

form.submit();

document.body.removeChild(form);
}

};