My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Wednesday, January 28, 2009

32 bytes, ehr ... 9, ehr ... 7!!! to know if your browser is IE

Update another clever trick from comments, which I will explain later:

if(!+"\v1") // true only in IE
if("\v"=="v")// true only in IE
try{IE=window=!1}catch(e){IE=!0}

it works, doesn't it?

The reason is simple, if we consider "\v" as a vertical space, browsers interpreter that check in this way:

!(+
1
)

where "not one" is always false, considering that 0 is always casted as false, and every other number is casted as true.

Internet Explorer will ignore the vertical space, trying to cast a "v" character into a number, thanks to the sign (plus or minus, it does not matter)

// How IE interpreters the code
!(+"v")

// where
isNaN(+"v") === true
// and where NaN is implicitly casted as false


About conditional comments


I read a lot of comments in both Ajaxian and other places ... well, guys, this is a trick as conditional comment is.
The day IE will interpret correctly the "\v" character, Opera or some other browser could implement conditional comments as well.
That's my simple point of view:
conditional comments could be stripped out from minifiers (not MyMin) and make code difficult do understand or maintain while this 7 bytes trick is portable, minifier/packer safe, and extremely easy to maintain (the day IE will be able to return a false, we'll have solved almost every cross browser problem thanks to JS2 but just in case, all we need to do, will be a simple replace with the isIE.Next trick)


Note this trick is library/hack proof. If you think that execScript check is enough, for example, consider this: top["execScript"]=null and/or libraries that implements execScript for other browsers.

Tuesday, January 27, 2009

png to gif transformation - an excellent free solution via PHP GD2

Why we still need png to gif in our web pages


There a couple of valid plugins for different libraries able to manage "silently" png transparency in order to fix those browsers (cough: IE6) that are still used, for god knows which reason, but not able to understand properly the alpha channel. The problem is that these plugins cause overhead in our pages and for each png images, which is nearly a non-sense since these plugins fix old browsers whose performances have never been brilliant!
Especially for modern GUIs, where little icons are widely used, these plugins could make the application incredibly slow.
If we could choose old good GIF instead of png, 'till the end of those browsers, the problem will not exist and performances will be acceptable.

PNG 2 GIF, simple? ... not, really!


First of all a couple of search via Google brought me in "free but you have to pay anyway" applications through some open source, simple, but not that nice, program language solution, incompatible with modern interpreters or not able to produce a good quality result.
The problem is basically this one: alpha channel is something amazing, but it changes border colors if we are putting the png over a bright background rather than a dark one.
For this reason it is really difficult to obtain a valid GIF copy of the original PNG feel using common transformers, since the main difference is given by our cool theme that can be bright or dark.
Thanks to PHP and its distributed GD2 library, I have been able to create a good compromise between pixelated borders and our chose theme.

png2gif, the php/gd2 way



function png2gif($pngs, $background = array(255, 255, 255), $dest = 'gif'){
// by WebReflection
foreach($pngs as $png){
$size = getimagesize($png);
$img = imagecreatefrompng($png);
$image = imagecreatetruecolor($width = $size[0], $height = $size[1]);
imagefill($image, 0, 0, $bgcolor = imagecolorallocate($image, $background[0], $background[1], $background[2]));
imagecopyresampled($image, $img, 0, 0, 0, 0, $width, $height, $width, $height);
imagecolortransparent($image, $bgcolor);
imagegif($image, str_ireplace('.png', '.gif', $dest.DIRECTORY_SEPARATOR.basename($png)), 100);
imagedestroy($image);
}
}

// example
png2gif(glob("icons/*.png"));

That's it, you copy and paste an entire directory into, for example, an icons folder, and you call the function passing the list of files contained in that directory.
Parameters are the file list itself, the background color, and finally the destination folder to put transformed png into gif, by default the dir "gif".
The main difference between this function and every other method I found, is that borders are gracefully adapted to your main theme, by default a bright one with white background.
In this way you can choose how borders should be readapted instead of black borders for alpha problems, or cutted one. Even circle icons result are, in my opinion, brilliant!
The array should contain R,G,B integer values, from 0 to 255, where array(0,0,0) is black and array(255,255,255) is white, the default background theme.

You can try this function to convert most common icons theme, and please tell me if the result was not good enough ;-)

