Do it yourself avatars

Although the avatar creator script is itself interesting the main purpose of this article is to demonstrate how a Javascript oriented document can still be completely accessible when Javascript is disabled and that it can even have an elevated level of accessibility with Javascript enabled. This article follows "To javascript or not to javascript?" which was extremely basic. That article also advocated the use of Javascript but only for applications that retain dual functionality for those people with Javascript disabled.

The following application was created for use on a gaming site and allows the players to design there own custom avatar at the time of registration. With the live site the details are then held in the database and can be reloaded into the form and edited by the player at any time in the future.

Javascript has been used extensively throughout the project but maintaining a high level of accessibility (with HotKeys etc.) while also retaining a fully working (but very basic) model with Javascript and CSS disabled. An attempt has been made to completely seperate the Javascript from the HTML and this has been achieved without any inline Javascript including the event handlers. In fact the only reference to Javascript at all is the <script> element in the document head that calls the Javascript file.

The following is for those interested in viewing the PHP code used to assemble the various layers into one complete PNG image. The prototype version of the script also featured caching but since the size of the images is so small (between 3-5 kilobytes) this was considered unnecessary and removed. Nevertheless script checks if the client has the image cached and if so sends a 304 (not modified) hence saving bandwidth, data transfer and loading time.

<?php

# form inputs in the correct stacking order from bottom to top
$form_fields = array('background''body''hair''shirt''arms''feet''legs''weapon');

ob_start();
if(!isset(
$_GET['avatar'])) die;
validate_inputs($form_fields);
create_avatar($form_fields);
output_image(ob_get_clean());

function 
validate_inputs($inputs)
{
    foreach(
$inputs as $item)
    {
        if((
preg_match('@(\.|/)@'$_GET['avatar'][$item])) 
        or (!
is_file('avatars/'.$_GET['avatar'][$item].'.png')))
        {
            die();
        }
    }
}

function 
create_avatar($inputs)
{
    while(
$inputs)
    {
        
$layer = @imagecreatefrompng('avatars/'.
                 (
$file $_GET['avatar'][array_shift($inputs)]).
                 
'.png')
                 or die(
'I could not open the avatars/'.$file);
        
$layerWidth imageSX($layer);
        
$layerHeight imageSY($layer);
        if(!isset(
$slate))
        {
            
$slate imagecreatetruecolor($layerWidth$layerHeight);
        }
        
imagecopy($slate$layer0000$layerWidth$layerHeight);
        
imagedestroy($layer);
    }
    
imagetruecolortopalette ($slatefalse256);
    
imagepng($slate);
    
imagedestroy($slate);
}

function 
output_image($image)
{
    
$hash md5($image);
    if(isset(
$_SERVER['HTTP_IF_NONE_MATCH'])){
        if(
$hash == trim($_SERVER['HTTP_IF_NONE_MATCH'], '"')){
            
header("HTTP/1.x 304 Not Modified");
            
header('Etag: "'.$hash.'"');
            die();
        }
    }
    
header('Etag: "'.$hash.'"');
    
header('Content-Type: image/png');
    echo 
$image;
}

?>