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

Thursday, September 28, 2006

bytefx less than 3Kb ? ... NO, less than TWO !!!!!

What an incredible compressor ... i didn't know it but seems really really a good compressor !!!
I'm talking about memtronic javascript compressor that allows me to pass from about 2.7 Kb into 1.98 Kb (with comments) !!!

Sure, it's not a big difference from Dean Edward packer but now I can say that I've created an FX library in exactly 1.889 byte (without comments) ... it's hilarus for me, I didn't know that some compressor was able to reduce size in this way.

Thank you memtronic !!! Your compressor is fantastic !!!

Here you can find bytefx API page with memtronic version demo and the link under Sources:
bytefx

Running with a Number ...

I think this post is only for basic JS developers and shows how a simple function or a simple operation should be wrote in different ways.
In this specific case, I would talk about logic used for a single function while I was optimizing bytefx.

The problem
I've two numbers, x and y, I need to change x, adding or removing a "speed" value, while x is not equal to y.

This simple problem has a lot of solutions. This is probably the simplest one:

function xRun2y(x, y, speed) {
// check wich number is greater than other one
if(x < y) {

// ok, x is less than y ... then add speed
x += speed;

// x can't be greater than y ...
// x can be only less than y ... or equal
if(x > y)
x = y; // stop run
}

// other case, x is greater than y
else if(x > y) {

// well, in this case we remove speed from x
x -= speed;

// but x can't be lower than y ... then ...
if(x < y)
x = y;
}

// we don't need to care about x == y
// just return x
return x;
};

As you can see by yourself, this simple function could create a range of numbers from Nstart to Nend.
This is an example:

// from 10 to 0
var start = 10, end = 0, speed = 1,
arr = [];
while(start !== end) {
start = xRun2y(start, end, speed);
arr.push(start);
};
alert(arr); // 9,8,7,6,5,4,3,2,1,0

// from 0 to 10
var start = 0, end = 10, speed = 1,
arr = [];
while(start !== end) {
start = xRun2y(start, end, speed);
arr.push(start);
};
alert(arr); // 1,2,3,4,5,6,7,8,9,10

These operations should be usefull to move an element, to change some value from a startPoint to endPoint ... but, as i've said, there are different ways to do that.

This way is, for me, a better way to write the same function.

function xRun2y(x, y, speed) {

// check wich number is greater than other one
if(x < y)

// with a ternary operator we can do everything inline
x = x + speed > y ? y : x + speed;

// other case, x is greater than y
else if(x > y)

// well, in this case we remove speed from x
x = x - speed < y ? y : x - speed;

return x;
};

Simple ? Clear ? ... I like this way as I like ternary operator, it's a must to write compact but efficient code.
However, look at this last function ... don't You see something strange ?
If and else if do exactly same operations ... only plus sign and greater than are diferent.
How we could create these similar operations in a single line ?
Using eval, sure !

function xRun2y(x, y, speed) {
var temp = x < y ? ["+", ">"] : ["-", "<"];
return eval("x".concat(temp[0], "speed", temp[1], "y?y:x", temp[0], "speed")); };

Yess !!! ... seems perfect ? ... or seems the evil ? Let me explain that :)

function xRun2y(x, y, speed) {

// we need to create dedicated ternary operation
var temp = x < y ?

// if x is lower than y we need to add speed
// and verify that x + speed is not greater than y
["+", ">"] :

// in other case we need to remove speed from x
// and check if x is not lower than y
["-", "<"];

// if x is lower than y this string is:
// x + speed > y ? y : x + speed
// else if x is greater than y ...
// x - speed < y ? y : x - speed
return eval("x " + temp[0] + " speed " + temp[1] + " y ? y : x " + temp[0] + " speed"); };

... simple ? cool ? ... no, it's not cool !
Eval in this case isn't absolutely dangerous or a problem while ternary operator is.
The answer is simple, we have reduced code size with a simple and fast function but we do everytime two operations.
These are x + speed or x - speed in both cases duplicated.
It's true, a simple addiction shouldn't be a problem for code execution, but if there's a way to use a better function, why we shouldn't use that ?

function xRun2y(x, y, speed) {

if(x < y)
// we need minimum value because
// if x + speed is greater than y
// we want y
x = Math.min(x + speed, y);

else if(x > y)
// we need maximum value because
// if x - speed is lower than y
// we want y
x = Math.max(x - speed, y);

return x;
};

Final solution
We don't care about case x == y but an operation like this one Math.min(1,1) returns 1 and not an error than why we couldn't use ternary operator ?

function xRun2y(x, y, speed) {
return x < y ? Math.min(x + speed, y) : Math.max(x - speed, y);
};

Do you think this is the best way to run from a Number to another ? I think so :)

Tuesday, September 26, 2006

simple effects ? bytefx

... less than 5.50 Kb for the un-packed version, less than 3Kb for Dean's packer compressed version (my php compressor doesn't like it ... and I need to know more about this problem !!! ).
It's bytefx, naturally compatible with FireFox or JS 1.6 standard browsers, compatible with IE5.5 or greater version with my JSL.

I need some day to write the simple API, docs and FAQs ... but you can just view an alpha version in action surfing this demo page.
Seems cool ? Seems another boring FX library ? Seems doesn't work with Safari or other browsers ? (I've not yet tested with Konqueror or Safari)

I don't know what bytefx seems ... but I like it :)

Are speed tests really usefull for developers ?

Yesterday my favourite portal HTML.it has posted a speed test inside its blog.
This JavaScript benchmark was posted on this site.

I think that those kind of tests are only a "point of view" and aren't comparable with real javascript usage.
As I've showed on my for and while loop test, every operation is a single case to analize and every single case has not everytime the same result.

For example using a for and an <= expression with every loop should be faster in some browser and should be slower in some other.
Then it's not always a good way to compare some internal loop operations because a browser should be faster to push a string inside an array but should be slower to execute that kind of for loop.

Often speed depends on garbage collector too, that should be faster in some point of execution and slower in some other.

These concept are true with every other benchmark of that site.

Math engine, as Ajax declaration, are faster with FireFox and really bad with IE because the object has a dedicated try catch for every IE browser.
I usually don't use that way to declare an XMLHttpRequest then my library should be faser than Opera with IE too.
If you change some piece of execution code, you can see that IE6 or 7 is even slower than FireFox with some function.

