» Front Page
» My First Ajax Node
This Howto demonstrates the use of Ajax and creation of dynamic html field with Scriptacolous and Prototype (libraries already used by ATK).
Before starting to analize the code have a look at
Visual Demo.
You can scroll and double click on the post-it image to create a new field, drag the field already created, double click on the stamp to active Ajax.
Since this is a demo, it doesn't store anything, but you can easily understand what is the objective of the demo and view the visual design, the drop capability of Scriptacolous and also the creation on the fly of new fields.
We use this CSS to hide Ajax updater image
Web.css
#loading {
display:none;
}
This is the core javascript.
It has several objectives:
- fs_add creates a new graphical field (DIV) that the user might move on the form image (used to add new Javascript DIV Field)
- fs_pos creates and arranges field (DIV) position over the form image (used to create Javascript DIV Field from DB informations)
- showResponse hides Ajax Updater
- showLoad shows Ajax Updater
Web.js
/*
* This file is part of the FSdocu distribution.
* Detailed copyright and licensing information can be found
* in the doc/COPYRIGHT and doc/LICENSE files which should be
* included in the distribution.
*
* @package docu
*
* @author Fabio Saitta <fabio@saitta.it>
*
* @copyright (c)2000-2004 http://www.saitta.it
* @license http://www.opensource.org/licenses/sleepycat.php
*
* @version $Revision: 0.99 $
* $Id: web.js,v 0.99 2006/11/07 15:48:00 fabio Exp $
*/
fsc=0;
function fs_add(element)
{
if(fsc==0) {
Droppables.add('ar_image', {onDrop:fs_drop});
el1=document.getElementById("fs_timbro");
};
fsc=fsc+1;
var newdiv=document.createElement("div");
var newtext=document.createTextNode("Campo"+fsc);
newdiv.appendChild(newtext);
newdiv.id="fs_div"+fsc;
newdiv.alt="campo"+fsc;
newdiv.style.textAlign="center";
newdiv.style.color="#ffffff";
newdiv.style.backgroundColor="#7f9f50";
newdiv.style.fontFamily="Lucida Grande, Verdana, Arial, Helvetica, sans serif";
newdiv.style.fontSize="11px";
newdiv.style.width="70px";
newdiv.style.position="absolute";
newdiv.style.top=(el1.offsetTop)+"px";
newdiv.style.left=(el1.offsetLeft+85)+"px";
el=document.body;
el.appendChild(newdiv);
new Draggable("fs_div"+fsc);
}
function fs_pos(name,x,y)
{
if(fsc==0) {
Droppables.add('ar_image', {onDrop:fs_drop});
el1=document.getElementById("fs_timbro");
};
fsc=fsc+1;
var newdiv=document.createElement("div");
var newtext=document.createTextNode(name);
newdiv.appendChild(newtext);
newdiv.id="fs_div"+fsc;
newdiv.alt=name;
newdiv.style.textAlign="center";
newdiv.style.color="#ffffff";
newdiv.style.backgroundColor="#7f9f50";
newdiv.style.fontFamily="Lucida Grande, Verdana, Arial, Helvetica, sans serif";
newdiv.style.fontSize="11px";
newdiv.style.width="70px";
newdiv.style.position="absolute";
newdiv.style.top=(x)+"px";
newdiv.style.left=(y)+"px";
el=document.body;
el.appendChild(newdiv);
new Draggable("fs_div"+fsc);
}
function showResponse (originalRequest) {
$('loading').style.display = "none";
}
function showLoad () {
$('loading').style.display = "inline";
}
function fs_drop(element,droppableElement)
{
}
Class.FSfield.inc
<?php
/*
* This file is part of the FSdocu distribution.
* Detailed copyright and licensing information can be found
* in the doc/COPYRIGHT and doc/LICENSE files which should be
* included in the distribution.
*
* @package docu
*
* @author Fabio Saitta <fabio@saitta.it>
*
* @copyright (c)2000-2004 http://www.saitta.it
* @license http://www.opensource.org/licenses/sleepycat.php
*
* @version $Revision: 0.99 $
* $Id: class.Fsfield.inc,v 0.99 2006/11/07 15:48:00 fabio Exp $
*/
useattrib("atknumberattribute");
useattrib("atkcurrencyattribute");
useattrib("atkFileAttribute");
useattrib("atkboolattribute");
userelation("atkmanytoonerelation");
class FSfield extends atkNode
{
function FSfield()
{
$this->atkNode('FSfield',NF_ADD_LINK|NF_ADDAFTERADD);
$this->add(new atkAttribute("id" ,AF_NO_QUOTES |AF_AUTOKEY));
$this->add(new atkAttribute("descri" ,AF_SEARCH),"Registro");
$this->add(new atkNumberAttribute("xcord" ,AF_SEARCH),"Registro");
$this->add(new atkNumberAttribute("ycord" ,AF_SEARCH),"Registro");
$this->add(new atkManyToOneRelation("image","image.FSimage",AF_OBLIGATORY|AF_NO_QUOTES|AF_SEARCHABLE),"Registro");
$this->setTable('fs_field');
$this->setSecurityAlias("image.FSimage");
}
function descriptor_def()
{
return "[descri]";
}
}
?>
class FSImage creates a thumbnail of form image (jpeg, gif or png) and shows it in Recordlist mode
main class load prototype.js and scriptaculous.js as well as web.js and web.css
Function editPage:
- stores key value in reg_id session variable for insert or update new field value later on.
- Creates two new little images (icons) postit and stamp and attach double click event to them.
- Creates a new little image for ajax activity and hide it.
- Calls several time function fs_pos to arrange the position of all the field already stored in DB.
- Defines fs_salva (field save) to store or update field position (you can move the field dragging them all over the form). Fs_salva is dynamic because has to store stackID value used for retrieve reg_id session variable. Call Ajax to save fields position
Action ajaxupdate:
- decodes field name, x position and y position
- retrieves master-detail key id from reg_id session variable
- verify if detail record exists
- if yes call sql update to modify it
- if no call sql insert to create it
class.FSImage.inc
<?php
/*
* This file is part of the FSdocu distribution.
* Detailed copyright and licensing information can be found
* in the doc/COPYRIGHT and doc/LICENSE files which should be
* included in the distribution.
*
* @package docu
*
* @author Fabio Saitta <fabio@saitta.it>
*
* @copyright (c)2000-2004 http://www.saitta.it
* @license http://www.opensource.org/licenses/sleepycat.php
*
* @version $Revision: 0.99 $
* $Id: class.Fsimage.inc,v 0.99 2006/11/07 15:48:00 fabio Exp $
*/
userelation("atkOneToManyRelation");
useattrib("atknumberattribute");
useattrib("atkcurrencyattribute");
useattrib("atkFileAttribute");
useattrib("atkboolattribute");
class sepiaAttribute extends atkFileAttribute
{
// By overriding this method, we can do processing on the uploaded image.
// The path and filename of the image are passed as parameters.
function processFile($path, $filename)
{
// Include the image class we downloaded. By using the 'moduleDir' method, we
// ensure that our module works regardless of where it is installed.
$dest_width=100;
$dest_height=100;
$ext = substr( $filename, strrpos( $filename, '.' ) + 1 );
if ( $ext == 'jpg' || $ext == 'jpeg' ) $src = imagecreatefromjpeg( $path.$filename ) or die( 'Cannot load input JPEG image' );
else if ( $ext == 'gif' ) $src = imagecreatefromgif( $path.$filename ) or die( 'Cannot load input GIF image' );
else if ( $ext == 'png' ) $src = imagecreatefrompng( $path.$filename ) or die( 'Cannot load input PNG image' );
else die( 'Unsupported source file format' );
list($width, $height) = getimagesize($path.$filename);
$dst = imagecreatetruecolor($dest_width, $dest_height);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
$thumb = "thumb_".$filename;
imagepng($dst, $path.$thumb);
imagedestroy();
}
function display($record, $mode="")
{
// In recodlists, show the thumbnail instead of the larger image.
if ($mode=="list")
{
// use the thumb_... version of the image
return '<img src="'.$this->m_url."thumb_".$record[$this->fieldName()]["orgfilename"].'">';
}
return parent::display($record, $mode);
}
}
class FSimage extends atkNode
{
function FSimage()
{
$this->atkNode('FSimage',NF_ADD_LINK|NF_ADDAFTERADD);
$page = &$this->getPage();
$page->register_script(atkconfig("atkroot")."atk/javascript/prototype/prototype.js");
$page->register_script(atkconfig("atkroot")."atk/javascript/scriptaculous/scriptaculous.js");
$page->register_script(moduleDir("image")."web.js");
$page->register_style(moduleDir("image")."web.css");
$this->add(new atkAttribute("id" ,AF_NO_QUOTES |AF_AUTOKEY));
$this->add(new sepiaAttribute("image", "../docu/"),"Registro");
$this->add(new atkOneToManyRelation("dettagli", "image.FSfield", "image"),"Registro");
$this->add(new atkAttribute("descri", AF_OBLIGATORY),"Registro");
$this->setTable('fs_image');
}
function editPage(&$handler, $record, $locked=false)
{
global $g_sessionManager;
$g_sessionManager->stackVar("reg_id",$record['id']);
$content= '<img src="/docu/postit.jpg" id="fs_postit" ondblclick="fs_add(this)" alt="Crea Campo">';
$content.= '<img src="/docu/timbro.jpg" id="fs_timbro" ondblclick="fs_salva(this)" alt="Salva Campi">';
$content.= '<img id="loading" src="/docu/Loading.gif" alt"Loading...">';
$content.= '<div id="zipResult"></div>';
$content.=chr(13).'<script type="text/javascript">'.chr(13);
// ora mi prendo tutti i campi del file
$g_db = &$this->getDb();
$query = "SELECT descri,xcord,ycord FROM fs_field where image=".$record["id"];
$g_db->query($query, $offset, $limit);
while ($g_db->next_record())
{
$result = $g_db->m_record;
$content.='fs_pos("'.$result["descri"].'",'.$result["xcord"].','.$result["ycord"].");".chr(13);
};
// creo la parte di update ajax
$content.= chr(13).'function fs_salva(element)';
$content.= chr(13).'{';
$content.= chr(13).'var url="dispatch.php";';
$content.= chr(13).'for (i = 1; i <= fsc; i++)';
$content.= chr(13).'{';
$content.= chr(13).'el=document.getElementById("fs_div"+i);';
$content.= chr(13).'if (el != undefined)';
$content.= chr(13).'{';
$content.= chr(13).'var pars = "atknodetype=image.FSimage&atkaction=ajaxupdate&atklevel=2&atkprevlevel=1&atkstackid='.atkStackID().'&field=" + el.alt + "&x=" + el.style.top+ "&y=" + el.style.left;';
$content.= chr(13).'var myAjax = new Ajax.Updater({success: "zipResult"}, url, {method: "get", parameters: pars, onLoading: showLoad, onComplete: showResponse} );';
$content.= chr(13).'}}}';
$content.='</script>'.chr(13);
return $page = $handler->editPage($record, $locked).$content;
}
function descriptor_def()
{
return "[descri]";
}
function action_ajaxupdate()
// function action_ajaxupdate($field="",$x=0,$y=0)
{
global $g_sessionManager,$_GET;
$field = htmlspecialchars($_GET['field']);
// attenzione px
$x = htmlspecialchars($_GET['x']);
$y = htmlspecialchars($_GET['y']);
$x = substr($x,0,strpos($x, 'px'));
$y = substr($y,0,strpos($y, 'px'));
$reg_id=$g_sessionManager->stackVar("reg_id");
// id,field,x,y creare un sql
// verifico se esiste faccio un update
// se non esiste faccio un insert
$g_db = &$this->getDb();
$query = 'SELECT * FROM fs_field WHERE descri="'.$field.'" and image = '.$reg_id;
$g_db->query($query, $offset, $limit);
// attenzione al primo record ???
if ($g_db->next_record())
// faccio update
$query = 'update fs_field set xcord='.$x.',ycord='.$y.' WHERE descri="'.$field.'" and image = '.$reg_id;
else
// faccio insert
$query = 'insert into fs_field (image,descri,xcord,ycord) values ('.$reg_id.',"'.$field.'",'.$x.','.$y.')';
$g_db->query($query, $offset, $limit);
return(true);
}
}
?>
So when you display the full image:
- All the fields (master-detail) are retrieved from DB
- Master key is stored in a session variable
- A dynamic javascript function is created and:
- Create a position draggable DIV for any of the fields over the image
- Attach a function to double-click event of Stampa image
When you double-click on post-it image :
- You can drag it all over the image
- The new field is not stored on DB
When you double-click on stamp image:
- You loop on all the field (attention you loop on javascript DIV not on DB)
- For any of fields you call a Ajax updater function that fires Action ajaxupdate with proper parameters
- ATK information like ATKstack, ATKLevel, ATKPreviouslevel are passed to Dispatch.php ATK PHP file
- Ajax work-in-progress image is displayed
- When Ajax function called is completed work-in-progress image is hided
Action ajaxupdate is called from Ajax and:
- decodes field name, x position and y position (passed by GET parameters)
- retrieves master-detail key id from reg_id session variable
- verify if detail record exists
- if yes call sql update to modify it
- if no call sql insert to create it
module.inc
<?php
/*
* This file is part of the FSdocu distribution.
* Detailed copyright and licensing information can be found
* in the doc/COPYRIGHT and doc/LICENSE files which should be
* included in the distribution.
*
* @package docu
*
* @author Fabio Saitta <fabio@saitta.it>
*
* @copyright (c)2000-2004 http://www.saitta.it
* @license http://www.opensource.org/licenses/sleepycat.php
*
* @version $Revision: 0.99 $
* $Id: class.module.inc,v 0.99 2006/11/07 15:48:00 fabio Exp $
*/
global $config_atkroot;
require_once($config_atkroot."atk/atktools.inc");
class mod_image extends atkModule
{
function getMenuItems()
{
menuitem("image");
menuitem("image", dispatch_url("image.FSimage", "admin"), "image");
menuitem("field", dispatch_url("image.FSfield", "admin"), "image");
}
function getNodes()
{
registerNode("image.FSimage",array("admin","add","edit","delete","stampa","ajaxupdate"));
}
function getDescription()
{
return "Gestione Documentazione";
}
}
?>

