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

Wednesday, November 29, 2006

W3 Validator, DOM, byteson and PHP

I've uploaded a new byteson example page, based on a single and simple request to a PHP page that uses W3 Validator service, calling a SOAP result, parse them to convert it into an associative array and finally send it with JSON to byteson.

Here is the PHP code

<?php
// byteson filter
if(isset($_POST['byteson'])) {

// http://www.phpclasses.org/browse/package/3512.html
require 'FastJSON.class.php';

// http://www.devpro.it/code/143.html
require 'W3validator.function.php';

// remove magic quotes if th it's present
// magic_quotes is a problem for JSON strings
if(get_magic_quotes_gpc())
$_POST['byteson'] = stripslashes($_POST['byteson']);

// get the output
$output = FastJSON::encode(W3validator(FastJSON::decode($_POST['byteson'])));

// force new content to download
header('Content-Type: text/plain; charset=utf-8');
header('Content-Length: '.strlen($output));
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');

// exit writing JSON object for byteson
exit($output);
}
?>

This code is above the xHTML output and You can view them by yourself, viewing the example page.

This php page uses two other files, my FastJSON class and my last function on devpro, called W3validator (what a fantasy ...).

The JavaScript code is this one

// new browsers filter
if(document.getElementsByTagName)
onload = function(){ // (C) Andrea Giammarchi

// assing tabindex and accesskey
function accesstab(input){
input.accesskey = input.tabindex = ++accesskey;
++tabindex;
};

// assign generic link properties
function addLinkProperties(a){
tab(a);
a.href = "#";
a.onclick = function(){
var ul = this.parentNode.getElementsByTagName("ul").item(0);
ul.style.display = !!ul.style.display ? "" : "none";
return false;
};
return a;
};

// append one or more elements into another
// then return the "parent" element
function append(parent, elements){
if(elements.constructor !== Array)
elements = [elements];
for(var i = 0; i < elements.length; i++)
parent.appendChild(elements[i]);
return parent;
};

// create a list if there are errors or warnings
function createList(message, obj){
for(var a, span, i = 0, key = "", list = [], tmp = []; i < obj.length; i++) {
for(key in obj[i]) {
span = node("span");
span.innerHTML = obj[i][key];
tmp.push(append(
node("li"), [
append(node("strong"), text(key)),
span
]
));
};
a = addLinkProperties(append(node("a"), text(message.concat(i + 1))));
list.push(append(node("li"),[a, append(node("ul"), tmp)]));
tmp = [];
};
return append(node("ul"), list);
};

// perform the request using byteson
function doRequest(oldtab){

// just call the server side page
// sending input string and adding just
// one listener ( because I'm sure, this application will works "perfectly" :P )
byteson.request("w3validator.php", inputuri.value, {

// onload event
load:function(obj, xhr){
var a, key = "";

// remove the loading text
resultlist.removeChild(resultlist.firstChild);

// create a list with every key value ...
for(key in obj) {

// but not with Arrays (errors or warnings)
if(obj[key].constructor !== Array) {
append(
resultlist,
append(
node("li"), [
append(node("strong"), text(key)),
append(node("span"), text(obj[key]))
]
)
);
};
};

// create last list with each error or each warning
for(key in obj) {
if(obj[key].constructor === Array && obj[key].length > 0) {
a = addLinkProperties(append(node("a"), text(key)));
append(
resultlist,
append(
node("li"), [
a,
createList(key.replace(/list/, ' #'), obj[key])
]
)
);
a.onclick();
};
};

// enable the form
inputuri.disabled = senduri.disabled = false;

// set old tabindex for next request
// (correct tab navigation ready)
tabindex = oldtab;
}
});
};

// create and return an element of specified type
function node(type){return document.createElement(type)};

// add tabindex property using global scpe tabindex integer value
function tab(node){node.tabindex = ++tabindex};

// create and return a text node with specified content
function text(value){return document.createTextNode(value)};

// internal global scope variables
var accesskey = 0, // accesskey (WCAG ready ?)
tabindex = 0, // tabindex (tab navigation ready)
output = document.getElementById("request"), // div container
form = node("form"), // used form
inputlabel = node("label"), // label for input text
inputuri = node("input"), // input text
sendlabel = node("label"), // label for send button
senduri = node("input"), // send button
resultlist = node("ul"); // result unordered list

// assign accesskey, tabindex and other parameters for each input (in this case text and btn)
accesstab(inputuri);
inputuri.type = "text";
inputuri.id = inputlabel["for"] = "userinput";
// keyboard event listener (RETURN to perform the request)
inputuri.onkeyup = function(evt){
if(!evt)
evt = window.event;
if(!evt.wich)
evt.wich = evt.keyCode;
if(evt.wich === 13 && this.value.replace(/^\s*|\s*$/, '').length > 4)
senduri.onclick();
};

accesstab(senduri);
senduri.type = "button";
senduri.id = sendlabel["for"] = "sendinput";
senduri.value = "verify";
// request function
senduri.onclick = function(){
var newresult = node("ul");

// disable the form
inputuri.disabled = senduri.disabled = true;

// blur this button
senduri.blur();

// remove precedent unordered list element and replace
// replace them with a simple "loading response" message
// inside a new unordered list element
resultlist.parentNode.replaceChild(newresult, resultlist);

// assign new ul element to global scope var (to change them on load)
resultlist = newresult;
append(resultlist, append(node("li"), text("loading response")));

// perform the request with byteson
doRequest(tabindex);
return false;
};

// form should be disabled, by default
// (the form doesn't degrade in this example)
form.onsubmit = function(){return false};

// create the output only for the first time
output.replaceChild(
append(
form, // the form
append(
node("fieldset"), [ // top fieldset
append(
node("legend"), // and its legend
text("W3 Validator") // with its content
),
append(
inputlabel, // label for text input
text("write a valid url address") // and its content
),
inputuri, // input text
append(
sendlabel, // label for button
text("verify W3 validator result") // and its content
),
senduri // send button
]
)
), output.firstChild);

// create result fieldset to show informations
// append this fieldset after the precedent output
append(
output,
append(
node("fieldset"), [ // bottom fieldset
append(
node("legend"), // and its legend
text("W3 Response") // with its content
),
resultlist // and its unordered list
] // to show the result
)
);
}; // that's all