What I mean is that a real "every day" application will "never" use 4000 try catch but should use a lot of Math operations and DOM elements using many array and Ajax declarations too.
Every Ajax object or class should be simple or really complex and some browser should declare it faster or slower than other one.

Another generic relevant thing is code optimizzation ... you could use "only Opera" to surf the web but if a library contains bad practices its code execution will be slow in every case.

And again, the use of "var" before every temporary variable should be slower or faster, look at the MathEngine function ... is this usefull ?
is this a real application example ?

The StringFuncs has 2 operations that are only for human eyes because if you don't call those functions how can you test them ?

str.toLowerCase; // yeah! a string has a toLowerCase prototype ..
str.toUpperCase; // yeah! a string has a toUpperCase prototype ..

// P.S. I think that He's just forgot it :)


A "StringBuilder" function should be a better test too because every JS developer knows that using string += something isn't a good speed practice and the use of concat instead of '+''+''+''+''+' ... is faster too.
A StringBuilder operation is not different from array.push, but doesn't use sort and reverse as ArrayFuncs does.

Is a StringBuilred competition another usefull test to do ? I think that ...

function StringBuilder() {
function StringBuilder() {
this.append = function(what){
arr.push(what);
};
this.toString = function(){
return arr.join("");
};
var arr = [""];
};
var startTime = getTime(), i = 4001, sb = new StringBuilder;
while(i--)
sb.append("a piece of code".concat(i));
sb = sb.toString();
var endTime = getTime();
getElement('speed_sb').innerHTML = (endTime - startTime);
getElement('speed_total').innerHTML = eval(getElement('speed_total').innerHTML) + (endTime - startTime);
};


... and Should we care about few milliseconds ?

Then this is what I think: these speed tests are a must only for Rhino or SpiderMonkey developers because a javascript developer doesn't need to care about performances (in this way) ... He needs to care about generic code optimizzations using lightweight code implementations without redundant operations inside a cross-browser code.
That's what a developer should care about, not dedicated speed test for this or that function.

Finally, this is a revisited version of that benchmark, enjoy with it, don't care about it!

See you :)

Sunday, September 24, 2006

a better DOMContentLoaded ?

a better DOMContentLoaded ?



The first line of this post is dedicated to Dean Edwards: thank you very much for your DOMContentLoaded implementation !!!
Without Dean solution I'll probably never create the code I'm going to show, than thanks again Dean.

Next step is my solution code concept:
if we need to write runtime a script tag, why should we use conditional comments if we could implement the solution directly with that tag for other browsers too ?

Since onreadystatechange inside a script tag is called only from IE and Safari, we could write this properties directly inside the script tag, then we don't need any anonimous function and we don't need any webkit dedicated interval too.

<script id=__ie_onload defer src=javascript:void(0) onreadystatechange=ourFunction><\/script>

If we write a javascript source using void(0) as undefined source we could use undefined source using a function with an undefined return.

<script id=__ie_onload defer src=javascript:ourFunction2() onreadystatechange=ourFunction><\/script>

Then if we need to use defer with uncompatible browsers, we could set its value as generic value (I think it's more clean and elegant) and if we use our functions with the script we don't need to get it then we don't need it's id too.

<script src="javascript:ourFunction2()" defer="defer" onreadystatechange="ourFunction()"><\/script>


This code is the base for my onContent callback, that's based on a single global object, used fromthe tag to call functions.
It's time to show my lightweight function (less than 500 bytes), compatible with FireFox, Opera 9, IE5+ and Safari (thank you guidoz) and compatible with other browsers too (IE4, Opera < 9, others), using "old" window.onload method.

function onContent(callback){
__onload__={
$:function(){window.onload=null;callback()},
IES:function(e){if(e.readyState==="complete")this.$()},
FO:document.addEventListener?"document.addEventListener('DOMContentLoaded',__onload__.$,false)":"void(0)"
};
onload=callback;
if(!window.opera||parseInt(window.opera.version())>=9)
document.write(''));
};

Let me explain what this function does :)

__onload__ is the global object used by script tag and has 2 methods and just 1 parameter.
The dollar method $ is a shortcut to delete window.onload callback with every "DOMContentLoaded" compatible browser and then $ calls the callback too.

The second function, called IES, is the IE/Safari dedicated callback, used inside onreadystatechange event.
This function recieves the this (window) object and then check if its readyState is "complete", then call the dollar $ function to delete onload and to call callback.
The last FO parameter is dedicated to FireFox and Opera but preserve javascript errors using addEventListener only if this document method is available.
In other cases, this parameter will be the string "void(0)".
We don't need to care about addEventListener compatible browsers (i.e. Safari) because if they don't support the "DOMContentLoaded" event they'll necer use that listener.
FireFox as Opera will use that method directly (onerror, for FireFox, and onload, for Opera, script functions have a delay and if you add DOMContentLoaded event after page is loaded (cached) this will nevver be called).

Next line is the window.onload method, used from every browser ... then if __onload__ "hack" fails, callback will be called without problems.
The next line contains an if, this is necessay for Opera version older than 9 because Opera 8.5, for example, doesn't show the page if you write a script tag while page is downloading.
Final line writes the script tag, adding event or void inside src, adding defer="defer" for IE and setting onreadystatechange for IE and Safari.


What about addLoadEvent for multiple onload callbacks ?
I think that a single callback, if we don't use external libraries, is sufficient to call every other event on page complete or on dom ready (just calling multiple functions inside another one).
However this should be a simple solution to add multiple events using onContent function.

function addContentEvent(callback) {
if(!window.__onload__)
onContent(function(){__onload__.callEvents()});
__onload__.callEvents = function() {
for(var i = 0; i < __onload__.events.length; i++)
__onload__.events[i]();
};
if(!__onload__.events)
__onload__.events = [callback];
else
__onload__.events[__onload__.events.length] = callback;
};

// Example:
// addContentEvent(function(){alert("hello dom 1")});
// addContentEvent(function(){alert("hello dom 2")});

