Asynchronous JavaScript and XML (asynchronous JavaScript and Xml,ajax) are undoubtedly the most popular new WEB technologies. In this article we will use PHP and simple Ajax Toolkit (Sajax) to create an easy photo album as an online Web application. We first write a simple photo album using standard PHP development methods and then use Sajax to turn it into an active Web application.
Create a simple photo album
This article uses two methods to create a simple photo album: a traditional Web application and an Sajax-based application. We'll write an album in PHP, read the contents of a directory, and display a table of thumbnail images. If the user clicks a thumbnail, the image is fully expanded. Because a traditional application is written, each click will be a new HTTP request, and the parameter will be passed as part of the URL.
You'll learn how to apply the Sajax library to your albums to see why using Sajax can speed up application development.
Add a pager table
Users who visit albums need some way to quickly view photos. Because many large photos are not easy to display on one page, you need to create a pager-a simple table that displays a small number of thumbnails at a time. You also write navigation to help users move back and forth through the image list.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 1. The page-Splitter provides a way to display user photos
What is OpenAjax Alliance?
Before the start of the May 2006 JavaOne conference, representatives of 29 companies met in Adobe Systems's conference room to prepare for a general determination of the future of Ajax, a group called OpenAjax Alliance.
The group made several decisions, the most notable of which was to name themselves: OpenAjax Alliance. It also decided not to organize itself into a formal standard group, or an open-source-led organization like the Eclipse Foundation, so the group's own form was temporarily informal. The group agreed to convene a conference call about once a week.
OpenAjax Alliance focuses on three areas: reduce the risk of using AJAX by providing interoperability, ensuring that Ajax solutions adhere to open standard routes and use open source technology to keep the Web open. The team's first task is to find ways to build and maintain interoperability between Ajax tools.
OpenAjax Alliance includes 31 technology companies, including IBM, Adobe Systems, Eclipse Foundation, Google, Laszlo Systems Inc., Oracle, Red Hat Inc., and Z End Technologies LTD.
First, you collect at least 20. jpg pictures and put them in a folder. Each picture also has a thumbnail that is saved in a separate thumbnail folder. Although you can use the GD package to generate thumbnails (see Resources), this article assumes that thumbnails are ready. You can also use the photos and thumbnails provided in this article (see download).
To complete the remainder of this article, assume that the photo is saved in the/images subdirectory, and the thumbnail is placed in the/images/thumbnails. You can make the appropriate modifications in your code. In addition, we also assume that thumbnails and corresponding images use the same name.
The pager should pass two parameters: Start is the index number of the first picture that is displayed alphabetically, and step is the number of photos displayed.
Listing 1. Album Viewer
/*
* Find a list of images in/images and provide thumbnails
*/
function get_table ($limit _start = 0, $limit _step = 5) {
$images = get_image_list (' images ');
Generate navigation for Previous and Next buttons
Code given below
$output. = ' <table class= ' image_table ';
$columns = 5;
foreach ($images as $index => $image) {
Begin directory listing at item number $limit _start
if ($index $limit _start) continue;
End directory listing at item number $limit _end
if ($index >= $limit _start + $limit _step) continue;
Begin column
if ($index-$limit _start% $columns = 0) {
$output. = ' <tr> ';
}
Generate link to blown up image (below)
$thumbnail = ' ';
$output. = ' <td> '. Get_image_link ($thumbnail, $index). ' </td> ';
Close column
if ($index-$limit _start% $columns = = $columns-1) {
$output. = ' </tr> ';
}
}
$output. = ' </table> ';
Return $nav. $output;
}
The table is simple, and it traverses the list of pictures from the index number $limit _start. Then put a thumbnail of each picture, as one line per five pictures. The cycle ends when the $limit _start + $limit _step is reached.
The table is a visual representation of the directory list, so a function is needed to list all the images in the directory. The Get_file_list () function in Listing 1 returns a list of all the pictures in the/images directory with an indexed array. The following is an example implementation.
Listing 2. Get_file_list implementation
function get_image_list ($image _dir) {
$d = Dir ($image _dir);
$files = Array ();
if (! $d) return null;
while (false!== ($file = $d->read ())) {
GetImageSize returns true only on valid images
if (@getimagesize ($image _dir. '/' . $file)) {
$files [] = $file;
}
}
$d->close ();
return $files;
}
Note: You will also use the Get_file_list () function later in this article. It is important that the returned array is invariant whenever the function is called. Because the provided implementation is directory-searchable, you must ensure that the specified files in the directory do not change, and are sorted alphabetically each time.
The implementation of the navigation
Although tables list some of the images in the table of contents, users also need a way to see the pictures that don't appear in the table. To truly implement the pager, you need a set of standard links: home, Prev, Next, and last.
Listing 3. Page-Pager Navigation
Append Navigation
$output = '
showing items '. $limit _start. '-' .
Min ($limit _start + $limit _step-1, COUNT ($images)).
' of '. Count ($images). '
';
$prev _start = max (0, $limit _start-$limit _step);
if ($limit _start > 0) {
$output. = Get_table_link (' << ', 0, $limit _step);
$output. = ' | ' . Get_table_link (' Prev ',
$prev _start, $limit _step);
} else {
$output. = ' << | Prev ';
}
Append Next button
$next _start = min ($limit _start + $limit _step, COUNT ($images));
if ($limit _start + $limit _step count ($images)) {
$output. = ' | ' . Get_table_link (' Next ', $next _start, $limit _step);
$output. = ' | ' . Get_table_link (' > ", (count ($images)-$limit _step), $limit _step);
} else {
$output. = ' | Next | > > ';
}
$output. = '
;
Finally, write the Get_image_link () and Get_table_link () functions to let the user expand the thumbnail into a complete image (see Listing 4). Note that the script index.php (and the expand.php to be created later) are called only in these two functions. This makes it easy to change the functionality of the link. In fact, when you integrate with Sajax below, only these two functions need to be modified.
Listing 4. Get_image_link, Get_table_link implementation
Enlarge Picture
Now you have a paging device available to provide users with some thumbnail images. The second feature of the album is to allow the user to click the thumbnail to see the full picture. The Get_image_link () function calls the expand.php script, and we'll write it now. This script passes the index of the file that the user wants to expand, so you must list the directory here and get the appropriate file name. The subsequent operation is simple, just create the disease output image tag.
Listing 5. Get_image function
function Get_image ($index) {
$images = get_image_list (' images ');
Generate Navigation
$output. = ' ';
return $output;
}
The next step is to provide a navigation mechanism similar to that of the paging device. The previous page navigates to an image numbered $index-1, "next" navigates to an image numbered $index +1, and returns to the pager.
Listing 6. Get_image Navigation
$output. = '
viewing image '. $index. ' of '. Count ($images). '
';
if ($index > 0) {
$output. = Get_image_link (' << ', 0);
$output. = ' | ' . Get_image_link (' Prev ', $index-1);
} else {
$output. = ' << | Prev ';
}
$output. = ' | ' . Get_table_link (' Up ', $index, 5);
if ($index count ($images)) {
$output. = ' | ' . Get_image_link (' Next ', $index + 1);
$output. = ' | ' . Get_image_link (' > ", count ($images));
} else {
$output. = ' | Next | > > ';
}
$output. = '
;
Finally, create a simple HTML container and name it expand.php.
Listing 7. Get_image Navigation
! DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 strict//en"
"Http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd" >
Creating a simple Picture album viewer
Creating a simple Picture album viewer
$index = isset ($_request[' index '])? $_request[' index ']: 0;
echo Get_image ($index);
? >
So we finished the album. Users can see all the pictures, and it's easy to navigate. Naturally, users can switch back and forth, and even return to a favorite picture via a bookmark function.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 2. Completed albums
Add Sajax
The album now provides basic navigation features, and the images in the catalog are indexed. Here you will see adding Sajax to improve programming and user experience.
Let's say you have a basic understanding of Ajax, and it's best to familiarize yourself with the basics of Sajax (see Resources for tutorials).
Sajax, Ajax and traditional WEB applications
Now we have developed the application using the standard WEB development model. The two main features are the pager and the image viewer, which correspond to different PHP files respectively. parameter is passed to the script as an HTTP GET request, and the script returns the page directly to the WEB client.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 3. In a traditional WEB application, three different requests call two pages
As anyone in the Web development community knows, Ajax allows asynchronous secondary requests to be sent to the server and displays the results directly in the Web page (shown in Figure 4). Unfortunately, even the simplest Ajax application is a big task. Because Ajax is not a standardized technology, Internet Explorer and other browsers (such as Firefox, Safari) are different implementations. In addition, programmers must write at least three functions to implement a function, which is to send the initial javascript of the HTTP request, the PHP script that returns the response, and another JavaScript function that handles these responses.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 4. Ajax applications are responsible for all HTTP requests
Sajax, built on Ajax libraries, simplifies this process by using simple heuristics: every PHP function that a WEB client needs to access is "exported" by Sajax. If there is a PHP function named Foo_bar (), then Sajax will export the function as a JavaScript function X_foo_bar (). Any calls by the client to X_foo_bar () are automatically forwarded to the Foo_bar () on the server, and the output is passed to another JavaScript function. The short page in Listing 8 illustrates this feature. To run this example, download the Sajax library (see Resources).
Listing 8. The application of Sajax
Require ("sajax.php");
function Foo_bar ($param) {
Return "You typed: $param";
}
$sajax _request_type = "get"; Set HTTP request type to get
Sajax_init (); Prepare Sajax
Sajax_export ("Foo_bar"); Foo_bar can now is called by client
Sajax_handle_client_request (); discussed below
? >
If you open the page in Listing 8, enter some content in the input box, and then click Enter, the input is displayed in a warning box. But behind this seemingly simple Web page, the X_foo_bar () JavaScript function calls the Foo_bar () function remotely and passes the response to the JavaScript built-in function alert (). The last argument for each Sajax export function is a response handler that handles the output of the Foo_bar ().
This example also illustrates another important feature of Sajax rapid development: There is no need for each function to have a separate file, and the page actually calls itself, making it easier to trace the call to the function (as shown in Figure 5). The X_foo_bar () function sends the AJAX request directly back to the page, containing the function name and parameters in the request. The key is the Sajax_handle_client_request () function, which intercepts all sajax calls and automatically processes them.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 5. Use the Sajax client to access multiple functions on the server side via one page
Connect Sajax to an album
With the code we just created, we'll use Sajax to quickly convert albums from multiple-page applications into active Ajax applications.
Because albums have two functions, get_table () and Get_image () are all functions that need to be exported with Sajax. In fact, in order to invoke these functions through Sajax, the functions themselves do not need to be modified, and soon we will see that we just need to modify the generated links.
Listing 9. The head of the Sajax album
Require ("sajax.php");
function Get_image () {}//Defined later
function get_thumbs_table () {}//Defined later
Standard Sajax stuff. Use get, and export two
Main functions to JavaScript
$sajax _request_type = "get";
Sajax_init ();
Sajax_export ("Get_thumbs_table", "get_image");
Sajax_handle_client_request ();
? >
For this article, the main part of the document is simple. We will use the Div and window IDs to display the server's output.
Listing 10. Display div and window IDs for server output
Finally, you write JavaScript callback functions. In this example, because all server output is directly exported to the window div tag, simple callback functions can be reused. You can get the header (head) by adding the callback function to the Sajax function call.
Listing 11. A simple head
Creating a Sajax photo album
The final step is to ensure that all links in your application are custom Sajax calls. Just take the code in the previous section and replace it with the following: href= "index.php?start=0&step=5" Into onclick= "x_get_table (0, 5, To_window)", href= "expand.php"? Index=0 "Into onclick=" x_get_image (0, To_window) ".
and make the same modifications in the corresponding functions: Get_image_link () and Get_table_link (). This translates to Sajax (as shown in Figure 6). All links become JavaScript calls corresponding to remote PHP calls, and PHP uses JavaScript response handler To_window () to output directly to the page.
The entire application is included in one page, and the rest of the functionality (Get_table (), get_image (), etc.) can be placed in a separate library file that cannot be accessed from the Web. In most Ajax applications, each request to the server needs to be handled by a separate script, or at least a very large handler must be written to redirect the request. Bringing all of these files together can be a hassle. Using Sajax always requires only one file, in which you simply define the function that we use. Sajax replaces the handler script.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 6. Completed Sajax album (thumbnail)
You can see that the URLs remain unchanged and bring more enjoyable user experiences. The window div is displayed in a gray box, and the content generated through the Sajax is very clear. The script does not have to know itself or its location on the server, because all links eventually become JavaScript calls directly to the page itself. So our code can be well modularized. We just need to keep JavaScript and PHP functions on the same page, even if the page position has changed.
Expand album
Using Sajax to turn our albums into active Web applications is so easy, we need to spend some more time adding features to further explain how Sajax makes it completely transparent to retrieve data from the server. We'll add metadata for albums so that users can add descriptions to their pictures.
Meta data
Albums without context descriptions are incomplete, such as the source of the photo, the author, and so on. So we're going to focus the image and create a simple XML file. The root node is gallery, which contains any number of photo nodes. Each photo node is numbered by its file property. You can use any number of tags to describe a photo in the photo node, but in this case only date, locale, and comment are used.
Listing 12. XML file that contains meta data
>
August 6, 2006
Los Angeles, CA
Here's a photo of my favorite celebrity
August 7, 2006
San Francisco, CA
in SF, we got to ride street cars
August 8, 2006
Portland, or
Road trip!
File parsing is not covered in this article. Let's say you're proficient with one of the many XML parsing methods in PHP. If you are unfamiliar, it is recommended that you read the articles in resources. Instead of wasting time explaining how to convert the file to HTML, as an exercise, readers can see for themselves how the following code uses XML files and generates HTML. The code in Listing 13 uses the SimpleXML package from the PHP V5.
Listing 13. Meta-data functions
function Get_meta_data ($file) {
Using getimagesize, the server calculates the dimensions
List ($width, $height) = @getimagesize ("images/$file");
$output = "
Width: {$width}px, Height: {$height}px
";
Use SimpleXML package in PHP_V5:
http://us3.php.net/manual/en/ref.simplexml.php
$xml = simplexml_load_file ("Gallery.xml");
foreach ($xml as $photo) {
if ($photo [' id '] = = $file) {
$output. =!empty ($photo->date)? '
Date taken:{$photo->date}
': ';
$output. =!empty ($photo->locale)? "
location:{$photo->locale}>/p>": ";
$output. =!empty ($photo->comment)? "
comment:{$photo->comment}
": ";
}
}
return $output;
Note that the Get_meta_data () function also uses getimagesize () (a core PHP function that does not require GD) to compute the size of the image.
And then back to the Get_image () function, which contains a list of file names generated by Get_image_list (). The only time to find the metadata is to pass the file name to the function.
Listing 14. Add Meta Data
function Get_image ($index) {
$images = get_image_list (' images ');
// ...
$output. = ' ';
$output. = '
Get_meta_data ($images [$index]). '
';
return $output;
}
Reopening the page will see the results of the server request. Figure 7 shows an enlarged image with meta data.
0 && image.height>0) {if (image.width>=510) {This.width=510;this.height=image.height*510/image.width}}} " >
Figure 7. Photo albums with Meta data
Concluding remarks
We see that using Sajax eliminates the barriers between client and server, and programmers can make seamless remote function calls without worrying about transport layers, HTTP get, and POST requests. We can spend more time writing PHP scripts that provide data and JavaScript in the presentation and control layers. In this photo album example, we have the client connect directly to the image database. By adding simple metadata, we see how simple it is to give users direct access to information on the server without worrying about protocol issues.
Like all Ajax applications, our album has a fatal weakness: It doesn't use the browser's "Access history" because it destroys the back button's functionality. In part 2nd of the "developing Ajax applications with PHP" series, we will address this problem by implementing a history buffer and state tracking mechanism.