P.S. for PHP maniacas, the imagecopyresampled gd2 function has been the only one able to merge properly the png into chose background color ... I don't want to tell you how long did it take to find this kind of solution (passing via pixel by pixel color allocation ... omg!)

IE8 RC1 - Faster SunSpider? It does not matter!

Just a quick post about IE.Next - SunSpider could score a bit better (not compared with other browsers) but the render speed, css direct/text visualization, plus events propagation, is about 2 times slower than beta 2 and 7

Guys ... just 3 words: what the hell!

Monday, January 26, 2009

PAMPA-J calls for tests

More downloads, and as usual more problems caused by different configurations.
I would like to leave a single official 1.0 release in Google Code PAMPA-J Project page but I need your tests to do this.

Test I would like to perform with your configurations:

  • PAMPA starts (default configuration: Apache, MySQL, PHP, Jaxer)

  • PAMPA works from every folder (Desktop, Documents, others)

  • PAMPA closes processes on stop and/or exit

  • PAMPA works from network as well (old version could be launched from another PC without problems and respecting paths)


All above tests passed without problems for me, but I would like to be sure it works properly in other environments. Would you help me? Cheers :-)

Saturday, January 24, 2009

PAMPA 1.0 - Jaxer included!

Update - Windows Home Fixed!
Version 1.0b contains hot fixes for Window$ Home Edition plus some little change to make PAMPA more robust than before during process managment.
If you already downloaded version 1.0 you can download only the Tray executable and rename it as PAMPA.exe

Please tell me if you have still problems, hoping this b version will be the final 'till 1.1 ;-)

Update
Final Version 1.0 now served via Google Code ;-)



I am proud to announce the first Release Candidate final 1.0 release of my tiny precious PAMPA, jumped from version 0.7 to 1.0 and rewrote from the scratch considering every suggestion its users gave me during these years (project born in 2006 and completely revisited for version 1.0)

What is PAMPA-J


PAMPA means Portable Apache, MySQL, and PHP Application, in this release with Aptana Jaxer included!
(shell I consider PAMPA-J a portable Aptana Cloud?)

What does it mean: Portable


PAMPA is the first customizable and portable WAMP environment that includes Jaxer and that is completely free (but of course donations are always appreciated).
You can launch PAMPA from an USB driver, a CD-ROM (without native database write capabilities), an external Hard Disk, your Hard Disk itself, configuring MySQL data folders, session folders via php.ini, Apache folders, Jaxer folders, and reading logs, changing configuration, in a truly simple way.

Where is it?


For some reason Google Code policy is to ask sourceforge about projects with the same name, so in Google Code I had to change PAMPA project name into PAMPA-J, considering that somebody created an empty PAMPA project in sourceforge 1 year ago, while PAMPA exists at least since February 2006 ... anyway, the link I provided already should not show the RC release yet, while this one, directly from the svn, is the unofficial Release Candidate 1 Direct Download

How does it work?


All you need to do is to choose a folder to decompress PAMPA and then launch PAMPA.exe from that folder, wherever it is.
The result, by default, should be a fully configured Apache, MySQL, PHP, and Jaxer environment for your Window$ 2000/XP/Vista/7 Operating System.
I am working to create a better "splash page" to introduce PAMPA, but right now the Release Candidate shows simply the Document Root with links for some file, Jaxer public folder included ;-)

What's in



  • Apache 2.2.11

  • MySQL 5.1.30-community

  • PHP 5.3.0 alpha 3 (RC1)

  • Jaxer 1.0.2.4347

  • phpMyAdmin 3.1.2



Why all this effort for a Release Candidate?
Because I am looking forward for your feedback, in order to release the first portable WAMP plus Jaxer environment with all you need to develop for your own or for distributions your applications and with all you need (in a reasonable way :D)


PAMPA-J is out, have fun! 8-)

Friday, January 23, 2009

Quick Tip: C# GZip content

A quick post with a simple trick that let us reduce instantly every Ajax interaction over our C#.NET application:

[-] public void GZResponse() {
if(-1 < Convert.ToString(Request.ServerVariables["HTTP_ACCEPT_ENCODING"]).IndexOf("gzip")){
Response.AddHeader("Content-Encoding", "gzip");
Response.Filter = new System.IO.Compression.GZipStream(
Response.Filter,
System.IO.Compression.CompressionMode.Compress
);
}
}

The good part is that precedent code, whenever we were using Response.Write, became automatically compatible. Above code could be used to serve JavaScript or css files too:


[+] public void GZResponse(){}

[-] protected void Page_Load(object sender, EventArgs e)
{
GZResponse();
Response.AddHeader("Content-Type", "text/javascript");
Response.WriteFile("jquery.min.js");
}


That's it :-)

P.S. for file serving I suggest a cache system for pre gzipped files

Thursday, January 15, 2009

ellipse and circle for canvas 2d context

These days I am working inside an ExtJS panel to create some sophisticated drawing stuff, and I discovered (too late ...) that there is almost nothing in canvas 2d context to draw circle or ellipse.

That's why I have extended Google excanvas library and/or native canvas 2d context prototype to let us create circles and ellipse in the same simple way we can create rectangles.

Here the extended proto:

(function(){
// Andrea Giammarchi - Mit Style License
var extend = {
// Circle methods
circle:function(aX, aY, aDiameter){
this.ellipse(aX, aY, aDiameter, aDiameter);
},
fillCircle:function(aX, aY, aDiameter){
this.beginPath();
this.circle(aX, aY, aDiameter);
this.fill();
},
strokeCircle:function(aX, aY, aDiameter){
this.beginPath();
this.circle(aX, aY, aDiameter);
this.stroke();
},
// Ellipse methods
ellipse:function(aX, aY, aWidth, aHeight){
var hB = (aWidth / 2) * .5522848,
vB = (aHeight / 2) * .5522848,
eX = aX + aWidth,
eY = aY + aHeight,
mX = aX + aWidth / 2,
mY = aY + aHeight / 2;
this.moveTo(aX, mY);
this.bezierCurveTo(aX, mY - vB, mX - hB, aY, mX, aY);
this.bezierCurveTo(mX + hB, aY, eX, mY - vB, eX, mY);
this.bezierCurveTo(eX, mY + vB, mX + hB, eY, mX, eY);
this.bezierCurveTo(mX - hB, eY, aX, mY + vB, aX, mY);
this.closePath();
},
fillEllipse:function(aX, aY, aWidth, aHeight){
this.beginPath();
this.ellipse(aX, aY, aWidth, aHeight);
this.fill();
},
strokeEllipse:function(aX, aY, aWidth, aHeight){
this.beginPath();
this.ellipse(aX, aY, aWidth, aHeight);
this.stroke();
}
};
for(var key in extend)
CanvasRenderingContext2D.prototype[key] = extend[key];
if(!this.G_vmlCanvasManager)
G_vmlCanvasManager = {init:function(){}, initElement:function(el){return el}};
})();


The last part is to obtain the same behavior with any browser and runtime create canvas:

var myCanvas = G_vmlCanvasManager.initElement(document.createElement("canvas"));


SImple and efficient? I hope so, since I spent more than a couple of minutes to be able to create a perfect circle via 1/4 of a rectangle (square ... but how could I remember the bloody constant to do that?) :P

P.S. I remind you that the simplest function to create a circle via a center is canvas.arc(x, y, radius, 0, Math.PI*2); and that this proto create a circle inside a specified square but ithout the same arc performances ( anyway, runtime rendering in IE and my personal version of excanvas even faster than FireFox 3 ... anybody interested? :-) )

Saturday, January 10, 2009

Internet Explorer Object watch

Update
As Luke suggested, there is a method remove in the watcher that should solver leaks problem while the new version should be supported by most recent Opera, Safari, FireFox, and Internet Explorer.
Main updates:

  • the watcher is always another object and not the original one

  • to safely destroy the watcher use its destroy method and then delete it


----------------------------------------------