First line calls onContent if __onload__ object doesn't exists then creates the __onload__ object too and add a callback to call an __onload__ method.
callEvents is this one and it's used on page complete.
It loops over every callback and calls them.
Last if/else creates the __onload__.events array, adding first callback too.

Finally You could create a single function moving onContent inside addContentEvent (at the top), if addContentEvent is your favourite way to add callbacks.

The last thing ? The Example Page, where you should read event: onContent with compatible browsers (and please let me know if some browser is no compatible).

P.S. this blog doesn't require an account to reply ;)




UPDATE
For some reason Safari (at least some versionof Safari) doesn't accept an undefined source script and shows an error when this function write the script tag.
To solve this problem I've changed the function that now has the same snif used by Dean Edwards plus Konqueror snif (for KDE 3.X).

This is my updated solution, now tested with a real big range of browsers with a size of 650 bytes.

function onContent(callback){
__onload__={
E:function(){window.onload=null;callback()},
IES:function(e){if(e.readyState==="complete")this.E()},
FO:document.addEventListener?"document.addEventListener('DOMContentLoaded',__onload__.E,false)":"void(0)"
};
onload=callback;
if(/WebKit|Konqueror/i.test(navigator.userAgent))
(function(){/loaded|complete/.test(document.readyState)?__onload__.E():setTimeout(arguments.callee,1)})();
else if(!window.opera||parseInt(window.opera.version())>=9)
document.write('');
};

Friday, September 22, 2006

F.A.P. - Fast Archive Preview

What is F.A.P. ???
F.A.P. is my archive preview idea, a fast way to know a title and its permanent link of each post and for each month (or date).
You can view an example scrolling this page to Archieves and wait few seconds over one month just with your mouse.


How does F.A.P. work ?
Blogspot is a wonderfull and free service but its server is not available to its users.
There's no way (at least I've not found anyone) to implement ajax request to do some "experiment".
However, we are really lucky because blogspot doesn't block external requests, then every host should read our blog content, simply calling one page.
This was the key to create F.A.P. web service, that's a simple dedicated blogspot output parser.
The called external page will read a built archive page, for example one of the pages you can find under the Archieves on your left or right side of your blog.
After that our external requet page will produce a JSON like output usable for our scripts.


How to implement F.A.P. in your blogspot place ?
The first thing to do is a class or a function that can add dinamically a javascript tag inside page's head tag.
I've used this simple Class to do that

function DinamycScriptInclusion() {
// (C) Andrea Giammarchi - MIT Style Licence - webreflection.blogspot.it
function __add(fileName) {
var js = document.createElement('script');
js.language = "javascript";
js.type = "text/javascript";
js.src = fileName;
__fileList.push(fileName);
document.getElementsByTagName('head').item(0).appendChild(js);
};
function __remove(fileName, i) {
var scripts = document.getElementsByTagName('script'),
j = scripts.length;
while(j--) {
if(scripts.item(j).src == fileName)
scripts.item(j).parentNode.removeChild(scripts.item(j));
};
__fileList[i] = null;
};
this.add = function() {
for(var i = 0, j = arguments.length; i $lt; j; i++) {
if(__fileList.indexOf(arguments[i]) !== -1)
this.remove(arguments[i]);
__add(arguments[i]);
}
};
this.clear = function() {
__fileList.forEach(function(fileName, i){__remove(fileName, i)});
__fileList = [];
};
this.remove = function() {
for(var i = 0, j = arguments.length; i $lt; j; i++) {
if(__fileList.indexOf(arguments[i]) !== -1)
__remove(arguments[i], i);
__fileList = __fileList.filter(function(e){return e !== null});
}
};
var __fileList = [];
};

This class has only 3 public methods
- add, to add one or more external javascript file
- clear, to clear every external javascript file we have add
- remove, to remove one or more external javascript file from pur head

The second thing to do is to create a global variable to save (or cache) requet informations.
The name of this var is, obviously, webreflection :D

webreflection = {};


Now we need to add an onload event because we need to find and modify every Archive Link.

// use your favourite "addEvent" method, this is only an example
onload = function() {

var blogName = "your_blog_name_here", // i.e. blogName = "webreflection"
li = document.getElementsByTagName("LI"),
i = li.length,
link = null,
tmp = blogName.replace(/-/g, "\\-"),
re = new RegExp("(http://".concat(tmp, "\\.blogspot\\.com/)([\\w]+)(_", tmp, "_archive\\.html)"));

// loop over every found LI
while(i) {

// blogspot calls UL Archive container class "archive-list"
// we're interesting only to this container, that should be
// the parent node of one or more LI
if(li[--i].parentNode.className === "archive-list") {

// well, we have found an Archive li
// then we can add a div (unobtrusive layout)
// that will be used with F.A.P.
li[i].innerHTML += "
");
// we can use a class name, then in our CSS div.month-ghost can be as you want

// now we need to get the link inside this LI
link = li[i].childNodes[0];

// and we need to get date inside this link
// stored inside the link as an un-standard parameter
link.webreflection_remstring = link.href.replace(re, "$2");

// we need to save created div too, using unique id created with innerHTML
link.webreflection_div = document.getElementById("month-ghost-".concat(i));

// we must hide created div (then you don't need to write display: none inside CSS)
link.webreflection_div.style.display = "none";

// and we must set link referer too (this.referer shortcut)
link.webreflection_div.referer = link;


// we need to add events to link and to div too to manage F.A.P.

// link onmouseover
link.onmouseover = function() {

// usefull callback
function loaded() {
// if external host has created output
if(webreflection.response) {

// delete the interval
clearInterval(element.webreflection_interval);

// and call monthPreview function
monthPreview(element);
}
};

// element is this link scope inside this function (and loaded function too)
// txt is the date inside the url of this link
// url is the page to call with your blog name and the date as query string
var element = this,
txt = this.webreflection_remstring,
url = "http://win.3site.eu/fap.aspx?blogname=".concat(blogName, "&date=", txt);

// if there's an interval (created by onmouseout)
if(element.webreflection_interval)
clearInterval(element.webreflection_interval);

// if webreflection_div_content parameter is present, we don't need to call external page
// then just show old result
if(element.webreflection_div_content)
monthPreview(element);

else {

// reset old webreflection.response
webreflection.response = null;

// clear old scripts add on head tag
DSI.clear();

// and add created url
DSI.add(url);

// then set the interval to check webreflection.response
element.webreflection_interval = setInterval(loaded, 20);
}
};

// link onmouse out
link.onmouseout = function() {

// function to hide the div
function removeDiv() {
element.webreflection_div.style.display = "none";
};

// element is the this scope for the function (for removeDiv too)
var element = this;

// if onmouseover hasn't load the content, the interval is not 0 (not false)
if(element.webreflection_interval)

// then stop that interval
clearInterval(element.webreflection_interval);

// and call after 500 seconds remove function
element.webreflection_interval = setInterval(removeDiv, 500);
};


// div onmouse over
link.webreflection_div.onmouseover = function() {

// while we go with mouse over the div the link will create an interval
// (onmouseout) to remove this div after 1/2 second ... then stop them
clearInterval(this.referer.webreflection_interval);
};

// div onmouseout over
link.webreflection_div.onmouseout = function() {

// when we leave the div we want to call link onmouseout to hide them
this.referer.onmouseout();
};

}
};
};