The last file is the external stylesheet used in this page.

This basic example can explain better than every post, how W3validator PHP function works and how much is simple to implement byteson inside other JS code.

Have fun with W3 validation ;-)

Friday, November 24, 2006

My Last DOMContentLoaded Solution

Update 28/11/2006 Thanks to anonymous for its perfect suggest!!!
Update 01/12/2006 Thanks to Stephen Elson for debug
Update 01/13/2007 This is my last version that doesn't use global window.__onContent__ variable
Update 13/02/2007
Sorry guys for my choosed old title, thanks to Pierluigi that explained me what did it mean




function onContent(f){//(C)webreflection.blogspot.com
var a=onContent,b=navigator.userAgent,d=document,w=window,c="onContent",e="addEventListener",o="opera",r="readyState",
s="");
a[c]=(function(o){return function(){a[c]=function(){};for(a=arguments.callee;!a.done;a.done=1)f(o?o():o)}})(a[c]);
if(d[e])d[e]("DOMContentLoaded",a[c],false);
if(/WebKit|Khtml/i.test(b)||(w[o]&&parseInt(w[o].version())<9))(function(){/loaded|complete/.test(d[r])?a[c]():setTimeout(arguments.callee,1)})();
else if(/MSIE/i.test(b))d.write(s);
};



Here is where this "story" began, inside a Dean Edwards post.
After that there was a big list of comments, posts and tests ... for a second post and a better solution !

Dojo adopted that solution ... then came back to old one ... but someone has never stopped to test, to try or to find a "perfect" way to add DOMContentLoaded with every browser and expecially with Internet Explorer.

Mark Wubben and Paul Sowden did it, they've created a complete and portable solution that works with http and https pages too !

Wonderful work guys and ... as first point, thank you very much !