We all complain about IE and its non-standard attitude, this time I would like to introduce a non standard feature, as Object.prototype.watch is, for Internet Explorer.

What is watch and why we need it


The watch method allows validation, control, monitoring, over a generic object.
This means that we can control properties assignment with one, or more, callbacks.

var man = {};
man.watch("name", function(propertyName, oldValue, newValue){
// propertyName: name
// oldValue: undefined if it is the first time, the old one otherwise
// newValue: new assigned value
return "Mr " + newValue;
});

man.name = "Andrea";
alert(man.name); // Mr Andrea


watch for Internet Explorer, about my implementation


I used a weird strategy to implement this feature in Internet Explorer and it is based on DOM and its IE feature called onpropertychange.
Accordingly, it was not possible to create an Object.prototype.watch, while it was simple to implement a createWatcher callback.

(function(watch, unwatch){
createWatcher = watch && unwatch ?
// Andrea Giammarchi - Mit Style License
function(Object){
var handlers = [];
return {
destroy:function(){
handlers.forEach(function(prop){
unwatch.call(this, prop);
}, this);
delete handlers;
},
watch:function(prop, handler){
if(-1 === handlers.indexOf(prop))
handlers.push(prop);
watch.call(this, prop, function(prop, prevValue, newValue){
return Object[prop] = handler.call(Object, prop, prevValue, newValue);
});
},
unwatch:function(prop){
var i = handlers.indexOf(prop);
if(-1 !== i){
unwatch.call(this, prop);
handlers.splice(i, 1);
};
}
}
}:(Object.prototype.__defineSetter__?
function(Object){
var handlers = [];
return {
destroy:function(){
handlers.forEach(function(prop){
delete this[prop];
}, this);
delete handlers;
},
watch:function(prop, handler){
if(-1 === handlers.indexOf(prop))
handlers.push(prop);
if(!this.__lookupGetter__(prop))
this.__defineGetter__(prop, function(){return Object[prop]});
this.__defineSetter__(prop, function(newValue){
Object[prop] = handler.call(Object, prop, Object[prop], newValue);
});
},
unwatch:function(prop){
var i = handlers.indexOf(prop);
if(-1 !== i){
delete this[prop];
handlers.splice(i, 1);
};
}
};
}:
function(Object){
function onpropertychange(){
var prop = event.propertyName,
newValue = empty[prop]
prevValue = Object[prop],
handler = handlers[prop];
if(handler)
attachEvent(detachEvent()[prop] = Object[prop] = handler.call(Object, prop, prevValue, newValue));
};
function attachEvent(){empty.attachEvent("onpropertychange", onpropertychange)};
function detachEvent(){empty.detachEvent("onpropertychange", onpropertychange);return empty};
var empty = document.createElement("empty"), handlers = {};
empty.destroy = function(){
detachEvent();
empty.parentNode.removeChild(empty);
empty = handlers = null;
};
empty.watch = function(prop, handler){handlers[prop] = handler};
empty.unwatch = function(prop){delete handlers[prop]};
attachEvent();
return (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(empty);
}
)
;
})(Object.prototype.watch, Object.prototype.unwatch);

A first example, based on precedent one, is this one:

var // original object
man = {},

// create its watcher
manWatcher = createWatcher(man);

manWatcher.watch("name", function(propertyName, oldValue, newValue){
return "Mr " + newValue;
});

// assign name property
manWatcher.name = "Andrea";

// retrieve original object property
alert(man.name); // Mr Andrea

Another example is based on validation, or better, a one shot assignment over the watcher:

var obj = {},
watcher = createWatcher(obj);

watcher.watch("test", function(prop, oldValue, newValue){
return oldValue === undefined ? newValue : oldValue;
});

watcher.test = "one assignment";
watcher.test = "never again";

alert(obj.test); // one assignment

Cool?

About limits


The main one is that the for in loop will not work as expected over a watcher element because it is a DOM node and it will expose every property.
Another one could be about memory leaks and/or low execution, since there are a couple of things to do during assignment.
The good part is that the watcher and the watched will have the same property value, except for those attributes that we cannot modify in a DOM node (style, for example).

Last but not least, happy new year! :D