I'm sorry for my bad english and I hope you've understund what this script does.
There's just a final function to view, the monthPreview function.

// function to write the content inside the div
function monthPreview(element) {

// div was saved as link.webreflection_div
// content is an array used "as StringBuild" to add content
var div = element.webreflection_div,
content = [];

// if content was loaded
if(element.webreflection_div_content)
// add that
div.innerHTML = element.webreflection_div_content;
// else create the output with webreflection.response array
else {
for(var i = 0, e = webreflection.response; i $lt; e.length; i++)
content.push('<a href="'.concat(e[i].link,'">',e[i].title,'</a>'));
};

// now we can show the div
div.style.display = "";

// and cache its content for next time :)
element.webreflection_div_content = div.innerHTML;
};


Last thing to know is that you can modify your "preview div" layout using css inside your blogspot page model.

To have the web service You can use my space but please, when I'll tell you (give me few days) what does server side script do to create response, use your server not mine :D


Finally, it's the complete script without comments, see you.

Thursday, September 21, 2006

JavaScript benchmark for while and for loop

It's just a page to test directly differents loops and respective times with blue "winners" too :)

If you have a really old (or slow) PC please don't visit the page.

If you visit the page and everything stops, close the page and wait few seconds.

Loops Benchmark: http://www.devpro.it/examples/loopsbench/

Wednesday, September 20, 2006

anonymous function to add simple events

This post shows as is possible to set prototypes with anonymous functions.
This one shows a simple anonymous function application example, a fake addEventListener.
We often use directly the method with the document element, it's simple and fast, then often our preferred way to implement an event.
I'm talking about this code

document.getElementById("myId").onclick =
function(){alert("hello")};


DOM and standars like this method to add an event

document.getElementById("myId").addEventListener(
"click",
function(){alert("hello")},
false
);


But IE doesn't implement addEventListener method (what a news ...)

document.getElementById("myId").attachEvent(
"onclick",
function(){alert("hello")}
);


The attachEvent has different problems, it isn't a standard method (then IE and few other browsers supports that) and the scope inside the callback is not the element.
This code, for example, doesn't work as expected:

document.getElementById("myId").attachEvent(
"onclick",
function(){alert(this.className)}
);


I've implemented the addEventListener in my big dollar function but often developers doesn't like "big" external libraries (scriptacolous as prototype and Dojo are some exceptions).
That's why I'm writing this simple function to add an event directly to an element, using generic on* event names.

function addSimpleEvent(
obj, // the object (i.e. window, document, element)
type, // the type (i.e. "onload", "onmouseover", "onclick")
callback // the callback (i.e. function(){alert(this)})
) {
obj[type] = (function(base){ // anonymous function
return function(evt){ // function called on event
if(!evt)evt=window.event; // event for IE browsers
if(base)base.call(this,evt); // old function, if defined
callback.call(this,evt); // callback
}
})(obj[type]) // old defined (or not) function
};

This function uses anonymous function to preserve old event (base variable), if presents, and calls every callback with the element scope (using call).
This is an example:

// imagine that other script did it ...
onload = function(){alert("Hello 1")};

// addSimpleEvent function
function addSimpleEvent(obj,type,callback) {
obj[type] = (function(base){return function(evt){
if(!evt)evt=window.event;
if(base)base.call(this,evt);
callback.call(this,evt);
}})(obj[type])
};

// you can add one, two or more events
addSimpleEvent(window, "onload", function(){alert("Hello 2")});
addSimpleEvent(window, "onload", function(){alert("Hello 3")});


// if you want, you could create another function to add multiple events of same type
function addMultipleEvents() {
for(var i = 2, j = arguments.length; i < j; i++)
addSimpleEvent(arguments[0], arguments[1], arguments[i]);
}

// and use it in this way
addMultipleEvents(window, "onload",
function(){alert("Hello 4")},
function(){alert("Hello 5")},
function(){alert("Hello 6")}
);

Just test this script to view this sequence of alerts

Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6

What's about compatibility ? Every browser that supports call and doesn't loose base variable during execution, then IE 5.5+, FireFox, Opera, KDE, Safari and many others.

I hope this little function will be usefull for you window, document, or element common events.

anonymous function prototype

There are several scripts that use anonymous functions and we often use them as regulars.
Is there a way to have different or dedicated prototypes for this kind of function ?

Step 1, what is anonymous function ?
The "original" anonymous function is returned from the global Function object.

var myFunc = new Function("a", "b", "c", "return a + b + c;");
alert(myFunc(1,2,3)); // number 6

myFunc is a function with all Function prototypes or native methods and with the same constructor of a function.
Then myFunc is absolutely a function, but wich kind of function is it ? Exactly this one:

function anonymous(a, b, c) {
return a + b + c;
}

What's that ? That's a function that exists but you can't modify, get, or extend anyway because each new Function will produce a different referer for a different anonymous function.

(new Function).prototype.isAnonymous = true;
alert((new Function).isAnonymous); // undefined

You can try in a different ways creating for example a personal anonymous function, but the result will be the same ...

var anonymous = new Function;
(new Function).prototype.isAnonymous = true;
alert((new Function).isAnonymous); // undefined

// other way