Now, I can show my personal DOMCOntentLoaded solution, explaining them as better as I can.


The first step, the real key of this code, is the source of used script.

It's not void, it's not //0 ... it's exactly this one: //:
and is what I had not to create my tiny, simple and portable solution.

Here is the func, called onContent ...

function onContent(f){//(C)webreflection.blogspot.com
var a,b=navigator.userAgent,d=document,w=window,
c="__onContent__",e="addEventListener",o="opera",r="readyState",
s="<scr".concat("ipt defer src='//:' on",r,"change='if(this.",r,"==\"complete\"){this.parentNode.removeChild(this);",c,"()}'></scr","ipt>");
w[c]=(function(o){return function(){w[c]=function(){};for(a=arguments.callee;!a.done;a.done=1)f(o?o():o)}})(w[c]);
if(d[e])d[e]("DOMContentLoaded",w[c],false);
if(/WebKit|Khtml/i.test(b)||(w[o]&&parseInt(w[o].version())<9))
(function(){/loaded|complete/.test(d[r])?w[c]():setTimeout(arguments.callee,1)})();
else if(/MSIE/i.test(b))d.write(s);
};


... and this is the http test page, while this is the https test page.



Let me explain that unreadable function with a clear commented version :)


function onContent(callback){ // (C) webreflection.blogspot.com
// [please note that this code doesn't work]

// private scope variable

var IEStringToWrite = // this is IE dedicated string

"<script defer src='//:' onreadystatechange='
(function(element){

// if readystate is complete
if(element.readyState === "complete") {

// remove the element
element.parentNode.removeChild(element);

// call the global variable
window.__onContent__();
}
})(this);
'></script>";

// the above string is necessary to use onreadystatechange property
// with an undefined page. In this way IE tell us the readyState
// of the current document



// to call callback function IE need a global scope variable
// this variable could call one or more callback
// then if it's already created we need to call the old callback
// then this new callback
window.__onContent__ = (function(oldCallback){

// returns a function that will set every callback fired
// [thanks to Stephen Elson for its suggest]
// to remove multiple callbacks with different
// events and different ways for each browser

return function(){

// set window.__onContent__ as empty function
// required by IE5
window.__onContent__ = function(){};

// verify that callee wasn't fired before
if(!arguments.callee.done) {

// set calle.done 1 as true fired value
arguments.callee.done = 1;

// checks if oldCallback isn't null or undefined
if(oldCallback)
oldCallback(); // call them to preserve the right order

callback(); // call this scope callback function
// (sent calling onContent)
}
}

})(window.__onContent__); // undefined if is the first time we use __onContent__



// __onContent__ is my function to use as callback

// I need to add this function as event

// Opera 9 and FireFox both support DOMContentLoaded as well as
// addEventListener document method
if(document.addEventListener)
document.addEventListener("DOMContentLoaded", __onContent__, false);

// if some browser supports addEventListener but doesn't support DOMContentLoaded
// event I don't need to care about that because this event will never be fired

// at the same time if Safari or KDE one day will support DOMContentLoaded
// I prefere use this dedicated in-core
// event instead of next trick that's quite horrible but works with Safari,
// KDE as Opera 8.5 and lower too

// that's why I don't use an else if but an if ... because the first time
// event will be fired __onContent__
// became an empty function ... then calling them twice is not a problem

if(
// Safari and KDE
/WebKit|Khtml/i.test(navigator.userAgent) ||

// Opera less than 9
(window.opera && parseInt(window.opera.version())<9)
)
// runtime anonymous function
(function(){

// checks if document.readyState is loaded or complete
/loaded|complete/.test(document.readyState) ?

// then call __onContent__ , stopping internal loop
window.__onContent__() :

// or loops itself with the faster timeout
setTimeout(arguments.callee, 1);
})();

// at this point I've setted the DOMContentLoaded event for every browser
// but not for Inernet Explorer.
else if (/MSIE/i.test(navigator.userAgent))

// I can write dedicated string
document.write(IEStringToWrite);
};



My solution doesn't use conditional comments (I hate them !!!) ... and this is the compatibility list:

- FireFox 1 or greater
- Opera 8 or greater (I don't know 7)
- Safari 2 or greater (I don't know 1)
- KDE 3.4 or greater
- Internet Explorer 5 or greater (I don't know IE 5.2 for Mac)

Tuesday, November 21, 2006

Wall Street and GOOG , the day after the record

This is why they show beta logo, this is what should happen to bigs too.




Who caused the problem ? :)

Monday, November 20, 2006

CSS dropshadow ? Sure, simple and unobtrusive !

My favourite italian portal wrote a post about a CSS based dropshadow solution.
It talks about this webtoolkit.info post.

I didn't know that article then only now I can tell You something about that:

  • graphic effects are good "only" for high bandwidth web surfers
  • dropshadow isn't important as page content
  • id attribute should be absolutely unique
  • markup language has a scope and should be used for this scope (sure, when it's possible)
  • 5 nested divs just to show a simple shadow effect aren't a cool solution (imho)


Since You can do the same thing using a really basic JavaScript function without a repeated id, nested obsolete elements and in a cross browser way with about 1.8 Kb of uncompressed and goo JavaScript, I think You should think about my alternative proposal.

The only things You need is a better CSS, not based on id but based on class name and a simple callback.
This is revisited CSS

/* CSS container shadow */
.shadow-container {
position: relative;
left: 3px;
top: 3px;
margin-right: 3px;
margin-bottom: 3px;
}
.shadow-container .shadow2,
.shadow-container .shadow3,
.shadow-container .container {
position: relative;
left: -1px;
top: -1px;
}
.shadow-container .shadow1 {
background: #F1F0F1;
}
.shadow-container .shadow2 {
background: #DBDADB;
}
.shadow-container .shadow3 {
background: #B8B6B8;
}
.shadow-container .container {
background: #ffffff;
border: 1px solid #848284;
padding: 10px;
}
/* CSS container shadow */


And this is simple callback example:

onload = function(){
createDropShadow(
getElementsByClassName(
document, "div", "dropshadow"
)
);
};


Now You can use a dedicated dropshadow div class name in every div that You want to modify.

<div class="container dropshadow">
Lorem Ipsum ...
</div>


That's all, just include ej basic file and my dedicated function and You'll have a cleaner page without nested divs overloads and ... bye bye nested divs :)

Thursday, November 16, 2006

New Ajaj library on the net ! Welcome byteson

byte familiy is growing up and it's first "son" is called byteson.

byteson is a lightweight Ajaj library that could send and recieve in a simple way every JSON compatible variable.

byteson is server side language indipendent and it's naturally compatible with UTF-8 charset.

Finally, byteson is small, less than 2Kb Memtronic Version, about 3Kb packed and should be the only thing You need to create advanced client-server interaction.

Do You like it ?

byteson logo

Here You can find a portable txt summary of this library :)
byteson.txt

Monday, November 13, 2006

Unobtrusive Password Security Level Form

Few days ago I've put two JavaScript and PHP simple functions on devpro.it, called password security.

Today I would show You hot to use thoose functions, creating a valid W3 and WatchFire form to set or update one password with security level informations.
Security Level is a simple graphic/textual element used, for example, when You create a Windows Live account.

This kind of information is really useful for users because they've to choose at least a medium security password to have a "secure" account.

That's why I've created this example page and now I'm telling You how I did them.



First step, external files
I've saved PHP and JavaScript dedicated functions in different files.
Then I've created an external CSS file too and a simple graphic bar:
psl graphic bar

It's dimension are the same of element that will show this bar.

CSS important things are p.pwdlevelN informations, used to show background image with differents positions.



Next step, procedural PHP code
Example page uses few PHP lines of code, and thoose need to show different pages if user hasn't JavaScript support (or He has JS disabled) with or without a notice message.
This is used code:


// array with every password security levels
$security_levels = array('not secure', 'low security', 'medium security', 'high secutiry');

// if user choosed a password
if(isset($_POST['pwd'])) {

// include psl function [http://www.devpro.it/code/141.html]
require 'psl.php';

// get security level
$level = psl($_POST['pwd']);

// create an element with security level informations
$security_informations =
'

'.$security_levels[$level].'

';

// show message if password has been approved
if($level > 1) {
$security_informations .= 'password successful setted
';

// do stuff, for example change password on db
}
else
// just show a message about bad security
$security_informations .= 'please choose a better password
';
}
else {

// create default element
$security_informations = '

';
}

// create output
?>

As You can see, this code is really simple and sure, basic too but is useful to understand why whe need PHP to create an unobtrusive JS like page.



Next step, JS code, using PHP too
Since I've defined an array without special chars, I could use them to create same JavaScript array too while other code is used to enable or disable submit button showing security level under the password input.

// returns an element by id
function $(id){
return document.getElementById(id);
};

// checks password security, enables submit if it's secure
function checkSecurity(inputId, securityId, submitId){

// array with every password security levels (simple with a php variable)
var security_levels = ["<?php echo implode('","', $security_levels); ?>"];

// get security level
var level = psl($(inputId).value);

// create an element with security level informations
var security_informations = document.createElement("P");
security_informations.id = securityId;
security_informations.className = "pwdlevel".concat(level);
security_informations.appendChild(document.createTextNode(security_levels[level]));

// switch the old information element with new one
var oldinformations = $(securityId);
oldinformations.parentNode.replaceChild(security_informations, oldinformations);

// enable submit if password is secure enought
$(submitId).disabled = level < 2;
};

// clean pwd value and disable the submit
onload = function(){
$("userpwd").value = "";
$("setpwd").disabled = true;
}

Just a note about DOM, Internet Explorer is not compatible with standard (what a news ...) setAttribute HTMLElement method, then I've used direct assignment for id and className.




Final step, W3 / WCAG compilant layout
To show security level and to choose the new password, I've used a standard output that should be compatible with every browser from IE 5 to 7, FF 1 to 2, Opera 8 and 9 and maybe 7 too.
I've not tested KDE, Safari and Camino but I hope these browsers will don't have any problem with simple code used.
You can see full used code in this page.



I hope this example page will be a start point to create your own password security level area.

Saturday, November 11, 2006

Which function to get an XMLHttpRequest object ?

Ajax is "quite old" but I've not found any crossbrowser clean or standard way to create and return the correct XMLHttpRequest.

I wrote a standard crossbrowser function in my italian Ajax Guide (http://javascript.html.it/guide/leggi/95/guida-ajax/) but someone, every day, try to discard that function or to create a "new way" to get that object.

My opinion is this one:
- You don't need any conditional comment, these aren't a good code practice
- You don't need any evil eval function ... You can do your check without problems
- You don't need any try catch, first because they're not compatible with old browsers, second because You can simply check browser version and then choose the correct ActiveX, if it's necessary


Then, if You're looking for a simple, fast and cross browser way to get XMLHttpRequest You could just use this function:


function xhr(){ // webreflection.blogspot.com
var xhr = null,
b = navigator.userAgent;
if(window.XMLHttpRequest)
xhr = new XMLHttpRequest();
else if(!/MSIE 4/i.test(b)) {
if(/MSIE 5/i.test(b))
xhr = new ActiveXObject("Microsoft.XMLHTTP");
else
xhr = new ActiveXObject("Msxml2.XMLHTTP");
};
return xhr;
};


Every compatible browser, FF 1+, Opera 8+, Safari 2+, KDE 3.4+, IE 5+, will get a valid XMLHttpRequest while every other browser, for example IE4, will recieve a null value.
Then You can simply verify if client browser support Ajax request using a sintax like this one:

var ajax = xhr();
if(ajax)
// do every async interaction ...


That's all, I hope this will be helpful :)

Friday, November 10, 2006

PHP and JavaScript encoding comparison table

PHP and JavaScript are used every day for sync or async interactions.

I often read functions or libraries that use JS escape function and I often read forum discussions where someone writes that to solve chars problems You need to use escape function.

That's wrong, as is the usage of urlencode instead of rawurlencode.



What's wrong on PHP urlencode function ?
urlencode works quite perfectly with different charsets but has a big problem expecially with one char, the space char " ".
This char is not escaped but is replaced with a "+" sign.
If You write an url with this sign instead of a space there are no problems but if write a response for JavaScript with this char there are different problems.

As You can see from this comparative table JavaScript escape or encode perfectly the space char but doesn't escape the plus sing "+".
This means that when You'll unsecape an urlencoded string it will convert plus sign correctly.

To solve this simple but common problem, when You need to encode a PHP string, use always the raw version: rawurlencode.




What's wrong on JavaScript escape function ?
It has more and more problems than urlencode PHP function because it is an old function (too much) with the best encoding browsers compatibility but without unicode support.
The first point is that it doesn't escape correctly the plus sign, then if You send an escaped string with a simple addiction "1 + 2 = 3" to PHP page, it will recieve a string like this one "1 2 = 3" because PHP urldecode function converts plus sign into a space.
However, the real big problem with escape is that it doesn't support unicode and it converts "correctly" only ASCII chars.

For example at the end of comparative table You can view that escape converts only in range 0x00 - 0xFF but doesn't convert in UTF-8 compatible way every char in range 0x80 - 0xFF and produces, for example, a string like "%E0" that's not the correct multibyte utf-8 rappresentation of char #224.

If You read under this line You can see that escape cannot produce a compatible output for unicode chars in a cross language compatible way.

I mean that every multibyte char in range 0x0100 - 0xFFFF will be converted into a fake JSON rappresentation: %u0100 - %uFFFF that isn't absolutely a correct encoded char and isn't compatible with any other language or url string specs too.

You can use escape only if You send, and recieve, simple strings like [0-9a-zA-Z] with some extra char ( \w ) but isn't absolutely a portable and correct way to create iso or utf-8 PHP and JavaScript interactions.

Tuesday, November 07, 2006

not yield and the shortest Fibonacci function

I was joking with new JS 1.7 features, well documented in this page, when I've thought about yield keyword.

It's a strange keyword because isn't usable as the rest of JS code.

For example, alert(yield variable) generates an error but alert((yield variable)) shows an undefined value.
(I know, it is quite idiot to think that it should have some value ...)

Then I've done some test:

function fakeGenerator(){
var i = 0;
while(true)
// alert(yield i++); // error
// alert((yield i++)); // undefined
yield i++; // ok
};


As you can see, (yield something) returns an undefined value and then this is a true expression.

!(yield variable) === true


Now You can think that a while loop should use this condiction instead of while(true) {yield variables;} ... for example:

function fakeGenerator(i){
while(!(yield i++)){}
};

works perfectly then maybe I could use this information to write something like this ?

function fakeGenerator(i){
while(!(yield i++));
};

Sure, and it works perfectly again !!!
!undefined is true and semi colon stops while loop then with an "OR" or an "AND" and a true value as condiction I could create a lot of dirty generators ... why not Fibonacci too ?

function fib(){
let(i=0,j=1){
while((yield i)||([i,j]=[j,i+j]));
}
};

so c'mon brother, you can do it better!, said my dog ... and He was right, I can remove even OR, parentheses and why not, the last semi colon too (thanks to let block).

function fib(){let(i=0,j=1){while(!(yield i))[i,j]=[j,i+j]}};

That's what I meant for the shortest Fibonacci function, isn't that ?!?



Then ... What's up ?
I wrote this stupid post just to tell You that sometimes You don't need the "while(true)" condition, just switch true the "undefined yield" :)

Thursday, November 02, 2006

bytefx 0.4

The good thing is that bytefx now has a new scroll method, the sad thing is that Safari 2 probably doesn't work with MemTronic compressed version and this new method cost about 1 more Kb, then bytefx now is 2.09 Kb and not less than 2 ... however, it is not so important because packer version is less than 3Kb and more compatible with Safari 2 too.

Fixes: any fix, bytefx seems to be really stable and probably bug free (simple with few lines of code :D).

Changes: just a new method to scroll the page with elements as target.

Visit the faq page to view an example.

Simple, cool ? Hey guys, It's bytefx ;)