function anonymous(){};
anonymous.prototype.isAnonymous = true;
alert((new Function).isAnonymous); // undefined

That's becaus, as I've just said, every anonymous instance is different from every other.
With FireFox you should view this difference using toSource Obect native method.

function anonymous(){};

var realanonymous = new Function;

alert([
anonymous.toSource(), // function anonymous() {}
realanonymous.toSource(), // (function anonymous() {})
anonymous === realanonymous // false
]);

As you can see, those parentheses are the key to understand the anonymous function.




Step 2, parentheses and virtual scope
At this point we know that an anonymous functions cannot have dedicated prototypes and aren't like regular functions too.
In some script you can see the use of parentheses to call runtime a function or to create one.
This code, for example, is an unobtrusive way to add a personal String prototype.

String.prototype.toArray = (function(base){
return base || function(){return this.split("")}
})(String.prototype.toArray);

alert("test".toArray()); // t,e,s,t

Let me explain this few lines of code.
If some script, before this one, has just defined a String.prototype.toArray or browser has a native String.toArray function, the anonymous function created using parentheses and directly called with (String.prototype.toArray) that accepts, if present, the base function, will assign to that prototype old version (base) or, if base is not defind, our prototype function (function(){return this.split("")}).
The closed anonymous function is then a special function and its really usefull to solve a lot of problems.
This is only a little example but I think you used anonymous functions every day with every scripts ;)
Since the scope inside parentheses "magically disappear", but neither for itsself nor for its internal scope, we can think that those kind of functions are exactly anonymous.

var myAnonymous = (function anonymous(){}),
realAnonymous = (new Function);

alert([
myAnonymous, // (function anonymous(){})
realAnonymous, // (function anonymous(){})
myAnonymous === realAnonymous, // false
myAnonymous.toSource() === realAnonymous.toSource()
// true !!!
]);


Step 3, how to create a dedicate prototype
JavaScript is Object Oriented and each function is an object, then why I couldn't use "special" anonymous functions as an object ?
That's why I've created a simple solution to have customizable anonymous functions, every one will be different from every other, but everyone will have our dedicate prototypes.
This is the concept function

// anonymous explicit function
function anonymous() {

// prototype to prototype,
// this function copy each a prototype (p) to other (b) function
function p2p(p,a,b) {

// using prototype for b too isn't a good solution (imho)
// because only new anonymous will has these prototypes
for(var k in a[p])b[k] = a[p][k];
return b;
};

// we need arguments and its length plus a genric array
var l = arguments.length, a = [];

// if argument is not one or its not a function
if(l !== 1 || arguments[0].constructor !== Function) {

// create the string ([arguments[N],...,arguments[0]])
while(l)a.push("arguments[".concat(--l,"]"));

// then reverse ...
a.reverse();

// ... to assign anonymous function to arguments 0
eval("arguments[0]=new Function(".concat(a.join(","),")"));
}

// return its "prototyped" version of anonymous function
return p2p("prototype", arguments.callee, arguments[0]);
};

The major difference from native new Function(arguments) is that my anonymous function accpets an anonymous function too.
Here there's a complete test to view some application.

// anonymous explicit function
function anonymous() {
function p2p(p,a,b) {
for(var k in a[p])b[k] = a[p][k];
return b;
};
var l = arguments.length, a = [];
if(l !== 1 || arguments[0].constructor !== Function) {
while(l)a.push("arguments[".concat(--l,"]"));
a.reverse();
eval("arguments[0]=new Function(".concat(a.join(","),")"));
}
return p2p("prototype", arguments.callee, arguments[0]);
};

// common Function prototype
Function.prototype.isFunction = true;

// only anonymous prototype
anonymous.prototype.isAnonymous = true;

// first test --------------------------------------
// new anonymous creation with the same sintax of new Function
test = anonymous("str", "alert(str)");

// just few checks
alert([
"" + anonymous.isAnonymous, // undefined, anonymous is a function
"" + test.isFunction, // true, test is a function
"" + test.isAnonymous // true, test is an anonymous function
]);

test("Hello World!"); // Hello World! [then test works perfectly]
// _________________________________________________



// second test --------------------------------------
// common function declaration using anonymous
test = anonymous(function(str){alert(str.toUpperCase())});

// just check it
alert([
"" + test.isFunction, // true, test is a function
"" + test.isAnonymous // true, test is an anonymous function
]);

test("Hello World!"); // HELLO WORLD! [then test works perfectly]
// _________________________________________________



// third test --------------------------------------
alert(anonymous(function(){}).isAnonymous); // true
// _________________________________________________



// final test --------------------------------------
anonymous(function(a){alert(a + arguments.callee.isAnonymous)})("Anonymous ? ");
// true
// _________________________________________________

Just a look at the last test, where is used arguments.callee instead of "this".
That's simply why the "this doesn't exists" inside the function ("this" inside a function is the window object).
That's all :)

Tuesday, September 19, 2006

studying google adsense code ...

PLEASE READ ME FIRST
This post is "a joke", then I hope you'll look at this as an ironical and hilarious post about google scripts and google developers that are really more skilled than me :)
I've found an error on my "best" script then I've looked for problem inside adsense code because with its code my script generates strange redirect errors (adsense code escapes objects or numbers too without string casting while my encodeURIComponent implementation didn't care about toString() method befor parsing ... I know, I've done a stupid thing :P )


studying code ...
3 days ago I've posted about an unknown problem using my JSL at the top of this blog.

The first point is ... sorry blogspot, you didn't cause any problem to JSL

The secondo point is this one: google adsense ... have you seen the code ?
To find the problem and then the solution I've downloaded adsense code to look inside that after a "manual code beautifier operation".
That's what I think about adsense code:
  • absolutely "undebuggable", to be lightweight (about 7Kb in a local file, maybe compressed on-line with gz, then 2Kb) variables and function names are really hard to understand
  • contains few errors, Opera as FireFox show everytime something inside the js console
  • it doesn't use a "perfectly" optimized code

Let's analyze adsense JS code, using this page as referer: google adsense beautified code.

The good thing is that all adsense code is inside an anonymous function, then every other script on the page will not be modified ... every but another google adsense script, because the use of window inside the script allows itself to create a big list of window.google_* variables.
This shouldn't be a problem, but if you use a script that does a for in loop with the window oblect, you need to rememeber that every /^google_/.test(param) should be leave as is.
It's time to view the internal function code, starting from optimizzations.
As you can read on many lines, for example on line 6, every if / else if / else uses curly brackets ... even when it's not necessary (every single operation after the condition).
But if you look at the line 295, someone uses correctly an "if" without braces ... who did this ? Maybe not the same developer ?
Since it's correct even with single line funtions, to optimize this script a lot of braces should be removed, adding where we need a ";" char.

// example with function B (starting from line 5)
function B(b){
if(typeof encodeURIComponent=="function")
return encodeURIComponent(b);
else
return escape(b);
}

Then I've just removed 2 chars from the size of the script but hey ... that function should be different!

// another example with function B
function B(b){
return typeof encodeURIComponent=="function"?encodeURIComponent(b):escape(b)
}

Another example is on line 153, where there is a ternary operation for var h but not for var q.

var h=a.google_ad_region==b?"":a.google_ad_region,q=j?j.indexOf("_0ads")>0:false;

After few lines (159) we can read another "unoptimized" piece of code that should be wrote in one line.

a.google_num_0ad_slots=!a.google_num_0ad_slots||a.google_num_0ad_slots+1>1?1:a.google_num_0ad_slots+1;

However I wonder when this piece of code should be usefull because google_num_0ad_slots is not present in this script, then it's maybe defined from other scripts.
If this is true or not, the if,else and then if does something like this:
if google_num_0ad_slots is not defined, or is null or is 0, google_num_0ad_slots should be 1, in every other case should be google_num_0ad_slots + 1 then it should be 0 if google_num_0ad_slots is less than zero.

if(!a.google_num_0ad_slots||++a.google_num_0ad_slots>1)a.google_num_0ad_slots=1;

These "if/else and then if again" I've just optimized are in different lines of the script but in some cases there is only an if else (i.e. 170 whre there's any "greater than 1" check)
In these cases the code should be

if(!a.google_num_ad_slots)a.google_num_ad_slots=0;
++a.google_num_0ad_slots;

or should be this one

if(!++a.google_num_ad_slots)a.google_num_ad_slots=1;

only if parameter is always initialized with 0 or greater value.

Another little optimizzation should be done on function F (line 73).
Since adsense code optimizzation is based on short var names I think that repeat for a lot of times the same object param prefix is not so good as a dedicated params array should be:

function F(b){
var a=[
"ad_frameborde","ad_format","page_url","language","gl","country","region","city","hints","safe",
"encoding","ad_output","max_num_ads","ad_channel","contents","alternate_ad_url","alternate_color",
"color_bg","color_text","color_link","color_url","color_border","color_line","adtest","kw_type",
"kw","num_radlinks","max_radlink_len","rl_filtering","rl_mode","rt","ad_type","image_size","feedback",
"skip","page_location","referrer_url","ad_region","ad_section","bid","cpa_choice","cust_age","cust_gender",
"cust_interests","cust_id","cust_job","cust_u_url"
],l=a.length;
while(l)b["google_"+a[--l]]=null;
}

In this way all properties are simple to add or to remove from the list, and "b.google_" is present just one time.
However if an optimized while should be slower to parse with a really big array, using o.param1=o.param2=o.paramN=null instead of "=a" for each param should be the same thing.

AdSense script uses a lot of returns in-function, that is a method I don't like very much (but it's only my opinion and using only one return isn't a better way to write functions).
For example there's a "special" function I've seen that's not good enought for me, it is the x function (line 293).

function x(b,a){
var d=a.documentElement,r=z(b,a,"location"),g=1,e=1;
if(!r&&b.google_ad_width&&b.google_ad_height){
if(b.innerHeight){
g=b.innerWidth;
e=b.innerHeight
}
else if(d&&d.clientHeight){
g=d.clientWidth;
e=d.clientHeight
}
else if(a.body){
g=a.body.clientWidth;
e=a.body.clientHeight
}
r=(e>2*b.google_ad_height||g>2*b.google_ad_width)
}
return !r
}

If you look at the original version you can view that if z is true (then r in my version), function returns false.
Then if !r (when r is not true) it's possibile to do other operations inside the first if condition.
At the end of the first if you can assign a boolean value without the if and the second in-function return.
Then if r is true, the final return value is false. It's true for the first check as for the second, then in every other cases, when r is not true, returned value will be true (not false).

This is the way I usually like to return a boolean value from a function or method using only a single return (cleaner, imho) at the end of the function.

We are going to the end of this post, there's only another function I've not understand ... the C function (line 286).
As you can see C function recieves 3 parameters, any of these is used, A function is called and true value is returned.
Do you think it's usefull ? I think that A function, that doesn't have any input parameters and doesn't return anything, should return true value and should be used directly on line 325.

b.onerror=A;

... adding return true on A function ... then anyone doesn't need the C function (but maybe it was created for future implementations).

The absolute last thing I want to tell to google AdSense script developers is this one:
why do you optimize in this way the code but you use "var" for every temporary function variable ?

Look at the line 319, inside the function E ... wasn't better something like ...

var b=window,a=document,d=a.location,g=a.referrer,e=null;

??? it's the same with A, D and other functions ...

Monday, September 18, 2006

portable and rewrote onmousewheel function

I did a simple version of onmousewheel, then I add JSL and $DOM objects dependencies, then I've came back to single portable version without dependencies that's simpler than first one :D

Concept:
onmousewheel isn't a window or document event, onmousewheel is a generic Element event. While a generic event as onclick, onmouseover, onmouseout is called only above the element, onmousewheel will be called only above the element too, that's all!

No more double events (onmouseover that activates onmousewheel and onmouseout that deactivates onmousewheel) ... just the event.


/**
* function onmousewheel,
* onmousewheel(element:Object [, callback:Function]):Void
* @param Object window, document or DOM.element to use with callback
* @param Function callback function with element scope (.call(...)) and delta wheel value as single parameter
* @return Void
*/
function onmousewheel(element, callback) {

// @author Andrea Giammarchi [http://www.devpro.it/]
// @license MIT [http://www.opensource.org/licenses/mit-license.php]
// @credits Adomas Paltanavicius [http://adomas.org/javascript-mouse-wheel/]

function __onwheel(event) {
var delta = 0;
if(event.wheelDelta) {
delta = event.wheelDelta / 120;
if(window.opera)
delta = -delta;
}
else if(event.detail)
delta = -event.detail / 3;
if(delta)
callback.call(element, delta);
if(event.preventDefault)
event.preventDefault();
event.returnValue = false;
return false;
};

if(element.addEventListener && !window.opera)
element.addEventListener("DOMMouseScroll", __onwheel, false);
else
element.onmousewheel = (function(base){return function(evt){
if(!evt) evt = window.event;
if(base) base.call(element, evt);
return __onwheel(evt);
}})(element.onmousewheel);
};


And here you can view the always updated version or the example page.

too much simple DOMContentLoaded solution ?

Dean Edwards closed comments in this page (sorry Dean) , but I wonder if there is a real example page where this alternative way to implement DOMContentLoaded doesn't work as expected (please post me one !!!).

My simple solution is this one, anonymous function with multiple callbacks after document.body is not undefined.


(function(){if(document.body){for(var i=0;i<arguments.length;i++)arguments[i]();}else setTimeout(arguments.callee,1)})
(initLightbox, otherFunc, somethingElse, init);



Just 2 simple lines of code, but for some reason it shouldn't work correctly in some case.

Then, while my test page doesn't fail this method, at least with my browsers, I'd like to know when this way shouldn't work correctly or when this way should work (I always prefere to reduce JS size then if generic cases work correctly with this method to implement DOMContentLoaded I'll prefere this one).

Can anyone explain me what's up when this method fail ? Thank you.

Sunday, September 17, 2006

unoubtrusive presentation ?

Flash teachs us to add a skip intro button inside presentations.

I was joking with big dollar function and FakeDOM (or FastDOM ?) alpha libraries and I've thought to add a view intro button, using a page block with a div and calling an image to present this site.

Here you can try to view the result, do you like it ?

Saturday, September 16, 2006

Big dollar $ function to solve standards

Maybe the best and elegant way to get one or more document element as you know.

The $ function is used from a lot of JS developers, it's simple, fast and allow us to reduce scripts size (forget document.getElementById).

Then that's my idea: why we can't use $ to add element standards ?

Every element that's grabbed with $ function should be parsed by function to improve Element standards ... and why not, to add special features for each element.

Here there's an example: big $ function where you can view my first standard implementation, addEventListener and removeEventListener for every browser.

It's not based on attachEvent because attachEvent doesn't works with element scope (this is not the element inside an event listner function ... and it's terrible !!!) and this alpha version manages correctly whellscroll too for IE6, FireFox or Opera 9.

This function will be extended to add a lot of standard Element methods but only if this is a good solution or a good idea, then I'm waiting for some reply :)

Finally, here there's alpha code for a big $ function

function $() {
// (C) Andrea Giammarchi - alpha release
if(!window.$_$)
window.$_$ = {
elementsList: [],
eventsList: {
DOMActivate :"ondomactivate",
DOMAttrModified :"onattrmodified",
DOMCharacterDataModified:"oncharacterdatamodified",
DOMFocusIn :"ondomfocusin",
DOMFocusOut :"ondomfocusout",
DOMMouseScroll :"onmousewheel",
DOMNodeInserted :"onnodeinserted",
DOMSubtreeModified :"onsubtreemodified",
NodeInsertedIntoDocument:"onnodeinsertedintodocument"
},
$A: function(obj) {
var i = obj.length, result = [];
while(i)result.push(obj[--i]);
return result
},
elementsManager: function(element, eventName, callback, listener) {
var i = 0;
if(!this.elementsList.some(function(obj, j){var b = obj.node === element; if(b)i = j; return b}))
i = this.elementsList.push({node:element,events:{}}) - 1;
callback(this.elementsList[i], eventName, listener);
},
attachEvent: function(element, eventName, listener) {
if(!element.events[eventName])
element.events[eventName] = [];
if(element.events[eventName].indexOf(listener) < 0)
element.events[eventName].push(listener);
element.node[eventName] = function(event) {
element.events[eventName].forEach(
function(listener){
if(event) listener.call(element.node, event);
else listener.call(element.node, window.event);
}
)
}
},
detachEvent: function(element, eventName, listener) {
if(element.events[eventName])
element.events[eventName] = element.events[eventName].filter(function(lst){return lst!==listener});
},
temporaryMethod: function(methodName, element, base, eventName, listener, useCapture) {
methodName.push(element[methodName[0]]);
element[methodName[0]] = base;
element[methodName[0]](eventName, listener, !!useCapture);
element[methodName[0]] = methodName.pop();
},
eventsManager: function(addEvent, element, base, eventName, listener, useCapture) {
var methodName = addEvent ? ["addEventListener", "attachEvent"] : ["removeEventListener", "detachEvent"];
if(this.eventsList[eventName]) {
if(base && !window.opera)
this.temporaryMethod(methodName, element, base, eventName, listener, useCapture);
else
this.elementsManager(element, this.eventsList[eventName], this[methodName[1]], listener);
}
else {
if(base)
this.temporaryMethod(methodName, element, base, eventName, listener, useCapture);
else
this.elementsManager(element, "on".concat(eventName), this[methodName[1]], listener);
}
}
};
var elements = window.$_$.$A(arguments);
elements.forEach(function(element, i){
if(element.constructor === String)
elements[i] = document.getElementById(element);
if(window.$_$.elementsList.indexOf(elements[i]) < 0) {
window.$_$.elementsList.push(elements[i]);
elements[i].addEventListener = (function(base) {
return function(eventName, listener, useCapture) {
window.$_$.eventsManager(true, this, base, eventName, listener, useCapture);
}
})(elements[i].addEventListener);
elements[i].removeEventListener = (function(base) {
return function(eventName, listener, useCapture) {
window.$_$.eventsManager(false, this, base, eventName, listener, useCapture);
}
})(elements[i].removeEventListener);
}
});
return elements.length === 1 ? elements[0] : elements
};

blogspot modified my library

It's true ... JSL cannot be included at the top of this blog space (howevwer thanks for this free blog system).

Try yourself, include JSL inside head tag and .... WOW, your blog will "disappear".

That's another reason to base libraries or every kind of script on JSL that doesn't modify anything, if present.

JSL is an unobtrusive way to implement JS 1.6, not the unique way to use some personal prototypes.

To add my JSHighLighter, that requires JSL, in this blog I had to include external JS sources at the bottom of the blog model ... come on blogspot, trash your String.replace or encodeURIConponent implementation and use JSL :D


P.S. I don't know what was modified, I saw a redirect to an invalid url created by some blogspot script.

Friday, September 15, 2006

Why we need a JavaScript Standard Library

JavaScript 2.0 is on the air but in a lot of js oriented sites I can see every day show often the same, obvious or boring prototypes to normalize some old browser before every kind of different solution for every different problem.

Yesterday, for example, I've read about a "revolutionary" way to search a value inside an array, using an "horrible" Object solution.

If you read comments you could see that Array has a native, standard, prototype called indexOf that does exactly what that blog was talking about.

Well, why do you like to spend your time for these things ?

You like that because you don't know all JavaScript 1.6 objects methods or you don't use FireFox to develop your code and you don't have a low level code normalizer library.

However, one of the first comments shows the simplest solution for the problem, indexOf, but shows another Array.prototype normalizer function.

This is just one example of all posts that you can find on the net and everytime someone posts a Something.prototype normalizer solution for that problem, for that library.

Maybe in your 3rd part page scripts you have at least 2, 3 or more equal prototypes that normalize this, that or other library ... or recreate always the same result with a different name.

Don't you have enought ? Don't you care about sum of every script size ?

I wonder why JSL hasn't been linked from any "javascript specialist site" because to write a standard code, using for example JSLint, isn't enought to produce good, fast or optimized code.

I've talked about my JSL to Dojo developers too, to resolve a lot of common JS portability problems and to resolve encodeURIComponent or decodeURIComponent top-level function portability that are quite perfect only with JSL, as String.replace with function as second argument is.
Dojo developers didn't care about JSL ... they don't need another normalizer library (but they have a propetary normalizer library ... that doesn't make JS more standard for every other lib in a 100Kb of packed code lobrary ... ).

Prototype has its normalizer proto too but these aren't standard.
Array.each is not standard (while JS 1.6 has Array.forEach, that's a standard method), indexOf is not complete (and I havn't seen the String.indexOf implementation) ... then why rewr everytime the same normalizer prototype ?

Why increase every library with its normalizer prototype when JavaScript 1.6 has at least one of every normalizer proto used inside every library ?

Do you think this is a good way to depends everytime from each different library ?
Do you think your library is the "only cool one" and then every developer should learn a different (but same) implementation of every different library normalizer prototypes ?

We don't like IE because it doesn't respect standards, every JS library I've seen doesn't respect ECMA standards too.

Why don't you use JSL and then ECMA Standards for JS 1.6 ?

When finally all browsers will be compatible at least with JS 1.6, remove JSL will be a simple step while using prototype or other libraries dedicated implementation of "un-standard" ECMA will not be possible.

Don't you like this point of view ? Don't you like standards ?

Do you need some example ?


// original prototype versionfunction
$() {
var elements = new Array();
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if (typeof element == 'string')
element = document.getElementById(element);
if (arguments.length == 1)
return element;
elements.push(element);
}
return elements;
}

// JSL and prototypefunction
$() {
var elements = $A(arguments).forEach(function(element, i){
if(element.constructor === String)
elements[i] = document.getElementById(element);
});
return elements.length === 1 ? elements[0] :elements
}

// getElementsByClass from Top 10 JavaScript Function (Dustin Diaz)
function getElementsByClass(searchClass,node,tag) {
var classElements = new Array();
if ( node == null )
node = document;
if ( tag == null )
tag = '*';
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var pattern = new RegExp('(^|\\s)'+searchClass+'(\\s|$)');
for (i = 0, j = 0; i < elsLen; i++) {
if ( pattern.test(els[i].className) ) {
classElements[j] = els[i];
j++;
}
}
return classElements;
}

// JSL versionfunction getElementsByClass(searchClass,node,tag) {
var classElements = [],
els = (node || document).getElementsByTagName(tag || "*"),
pattern = new RegExp('(^|\\s)'+searchClass+'(\\s|$)');
for(var i = 0, j = els.length; i < j; i++)
classElements.push(els[i]);
return classElements.filter(function(element){
return pattern.test(element.className)
})
}

// JSL and $A prototype versionfunction getElementsByClass(searchClass,node,tag) {
var pattern = new RegExp('(^|\\s)'+searchClass+'(\\s|$)');
return $A((node || document).getElementsByTagName(tag || "*")).filter
(function(element){return pattern.test(element.className)
})
}

// inArray from Top 10 JavaScript Function (Dustin Diaz)
Array.prototype.inArray = function (value) {
var i;
for (i=0; i < this.length; i++) {
if (this[i] === value) {
return true;
}
}
return false;
};

// unusefull inArray implementation with JSL
Array.prototype.inArray = function (value) {
return this.indexOf(value) !== -1
}

// maybe more usefull inArray (has) version with multiple arguments
Array.prototype.has = function() {
var i = arguments.length, result = [];
while(i)
result.push(this.indexOf(arguments[--i]) !== -1);
return result.every(function(e){return e})
};// ...


Finally, if a lot of developers think that top 10 JS funcs should be inside a common.js, why they don't think that a low-level lib as JSL is should be a must before every every top-ten common.js ?

It's a single file, as a common.js file should be, it's lightweight, it's to develop every kind of medium or high level lib or code using standards, it's a must to write code for FireFox and use it with every other browser .... then why don't you like my JavaScript Standard Library ?

My Blog (finally!)

[eng]
Hello guys, welcome in my blog space :)

What about ? JavaScript, PHP, C#, Python, XHTML, CSS and some special comment for some of my scripts/codes/libraries (http://www.devpro.it/)

See you bloggers!


[ita] Finalmente ho deciso di spostarmi su un circuito bello e pronto, do l'addio momentaneo al vecchio blog http://www.3site.it/blog/ che per problemi di tempo e di ... tempo, non sono mai riuscito a sviluppare per intero, qui invece รจ tutto mooooolto comodo ;)