26.06.2014 Views

Learning WML - WAP Basics

Learning WML - WAP Basics

Learning WML - WAP Basics

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Learning</strong> <strong>WML</strong> - <strong>WAP</strong> <strong>Basics</strong><br />

By Steve Schafer<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This first article<br />

covers the background of <strong>WAP</strong>, how it works, and what you need to<br />

get started. Future articles will introduce the <strong>WML</strong> language, tips and<br />

tricks to provide content, and how to integrate other technologies such<br />

as PHP to make your pages more flexible.<br />

What Is <strong>WAP</strong>?<br />

<strong>WAP</strong> stands for Wireless Access Protocol, a general term used to<br />

describe the multi-layered protocol and related technologies that bring<br />

Internet content to mobile devices such as PDAs and cell phones.<br />

Such devices are referred to as thin clients because they have one or<br />

more constraints in the form of display, input, memory, CPU, or other<br />

hardware or usability limitations. The platform constraints and the<br />

slower (and more expensive) bandwidth of cellular and related<br />

networks make standard Internet protocols difficult to utilize. Using<br />

the growing set of <strong>WAP</strong> tools and protocols, however, the mobile<br />

Internet is quite a capable tool.<br />

A Brief History of <strong>WAP</strong><br />

As previously stated, <strong>WAP</strong> refers to a wide range of technologies and<br />

protocols, all related to mobile Internet functionality. This functionality<br />

has roots dating back to the mid 1990s. At that time, several vendors<br />

were working on the mobile Internet problem as mobile device sales<br />

skyrocketed, and several competing technologies emerged:<br />

• Nokia's Narrow Band Sockets (NBS) and Tagged Text Markup<br />

Language (TTML)<br />

• Ericsson's Intelligent Terminal Transfer Protocol (ITTP)<br />

• Unwired Planet's Handheld Device Markup Language (HDML)<br />

Each technology had its own purpose, but some overlapped with<br />

others in various areas. This diversity threatened to fragment the<br />

wireless industry along provider lines. In mid 1997, the <strong>WAP</strong> Forum<br />

was founded to aid in communication among the developers and to


spur a common set of protocols and technologies. In the same year,<br />

the industry took another step forward with the formation of the Open<br />

Mobile Alliance (OMA), which combined several distinct development<br />

and standards bodies into one.<br />

How Does <strong>WAP</strong> Work?<br />

These articles will focus on the delivery of <strong>WML</strong> content to mobile<br />

devices over a cellular or related technology network. However, the<br />

delivery of many protocols and technologies takes the same routenamely,<br />

through a proxy server that bridges the gap between the<br />

wired Internet and the wireless service provider's network.<br />

Figure 1.1 The <strong>WAP</strong> Gateway provides wireless networks with<br />

Internet access and optional content translation and filtering.<br />

This proxy server manages the communication between the wireless<br />

client and the Internet server(s), acting as a gateway to the wired<br />

Internet. It caches content and in some cases even translates raw<br />

HTML into <strong>WAP</strong>-compatible protocols such as <strong>WML</strong>.<br />

Many mobile devices have a built-in wireless browser. Although several<br />

different browsers are in use today among the various wireless<br />

providers, most browsers support <strong>WML</strong>, either natively or translated<br />

into HDML. A popular precursor to <strong>WML</strong>, the Handheld Device Markup<br />

Language (HDML), is still supported on several mobile platforms.<br />

However, due to the limitations of HDML (supporting only a handful of<br />

navigation tags and virtually no formatting tags), <strong>WML</strong> is becoming the<br />

most widely used mobile markup language. That said, if you plan to


support a particular platform, it's best to test your code extensively on<br />

that particular device.<br />

Note: When coding for the general public, be careful to stick to the<br />

standards and avoid using proprietary extensions to the various<br />

languages, no matter how tempting the feature set of the extensions.<br />

If you decide to provide the extensions to those who can use them,<br />

you should take the necessary server steps to identify the connecting<br />

browser and deliver code customized for that browser.<br />

What Is <strong>WML</strong>?<br />

<strong>WML</strong> (Wireless Markup Language) is the dominant language in use<br />

with wireless devices today. Essentially, <strong>WML</strong> is a subset of HTML, but<br />

has its roots in XML. Those developers with a solid base in XML should<br />

have a relatively easy time coding <strong>WML</strong>.<br />

The current <strong>WML</strong> standard is 1.3, although many mobile devices in use<br />

today support only the <strong>WML</strong> 1.1 standard. Therefore it's prudent to<br />

stay away from 1.3-specific features, unless you know that your target<br />

market's devices are 1.3-ready.<br />

There are several key differences between <strong>WML</strong> and standard HTML,<br />

including the following:<br />

• <strong>WML</strong> is highly structured and very particular about syntax. Several<br />

current HTML browsers allow for "messy" code such as missing tags<br />

and other formatting snafus. Such mistakes are not allowed in <strong>WML</strong>;<br />

the mobile browser will complain and generally won't display the<br />

page.<br />

• <strong>WML</strong> is case sensitive. The tags and are treated as<br />

different tags, although they accomplish the same purpose (bold<br />

text). Therefore, you must be careful to match the case of your<br />

opening tags with your closing tags (for example, This is<br />

bold will not work as expected).<br />

• Many tags have required attributes. Developers accustomed to HTML<br />

may be used to including only attributes they need-in some <strong>WML</strong><br />

tags, you must include a few attributes, even if they are blank or<br />

default.


• <strong>WML</strong> pages are structured in "decks" (see the next section), allowing<br />

for multiple pages to be defined in each <strong>WML</strong> file.<br />

<strong>WML</strong> also has a client-side scripting language, <strong>WML</strong>Script, to help<br />

automate particular tasks, validate input, and so on. <strong>WML</strong>Script is a<br />

subset of JavaScript and will be covered in a later article.<br />

Understanding Decks<br />

<strong>WML</strong> pages are structured within "decks," allowing several pages<br />

("cards") to be defined in each <strong>WML</strong> file. This deck analogy allows<br />

multiple pages to be delivered to the mobile client at the same time,<br />

minimizing the loading time between related pages. However, the<br />

limited memory on most devices constrains the deck size, usually to<br />

less than 1024 bytes. Therefore, careful consideration and planning<br />

should go into any <strong>WAP</strong> application; don't start coding without<br />

investing time in planning.<br />

Note: Remember your audience. Mobile users generally scroll through<br />

cards rapidly and will be reading on a display that's a mere handful of<br />

characters wide (usually less than 20 characters) and usually less than<br />

10 lines high. Keep your content to a minimum, provide an intuitive<br />

navigation structure, and optimize your decks to maximize links within<br />

the deck and minimize links outside of the deck.<br />

Visualizing a physical "deck of cards" structure can help in<br />

understanding the principles of <strong>WML</strong>. For example, suppose we have<br />

three simple cards (pages) as shown below:


Figure 1.2 - The physical card analogy to <strong>WML</strong> decks helps<br />

visualize how they work.<br />

These cards together form a deck and are delivered to the mobile<br />

device in one file. Now suppose that each card links to the next (card<br />

one links to card two, which links to card three, and so on), and that<br />

each card also has a "back" link to take the user back to the previous<br />

card. As the user navigates the deck, the cards stack in memory as<br />

shown below:<br />

Figure 1.3 - As the user follows the links through the deck, the<br />

cards stack up in memory.<br />

A developer accustomed to HTML might be tempted to implement the<br />

"back" feature by providing a link to the deck, specifying the previous<br />

card. However, this would cause the mobile device to re-request the


whole deck before redisplaying the card-a card it already had in<br />

memory.<br />

Instead, you should use the tag, which tells the browser to remove<br />

the current page and display the previous page in the history list (like<br />

using the Back button on a PC browser). Of course, the content of the<br />

previous page might need to be refreshed each time it's accessed; in<br />

that case, valid techniques could include recalling the whole deck or<br />

specifying that the page not be cached. Proper navigation will be<br />

covered in future articles.<br />

Figure 1.4 - The tag "pops" the top card off the stack<br />

(out of the history list), redisplaying the previous card in the<br />

history.<br />

Setting Up Your Server for <strong>WML</strong><br />

To configure your Web server to deliver <strong>WML</strong>, you must define the<br />

related MIME types for <strong>WML</strong> content. Web servers and client browsers<br />

use MIME (Multipurpose Internet Mail Extensions) to communicate the<br />

type of data that is being sent. Before sending data, the server sends<br />

a MIME identifier to the client browser, identifying the format of the<br />

following data. The client browser can then properly decode and apply<br />

the data. Most <strong>WML</strong> applications require three MIME types, as listed in<br />

the following table.<br />

File Extension MIME Type Definition Use<br />

.wml text/vnd.wap.wml <strong>WML</strong> source file<br />

.wmls<br />

text/vnd.wap.wmlscript <strong>WML</strong> script file<br />

.wbmp image/vnd.wap.wbmp Wireless bitmap file (image)


To add MIME types to your Web server, you must have administrator<br />

access to the server. The following sections cover setting up<br />

Microsoft's Internet Information Server (IIS) and Apache for <strong>WML</strong>. If<br />

you're using another type of server, read your server's documentation<br />

for more information on adding MIME types.<br />

Adding MIME Types to Internet Information Server (IIS)<br />

To add the MIME types to IIS, open the Internet Information Services<br />

Management Console (MC). Access to this console varies depending on<br />

which specific operating system you're using and how you installed<br />

IIS, but can usually be found under Administrative Tools (Windows<br />

2000) or Option Pack (Windows NT).<br />

Open the IIS MC, click on the server to expand its tree, and then rightclick<br />

on Default Web Site and choose Properties. (Note: If you don't<br />

want all the sites on your server to be able to deliver <strong>WML</strong>, right-click<br />

on those sites you want to be <strong>WML</strong>-enabled and then continue<br />

following these steps.)<br />

Click on the HTTP Headers tab and then click on the File Types button<br />

under the MIME Map section. In the File Types dialog, click on New<br />

Type and enter the extension and MIME definition ("Content Type")<br />

from the preceding table. Click on OK. Repeat this process for the<br />

other two MIME types.<br />

When you're finished, close the Web Site Properties dialog by clicking<br />

on OK. On some servers, there may be nodes or devices that also<br />

define HTTP codes and need to inherit the new setting(s). Choose the<br />

appropriate options for your system. Exit the IIS MC. Usually you<br />

won't need to restart the IIS service, but it wouldn't hurt to do so just<br />

in case.<br />

Tip: Before exiting the Web Site Properties, you may want to add an<br />

entry for <strong>WML</strong> on the Documents tab (such as index.wml). This causes<br />

the server to display that document by default, eliminating the need<br />

for your visitors to specify a particular file in the URL to access your<br />

site.<br />

Adding MIME Types to Apache


To add MIME types to Apache, you must edit the httpd.conf file. This<br />

file's location varies from system to system.<br />

This file uses "AddType" lines to define MIME types. Find the section<br />

where these appear and add the following lines:<br />

AddType text/vnd.wap.wml .wml<br />

AddType text/vnd.wap.wmlscript .wmls<br />

AddType image/vnd.wap.wbmp .wbmp<br />

Save and close the file and restart the Apache server to reload the<br />

configuration with the new MIME types.<br />

Tip: You may want to add "index.wml" or comparable entry to the<br />

DirectoryIndex section of the Apache configuration file (requires<br />

running mod_dir). This causes the server to display that document by<br />

default, eliminating the need for your visitors to specify a particular file<br />

in the URL to access your site.<br />

A Sample <strong>WML</strong> Deck<br />

Now that your server is set up to handle <strong>WML</strong> correctly, let's try<br />

serving up a sample page. The following listing shows the bare<br />

minimum coding necessary to contain a <strong>WML</strong> deck, consisting of a<br />

single, blank card:<br />

<br />

<br />

<br />

<br />

<br />

<br />

The first line of the preceding code specifies that the file is XML and<br />

version 1.0-compatible. The second line defines the XML scope of the<br />

file; namely, that its DOCTYPE is <strong>WML</strong>, and where the Document Type<br />

Definition (DTD) can be found.<br />

Tip: If you're unfamiliar with XML and don't fully understand these<br />

lines, just make sure that they appear at the beginning of all your


<strong>WML</strong> documents.<br />

The next line begins the <strong>WML</strong> definition with the tag.<br />

The tag defines a card in the deck. Note that the id and title<br />

attributes can be anything you choose, but should be short and to the<br />

point, and the id must contain only letters and numbers (no<br />

punctuation or spaces).<br />

If you want to try the preceding example, create the file on your Web<br />

server (in plain text form), adding the following lines between the<br />

tags to provide content for the card:<br />

<br />

A Sample Card<br />

<br />

Place the file in an accessible directory with adequate permissions to<br />

access it from an external browser. Now visit the Wapalizer at<br />

http://www.gelon.net. Type the URL to the file in the Wapalizer box<br />

and click on the Wapalize button. You should see a screen similar to<br />

the output below.<br />

Figure 1.5 - Your sample page in the Openwave simulator.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave


SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

<strong>Learning</strong> <strong>WML</strong> - Tools and Structure<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

some essential tools for coding and debugging <strong>WML</strong> code, as well as<br />

the basics of creating <strong>WML</strong> decks. Future articles will cover advanced<br />

<strong>WML</strong> language, tips and tricks to provide content, and how to integrate<br />

other technologies such as PHP to make your pages more flexible.<br />

Note: These articles cover <strong>WML</strong> version 1.1, which is supported by the<br />

majority of mobile devices in use today. The articles assume a working<br />

knowledge of HTML and general Web technologies, and further assume<br />

that you have read the previous article(s) in this series.<br />

Essential Documentation and Tools<br />

There are several tools you should assemble before beginning to code<br />

your <strong>WML</strong> applications.<br />

Note: The recommendations in this article are simply that:<br />

recommendations. You can make do with a simple text editor (such as<br />

Windows Notepad or vi in Linux) and the Web server, but spending<br />

some time assembling some good tools will pay off in the long run.<br />

Tip: If you decide not to use one of the <strong>WAP</strong> SDKs/IDEs and use<br />

Windows as your OS, try TextPad, a nifty text editor that's very<br />

versatile and code friendly (http://www.textpad.com).<br />

Documentation<br />

There is an abundance of <strong>WAP</strong>/<strong>WML</strong> documentation on the Web,<br />

mostly courtesy of the Open Mobile Alliance (OMA) and other <strong>WAP</strong><br />

organizations. You should spend some time reading the user interface<br />

(UI) suggestions, developer guidelines, and language reference text at<br />

the sites listed in the following sections.


Open Mobile Alliance - http://www.openmobilealliance.org<br />

The Open Mobile Alliance (OMA) is a relatively new standards<br />

organization made up of several key players in the <strong>WAP</strong> arena,<br />

including most cellular providers and phone manufacturers - visit<br />

http://www.openmobilealliance.org/members.html for a full list. The<br />

new site is still evolving, but much of the content from the previous<br />

site (the popular "<strong>WAP</strong> Forum") is still available. As of this writing, you<br />

can access the documentation by using the "Access to <strong>WAP</strong> specific<br />

information" link at the bottom of the main OMA page. A full list of<br />

<strong>WML</strong> release specifications, <strong>WAP</strong> specifications, and DTD definitions is<br />

available.<br />

Openwave Developer Program -<br />

http://developer.openwave.com<br />

Openwave, the company behind the popular Openwave Mobile Browser<br />

(formerly the UP.Browser) maintains a library of technical documents<br />

and developer guidelines. Follow the links on the left side of the main<br />

page to access <strong>WML</strong> language references (in the Technical Library), UI<br />

guidelines, lists of supported devices, and more.<br />

Forum Nokia - http://www.forum.nokia.com<br />

Nokia was one of the pioneers in the <strong>WAP</strong> arena and maintains an<br />

impressive number of emulators and related documentation. Their<br />

Documents section has a handy search feature to help find the<br />

document most applicable to your needs.<br />

Emulators and SDKs<br />

You could test your <strong>WML</strong> code with a cell phone, but most providers<br />

charge a premium for Web access, and testing could get rather<br />

expensive. Instead, use one of the following emulators, some of which<br />

are packaged with a full SDK_some even include a full Integrated<br />

Development Environment (IDE).<br />

Note: Most of the emulators and SDKs are only available for Windows,<br />

although a few are available for Solaris and other operating systems.


Tip: No emulator can fully replace actual device testing. Although you<br />

can initially code and debug on one of the emulators, don_t neglect<br />

testing on your actual target device(s) as well.<br />

The Gelon Wapalizer - http://www.gelon.net<br />

The Wapalizer is a Web-based <strong>WAP</strong> emulator and offers several skins<br />

to emulate popular phones. Simply type your URL into the Wapalizer<br />

box and click the Wapalize button to open your code in the default<br />

emulator. Alternatively, choose a different skin and use the emulated<br />

device's interface to navigate to your URL.<br />

Note: The Wapalizer is not as robust as some of the other emulators.<br />

Although it's a handy way to quickly check a page, you should also use<br />

an emulator that closely resembles your target microbrowser.<br />

The Openwave SDK - http://developer.openwave.com<br />

The Openwave SDK 5.1 provides the best suite of tools I've run<br />

across; I use it as my primary coding and testing environment. The<br />

Openwave developer site (http://developer.openwave.com) has<br />

several SDKs available, depending on what <strong>WML</strong> language revision and<br />

feature set you need. SDK 5.1 can be used for most applications,<br />

providing that you stay within the coding boundaries for your target<br />

device's supported version of <strong>WML</strong> and you fully test your code on the<br />

target device(s).<br />

Nokia Emulators - http://www.forum.nokia.com<br />

Nokia maintains an impressive list of emulators, including several that<br />

tie in to their Mobile Internet Toolkit. Note that you must register on<br />

the site to download any of the tools. Many are Java-based, and<br />

therefore require you to install a Java Virtual Machine (JVM) on your<br />

system to run the tool.<br />

Other Emulators<br />

If your device isn't compatible with the emulators listed above, visit<br />

the manufacturer's site for the device to see whether they offer a<br />

compatible tool.


Tip: If you need to find out what browser your target device uses,<br />

access a nonexistent file on your Web server and examine your server<br />

logs for the browser identification information. (A nonexistent file<br />

makes it easy to find the pertinent log entry.) For example, if I use my<br />

Samsung A400 phone to access "missingfile.jpg" on my Web server, I<br />

find the following log entry:<br />

[25/Jun/2002:16:27:50 -0500] "GET /wireless/missingfile.jpg<br />

HTTP/1.1" 404 306 "-" "SEC-spha400 UP.Browser/4.1.22b1<br />

UP.Link/5.0.2.3c"<br />

This entry shows the client ("spha400") and the browser and version<br />

(UP.Browser 4.1.22b, now the Openwave Mobile Browser, but still<br />

identified as UP.Browser).<br />

Basic <strong>WML</strong> Structure and Rules<br />

Now that we have our server set up and our toolkit stocked, we're<br />

almost ready to do some coding. First let's examine the basic form of<br />

<strong>WML</strong> documents and some basic coding rules.<br />

Basic <strong>WML</strong> Deck Structure<br />

The following listing shows the basic elements you need in most <strong>WML</strong><br />

decks:<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

...<br />

<br />

<br />

...<br />

<br />

...<br />


You may remember the document preamble code (first two lines) from<br />

the previous article. These lines are necessary to identify the type of<br />

content (<strong>WML</strong>) to the browser. They need to be present in all files.<br />

The deck is then started with a tag, and ended with a <br />

tag.<br />

Note that the section is optional, but is necessary to supply<br />

any meta information (via tags) required for your deck.<br />

Typically, cache instructions are passed via meta tags included in the<br />

section. Leaving the section blank by including only<br />

the tags is a bad idea; it can cause some browsers to fail.<br />

Include this section only if you need to include or <br />

tags.<br />

What follows is the actual deck, made up of individual cards, each with<br />

a mandatory ID and title parameter. (Other parameters for the<br />

and other tags will be covered in later articles.) Some<br />

browsers display the card title at the top of the display; others don't.<br />

It's important to make your card titles display-ready (short but<br />

meaningful, and so on).<br />

Tip: It may be helpful to have this information stored in a file as a<br />

template for all your <strong>WML</strong> coding. Note that some SDKs provide a<br />

skeletal <strong>WML</strong> structure when you create a new file.<br />

<strong>WML</strong> Language Rules<br />

Let's quickly review some basic <strong>WML</strong> language rules:<br />

• Most tags have opening and closing components. Those that don_t<br />

(, , , , and so on) include a slash at<br />

the end of the tag, signifying that the tag is singular (opening and<br />

closing).<br />

• The language is case sensitive, so all closing tags must match the<br />

capitalization of the opening tags. (This is bold will not<br />

have the desired results.)<br />

• All tag parameter values must be enclosed in quotes (for example,<br />

).<br />

• All text must be enclosed in a tag, even if that tag is just a simple<br />

paragraph tag ().<br />

• There are some elements that must appear in a certain order. For


example, within a element, the following must appear in order:<br />

, , .<br />

<strong>WML</strong> Text Formatting<br />

Like HTML, <strong>WML</strong> supports several types of text formatting. The<br />

following table describes the available text-formatting tags.<br />

Tag Name Use<br />

Paragraph Mark blocks of text as paragraphs.<br />

Line break Break the current line, much like pressing Enter in a word processing<br />

program.<br />

Bold Boldface the delineated text.<br />

Big Make the text appear in a large point size.<br />

Emphasized Emphasize the text (usually with italics).<br />

Italic Display the text in an italic font.<br />

Small Display the text in a small point size.<br />

Strong Display the text in a strongly emphasized font. (Usually a large point<br />

size and italic.)<br />

Underline Display the text in an underlined font.<br />

Note: Many of these text attributes (such as , and so on) are not<br />

supported by all mobile browsers.<br />

The most important text formatting tag is the paragraph tag ().<br />

This tag must appear around all text not delineated by other tags. It<br />

also controls how the browser presents the text if you add the optional<br />

mode attribute. The two supported modes are "wrap" and "nowrap",<br />

with "wrap" as the default. The wrap mode allows the text to flow<br />

down the device screen, breaking lines at appropriate spaces and<br />

punctuation. The nowrap mode uses a marquee-like display to display<br />

the text, scrolling the text horizontally when this mode is selected.<br />

For example, take the following code:<br />

<br />

<br />

<br />

<br />

<br />

No Wrap: The quick brown fox jumped over the lazy dog.<br />

<br />

<br />

Wrap: The quick brown fox jumped over the lazy dog.<br />


<br />

It results in the following display:<br />

Figure 2-1 - The mode attribute determines how the<br />

tag wraps text.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)


Figure 2-2 - Text set to nowrap will scroll horizontally when the<br />

browser selects that line. Note how the first line (nowrap) has<br />

scrolled in this figure. The text will continue to scroll until it<br />

reaches the end of the paragraph, where it will start over.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

If you don't use the mode attribute, the client browser uses the<br />

currently selected mode, or the default "wrap" if no mode had been<br />

defined previously. Typically the nowrap mode is used for headlines<br />

and selection text, while wrap is used for general and body text.<br />

ard Navigation<br />

Now let's consider how to navigate between cards within a deck. As an<br />

example, we'll use a riddle, having the user navigate between the<br />

question and answer.<br />

Take the following riddle:<br />

Q: You cannot see it, you cannot touch it, it isn't a liquid, it isn't a


solid, it isn't a gas, but it can be broken. What is it?<br />

A: Silence.<br />

We'll put the question in one card, the answer in another, and define<br />

links to move between the two. For the sake of variety, let's define the<br />

link forward (to the answer) as a button and the link backward (back<br />

to the question) as an in-text link. Consider the following code:<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

You cannot see it, you cannot touch<br />

it, it isn't a liquid, it isn't a<br />

solid, it isn't a gas, but it can<br />

be broken. <br />

What is it?<br />

<br />

<br />

<br />

<br />

Silence.<br />

<br />

<br />

<br />

Back to Question<br />

<br />

<br />

<br />

<br />

Card 1<br />

The first card is simply text, enclosed in paragraph tags. However,<br />

we've added a element to change the behavior of the Accept key<br />

- one of the default navigation keys on most mobile devices (see the


next section on keys for more information). This element has<br />

type and label attributes. The type attribute tells the browser how the<br />

is called. In this case it's by remapping the Accept key's<br />

function. The label supplies a label for the key, namely "Answer" in<br />

this example.<br />

Note: This label bends one of the UI suggestions of using a maximum<br />

of five letters for key labels.<br />

Within the element is a instruction, supplying the address<br />

(href) to "go" to when the key is pressed. The href is in the following<br />

form:<br />

#<br />

Since the destination is in the same deck, the deck name can be<br />

omitted and only the CardID is necessary.<br />

Note: The order of the and elements is arbitrary. I prefer<br />

to put my navigation directives before my text wherever possible. It<br />

would be acceptable to reverse their order. Whichever you choose it<br />

pays to be consistent.<br />

Card 2<br />

The second card also uses a paragraph element, but incorporates a<br />

link within the element by using the tag. The form of the<br />

tag is as follows:<br />

<br />

type<br />

text<br />

<br />

The label attribute supplies an optional label to the Accept button<br />

when the link is selected. We've omitted this attribute in the example,<br />

allowing the default "Link" (or other device default) to be displayed.<br />

The type of anchor can be , , or - you simply<br />

add the appropriate element to define the type. We've used to<br />

return to the previous card.<br />

The text is the actual text shown for the link, in this case "Back to


Question".<br />

Note that the is enclosed in the paragraph and that the<br />

paragraph is set to nowrap. This will prevent the link from wrapping to<br />

several lines. We aren't worried about the plain text ("Silence")<br />

wrapping in this example, but we could easily enclose the in<br />

its own nowrap tag and change the text paragraph to wrap.<br />

Result<br />

The above code has the following result:<br />

Figure 2-3 - The left display is Card1, the right is Card2. Notice<br />

that the emulator displays a check mark instead of the<br />

standard "Link" text for the Accept button.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

Expanding the Example<br />

You might have noticed that our second navigation method - mapping


the command to the Accept key - is superfluous. Most devices<br />

incorporate a standard "Back" button that could be used to return to<br />

the question without the overhead of remapping a key. With that in<br />

mind, let's put the key to a better purpose: moving to another<br />

question.<br />

Change the to the following:<br />

<br />

<br />

Next Question<br />

<br />

Then, duplicate cards 1 and 2 and re-label them as 3 and 4. You also<br />

need to change the link in card 3 (to card 4) and supply a new<br />

question and answer. The result would be similar to the following:<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

You cannot see it, you cannot touch<br />

it, it isn't a liquid, it isn't a<br />

solid, it isn't a gas, but it can<br />

be broken. <br />

What is it?<br />

<br />

<br />

<br />

<br />

Silence.<br />

<br />

<br />

<br />

Next Question<br />

<br />

<br />

<br />


<br />

<br />

<br />

What is greater than God, more evil<br />

than the devil, poor people have it,<br />

rich people want it, and if you eat<br />

it you will die?<br />

<br />

<br />

<br />

<br />

Nothing. Nothing's greater than God,<br />

nothing's more evil than the devil, poor<br />

people have nothing, rich people<br />

want nothing more than they already<br />

have, and if you eat nothing you will die.<br />

<br />

<br />

<br />

<br />

<br />

Next Question<br />

<br />

<br />

<br />

<br />

Note: Because the answer to the second question is lengthy, I moved<br />

the to its own nowrap paragraph, as discussed earlier. Also,<br />

I set up the last link to a nonexistent "card5," assuming that we would<br />

add more questions and answers.<br />

Understanding Device Keys<br />

Most mobile devices have a limited number of keys to aid in<br />

navigation. Many have a default Back key (my Samsung cell phone<br />

uses the CLR key), as well as Accept and Options keys (my Samsung<br />

uses OK and MENU, respectively).<br />

Note: See the <strong>WAP</strong> documentation for a particular device to determine<br />

which keys perform which default purposes.


The Accept and Option keys are referred to as soft keys because you<br />

can redefine their meaning and default labels with the software, in this<br />

case <strong>WML</strong> code. Many of the navigation elements allow you to specify<br />

which keys you want the soft keys to interact with. For example, in our<br />

riddle example we specified "accept" to modify the Accept key. We<br />

could have used "options" to redefine the other soft key (on the lower<br />

right side of the display).<br />

Tip: When defining multiple elements on the same card, the<br />

affected soft key will be labeled "MENU" and allow access to a menu of<br />

defined elements.<br />

Unfortunately, there are few firm standards for default keys. Some<br />

device manufacturers map different keys to the <strong>WAP</strong> interface, while<br />

some have more proprietary solutions such as a jog wheel to move<br />

between options and functions. The best thing to do before planning<br />

an interface is to read the UI and application style guides and test your<br />

target devices.<br />

<strong>Learning</strong> <strong>WML</strong> - Navigation, User Input, and<br />

Graphics<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

more advanced navigation, accepting user input, and displaying<br />

graphics. Future articles will cover advanced <strong>WML</strong> language, tips and<br />

tricks to provide content, and how to integrate other technologies such<br />

as PHP to make your pages more flexible.<br />

Note: These articles cover <strong>WML</strong> version 1.1, which is supported by the<br />

majority of mobile devices in use today. The articles assume a working<br />

knowledge of HTML and general Web technologies, and further assume<br />

that you have read the previous article(s) in this series.<br />

More Navigation Between Cards and Decks<br />

In the previous article ("<strong>Learning</strong> <strong>WML</strong> - <strong>WML</strong> Tools and Coding<br />

<strong>Basics</strong>"), we covered basic navigation using and


elements. These basic navigation constructs enable you to map links<br />

to the soft keys or place standard HTML-style hyperlinks into your<br />

cards. There are a few more useful navigation methods we should also<br />

cover.<br />

Select Lists<br />

The select list provides an easy way for the user to select between<br />

several predetermined values. Each value is enclosed within an<br />

tag, much like an HTML list:<br />

<br />

First option<br />

Second option<br />

<br />

Each option is automatically assigned a shortcut number, displayed<br />

next to it by the browser, as shown in the following figure.<br />

Each element is assigned a shortcut number which is<br />

displayed next to the element's text.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave


logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

The user can either press the shortcut number associated with the<br />

option or use the standard navigation (arrow) keys to select the<br />

appropriate option and then press the Accept key.<br />

Tip: The shortcut keys are only useful if you have 10 or fewer options,<br />

since there are only 10 numeric keys (0-9) that can be used to quickly<br />

select an option. If you have more than 10 options, consider adding a<br />

"More" option that displays the options in groups of 10 or fewer.<br />

The "onpick" parameter can be used to select a deck/card to navigate<br />

to if the user selects that option. For example, the following code will<br />

navigate the user to the appropriate "color" card within the current<br />

deck:<br />

<br />

Red<br />

Green<br />

Blue<br />

<br />

A full deck example follows:<br />

<br />

<br />

<br />

<br />

<br />

<br />

Red<br />

Green<br />

Blue<br />

<br />

<br />

<br />

<br />

Your choice was Red.<br />


<br />

<br />

Your choice was Green.<br />

<br />

<br />

<br />

<br />

Your choice was Blue.<br />

<br />

<br />

<br />

Tip: Notice the addition of the "title" parameter in each tag.<br />

The text provided with this parameter is displayed as the Accept soft<br />

key's label when the option is selected.<br />

We used the options to navigate to cards within the current deck.<br />

However, you could just as easily navigate to other decks or specific<br />

cards within those decks.<br />

<strong>Learning</strong> <strong>WML</strong> - Navigation, User Input, and<br />

Graphics<br />

Events<br />

You can also set up navigation that is caused by a particular event.<br />

The element provides several possible actions triggered by<br />

one or more intrinsic events.<br />

The tag has the following format:<br />

<br />

task<br />

<br />

The "type" can be one of the following:


Type<br />

Onpick<br />

Onenterforward<br />

Onenterbackward<br />

Ontimer<br />

Event<br />

User selects or deselects an item.<br />

User navigates to a card in a forward manner<br />

(e.g., from a or other link)<br />

User navigates to a card in a backward manner<br />

(e.g., from a element or Back command)<br />

A specified element expires<br />

The "onenter" types can be used to display or omit particular<br />

information depending on how the user navigated to the card, or to<br />

force a refresh of dynamic information if the user reached the card by<br />

going backward (thereby getting the card from the cache and not a<br />

fresh copy from the server).<br />

The "onpick" type is used when the is nested within an<br />

element to provide an action when the option is selected.<br />

The "ontimer" type is used to provide a task at a particular time,<br />

independent of user actions (refreshing a page, moving to another<br />

page, etc).<br />

The various tasks that can be used with the element are<br />

described in the following table.<br />

Task Use<br />

Navigates to a new card.<br />

Navigates to the previous card.<br />

Performs no task (no operation).<br />

Refreshes the current card's content.<br />

Exits the current context. *<br />

Spawns a child task. *<br />

"Throws" an exception that can be caught by an<br />

optional element. *<br />

* These tasks are specific to the Openwave browser. See the<br />

developer documentation at http://developer.openwave.com for more<br />

information.<br />

The "ontimer" event can be used to display a splash screen (containing<br />

welcoming or copyright information), or to automatically return to a<br />

card after displaying an error. It_s also very useful for refreshing a<br />

card to display up-to-date dynamic data.


In the following example, the deck displays a splash screen before the<br />

main menu:<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Welcome to ACME Incorporated.<br />

<br />

<br />

<br />

Main menu here...<br />

<br />

<br />

Note: The element's value parameter is measured in tenths<br />

of a second, so our splash screen stays active for 5 seconds (50<br />

tenths).<br />

Tip: The element includes an optional "ontimer" parameter,<br />

which alleviates the need for a separate element. Using<br />

this parameter, the above example could be shortened as follows:<br />

. . .<br />

<br />

<br />

Accepting User Input


Although the forms capability of <strong>WML</strong> isn't as robust as standard<br />

HTML, the element is very flexible and allows a variety of<br />

data to be accepted from the user.<br />

The element has the following minimal form:<br />

<br />

In this form, the element accepts user input and stores it in the<br />

variable specified. The input is freeform; that is, it is not constrained<br />

to any particular format (numeric, alphanumeric, etc.) or length--<br />

although most mobile browsers impose a length limit of 256<br />

characters.<br />

Note: A full discussion of <strong>WML</strong> variables is outside the scope of this<br />

article, but will be provided in the next article. For this exercise it is<br />

important to know that variables exist and can be set by and<br />

elements, among others. A variable can then be referenced<br />

by prefixing its name with a dollar sign ("$"), or using the preferred<br />

method of enclosing the name in parentheses and prefixing the whole<br />

structure with a dollar sign. For example, the variable "firstname" can<br />

be referenced as "$firstname" or "$(firstname)". When the variable is<br />

referenced, <strong>WML</strong> will substitute the variable's value for its reference.<br />

The element also supports the optional parameters shown in<br />

the following table.<br />

Parameter Use<br />

Title Supplies a title for the element. Some devices display this<br />

title in the default Accept soft key label; others display it<br />

as "tooltip" text while the element is selected.<br />

Type Set to "text" or "password," this parameter controls<br />

whether the text is displayed as it is entered or displayed<br />

in the browser's password character(s) (usually "*").<br />

Value The default value for the element. Note that if the<br />

variable specified in the "name" parameter already has a<br />

value, or if the value specified with this parameter doesn't<br />

conform to the specified "format" parameter, the value is<br />

ignored.


Accesskey<br />

Format<br />

Emptyok<br />

Maxlength<br />

Displays the specified number (0-9) next to the element.<br />

The key can be used to quickly select the element.<br />

Specifies the format mask of the input. See the format<br />

section below for details on masking.<br />

Controls whether the element can be left empty. The valid<br />

values for this parameter are "true" (the default) or<br />

"false."<br />

Maximum length of the input, in characters.<br />

Note: The "accesskey" parameter allows you to create a form of<br />

several input fields where the user can easily navigate between the<br />

fields. However, as the Openwave applications point out<br />

(http://developer.openwave.com), wizards that display card-sized<br />

chunks of data entry fields are much better received and make a<br />

better application design.<br />

The "format" parameter's value is composed of a series of characters<br />

that provide a mask for the input. The following table describes the<br />

valid instructional characters for the format mask:<br />

Character Meaning<br />

A<br />

Any symbol or uppercase alpha character (no numbers).<br />

A<br />

Any symbol or lowercase alpha character (no numbers).<br />

N<br />

Any number (no symbols or alphabetic characters).<br />

X<br />

Any symbol, number, or uppercase alpha character<br />

(cannot be changed to lowercase alpha).<br />

X<br />

Any symbol, number, or lowercase alpha character<br />

(cannot be changed to uppercase alpha).<br />

M<br />

Any symbol, number, or alpha character; by default, the<br />

first character is uppercase.<br />

M<br />

Any symbol, number, or alpha character; by default, the<br />

first character is lowercase.<br />

The format mask can also include symbols, which will be displayed in<br />

their appropriate position in the input.<br />

For example, to accept a phone number, including the area code, you<br />

could use this format mask:<br />

format = "(NNN) NNN-NNNN"<br />

When using the same character multiple times in a row, you can prefix


the character with a number indicating how many times it repeats.<br />

Using this method, our phone number mask could be written like this:<br />

format = "(3N) 3N-4N"<br />

However, this format isn't as recognizable as a phone number mask.<br />

Note: To specify an unlimited number of the same character, prefix the<br />

character with an asterisk (*).<br />

A simple deck to accept a person's first name (up to 25 characters)<br />

and then greet the person by name would resemble the following<br />

listing:<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

What is your first name?<br />

<br />

<br />

<br />

<br />

<br />

Hello, $(name)!<br />

<br />

<br />

<br />

Notice that the element uses the "M" format mask, supplying<br />

an uppercase letter as the default first character. The "maxlength"<br />

parameter constrains the input's length, despite the "*" in the mask.<br />

The deck navigation in this example is controlled by a element<br />

linked to the Accept key. In some devices the user will have to press<br />

the Accept key twice--once to exit the input element and another to


trigger the navigation.<br />

There are many useful things you can do with user-supplied input<br />

besides echo it back in other cards. The next article in this series will<br />

discuss several other uses for user input.<br />

Graphics and Icons<br />

The current generation of mobile devices includes very limited graphics<br />

capability. Most mobile browsers only support black-and-white<br />

bitmapped images, although a few recent browsers have added<br />

support for color and even animated formats (GIF, PNG, JPG). Some<br />

mobile gateways also perform on-the-fly conversions of graphics, most<br />

notably from BMP to WBMP.<br />

Tip: Unless you are developing for a particular platform where you<br />

know the capabilities, use only small black-and-white WBMP graphics.<br />

Most graphics applications do not support the WBMP format, but<br />

several converters exist, including a Web-based converter at Teraflops<br />

(http://www.teraflops.com/wbmp/).<br />

Note: Refrain from using graphics whenever possible; using graphics<br />

breaks the mobile application development rules of "fast" and<br />

"simple."<br />

To create a graphic for mobile use you will need a graphics application<br />

that supports the WBMP format, or at least black-and-white bitmap<br />

format and a suitable converter. Drawing upon our earlier splash<br />

screen example, let's create a graphical logo for ACME.<br />

To start, we need to choose a suitable size for the graphic. It's<br />

advisable to stick with a small size such as 100 x 100 pixels. The<br />

graphic shown in the following figure was created with the following<br />

steps (note that the options and procedure might differ in your<br />

graphics application):<br />

1 Create a new graphic, sized 80 x 80 pixels.<br />

2 Convert the graphic to black-and-white, or "line art."<br />

3 Select the appropriate background color (black or white) and fill<br />

the entire graphic.<br />

4 Use the text tool to create text in appropriate sizes and fonts


(using the opposite color of the background).<br />

5 Save the results in GIF format.<br />

6 Use the online tool at Teraflops.com to convert the GIF to WBMP<br />

format.<br />

7 Upload the resulting graphic (acme.wbmp) to the Web server.<br />

Tip: We advise using a graphics program that supports objects instead<br />

of straight bitmapped images. Graphics programs allow you to work<br />

with individual objects_moving, sizing, etc_before committing them to<br />

the electronic canvas.<br />

The ACME Incorporated logo.<br />

Images are added to <strong>WML</strong> cards using the tag. This tag has the<br />

following syntax:<br />

<br />

Note that the "alt" and "src" parameters are mandatory in all <br />

tags. The tag also supports the following optional parameters:<br />

Parameter Use<br />

localsrc Specifies an icon. (See the later section on icons.)<br />

align Aligns the image relative to the current line of text.<br />

Supports "top," "middle," and "bottom."<br />

height Specifies the exact height of the displayed image.<br />

width Specifies the exact width of the displayed image.<br />

hspace Specifies how much white space should be displayed at<br />

each side of the image (horizontal space).<br />

vspace Specifies how much white space should be displayed<br />

above and below the image (vertical space).<br />

Note: Not all mobile browsers support the sizing and spacing<br />

parameters.<br />

Incorporating the logo into our previous splash screen example, we get<br />

the following result (see figure):<br />

<br />

<br />


<br />

<br />

Welcome to<br />

<br />

<br />

<br />

<br />

Main menu here...<br />

<br />

<br />

Our new splash screen.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)


Icons are very small graphics to be used as highlights or<br />

ornamentation. Most mobile browsers support at least a limited<br />

number of icons. For the purposes of this discussion we will use icons<br />

supported by the Openwave browser. A full list of these icons can be<br />

found in the <strong>WML</strong> Language Reference documents on the Openwave<br />

developer site (http://developer.openwave.com).<br />

Icons are placed as graphics, using the tag and its "localsrc"<br />

attribute, along with the appropriate icon number(s). For example, a<br />

smiley face is icon number 68; it would be placed in the contents of a<br />

card with the following tag:<br />

<br />

Note that the "alt" and "src" tags are mandatory, even if left blank.<br />

Tip: When using icons for ornamentation, be sure to specify alternate<br />

text that can serve the same purpose or that resembles the icon.<br />

Let's suppose that ACME is a travel agency. We could add an airplane<br />

icon to the bottom of the splash screen with this tag (see figure):<br />


Our new splash screen complete with airplane icon.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

Tip: Because the <strong>WML</strong> language is fairly limited for emphasizing text,<br />

consider using icons such as triangles (icons 5-8) to draw attention to<br />

specific text. For example, this code is used to call attention to the fact<br />

that the user has reached the end of an article:<br />

<br />

End<br />

<br />

<strong>Learning</strong> <strong>WML</strong> - Variables and Scripting<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

variables and beginning scripting with <strong>WML</strong>Script. Future articles will<br />

cover more advanced scripting and how to integrate other technologies<br />

such as PHP to make your pages more flexible.<br />

Note: These articles cover <strong>WML</strong> and <strong>WML</strong>Script version 1.1, which are<br />

supported by the majority of mobile devices in use today. The articles<br />

assume a working knowledge of HTML and general Web technologies,<br />

and further assume that you have read the previous article(s) in this<br />

series.<br />

Understanding Variables<br />

<strong>WML</strong> supports variables that can hold transitional data between cards,<br />

provide custom output tailored to individual users, and more.<br />

Variables are special holding places for values. They can hold numeric<br />

or alpha values and their values can be changed by code at the<br />

programmer's whim. However, it is usually good practice to dedicate<br />

variables to particular purposes, and hence, to particular types of data.


Variables in <strong>WML</strong> consist of words, enclosed in parentheses, prefixed<br />

with a dollar sign ($). For example:<br />

$(name)<br />

$(address)<br />

$(link)<br />

$(target_url)<br />

$(_method)<br />

Note: Because the dollar sign ($) is used to signify a variable, it is a<br />

reserved character in <strong>WML</strong>. If you want an actual dollar sign to appear<br />

anywhere in your card(s), use a double dollar sign instead ("$$").<br />

Variable names must start with a letter or underscore. Subsequent<br />

characters can be alpha, numeric, or underscores. Variables are case<br />

sensitive; "phone_number" is different from "Phone_Number."<br />

Wherever a variable is referenced, <strong>WML</strong> will substitute the value of the<br />

variable where the variable name appears. For example, the following<br />

code:<br />

<br />

Hello $(name)!<br />

<br />

would produce the following result, if "David" was stored in the<br />

variable $(name):<br />

Hello David!<br />

Note: <strong>WML</strong> reserves the ampersand symbol (&) for entities. To use this<br />

symbol in your deck, you must substitute the corresponding entity<br />

"&amp;".<br />

Setting Variables<br />

Variables can be set by the following elements:<br />

<br />

/<br />


Each of these elements is discussed in more detail below. Note that<br />

some of these elements were introduced in previous articles, and their<br />

parameters and such will not be detailed here.<br />

The Element<br />

The element is straightforward; it accepts user input and<br />

stores it in the variable specified. For example, the following code<br />

would accept input for a phone number and store it in the variable<br />

$(phone_number). The number is then displayed on the second card.<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

What is your phone number?<br />

<br />

<br />

<br />

<br />

<br />

Your number is:<br />

$(phone_number)<br />

<br />

<br />

<br />

Note: The element accepts a "value" attribute that can set a<br />

default value for the input. However, if the variable specified has<br />

already been assigned a value, the "value" attribute is ignored.<br />

The and Elements


Each element within a can also set variables. When<br />

the user selects an option, that option's "value" parameter is stored in<br />

the variable specified by the select's name parameter. Although it<br />

sounds convoluted, it is pretty straightforward in practice. The<br />

following example would store the selected color's hex value in the<br />

variable "color":<br />

<br />

Red<br />

Green<br />

Blue<br />

<br />

For example, if the user selected "Green," the variable "color" would<br />

be set to "#00FF00".<br />

Note: The element accepts a "value" attribute that can set a<br />

default value for the input. However, if the variable specified has<br />

already been assigned a value, the "value" attribute is ignored.<br />

The Element<br />

The element is perhaps the most pure of the variable-setting<br />

elements, because it serves the sole purpose of simply setting a<br />

variable. In the following example, the variable "color" is set to<br />

"Green":<br />

<br />

The use of is complicated by the fact that it can only be<br />

executed upon a , , or action. As such, you<br />

cannot simply embed a anywhere you need to set a variable.<br />

It is common practice to set variables before moving to a card where<br />

you will need them, by embedding the proper elements<br />

within the corresponding or other action element.<br />

If you need to set variables for the current card and doing so from a<br />

previous page is impractical (for example, for the first card displayed<br />

in the deck), you can use and elements to force<br />

a to execute: For example, the following code will set the


appropriate variables when the user enters this card from a forward<br />

direction:<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Note: Unlike the other elements above, the element sets the<br />

value of the specified variable whether or not it has previously been<br />

assigned a value.<br />

Using Variables<br />

Variables are very flexible and can be used in a variety of ways. You<br />

can store the results of user choices or status flags, display variable<br />

text, etc. Using the element, you can even pass a<br />

variable's value to an external program.<br />

The element defines a name/value pair to be sent to the<br />

HTTP server with the next . The element is<br />

encapsulated within elements as follows:<br />

<br />

<br />

. . .<br />

<br />

The element can include an optional "method" parameter that<br />

can be set to "get" or "post," which determines how the variable/value<br />

pairs are sent to the server. There are many differences between the<br />

two methods, but generally speaking "get" encodes the pairs in the<br />

calling URL while "post" sends the pairs in the body of the calling<br />

message. Of the two, "post" should be used wherever possible<br />

because it is more robust.<br />

Using this method, you can pass values to CGI scripts or other<br />

external applications. For example, the following example passes the


name/value pair color/Red to colorselect.cgi:<br />

<br />

<br />

<br />

Note that the "value" attribute is mandatory and is not ignored if the<br />

variable has been set previously. To send a value of a variable that has<br />

been set elsewhere, use the variable tag as the "value" attribute. For<br />

example, the following code will send the current value of the color<br />

variable:<br />

<br />

Note: You can also include variables in the URL of and other<br />

navigation elements by using the standard "?name=value" notation:<br />


Although a full primer on <strong>WML</strong>Script is outside the scope of these<br />

articles, it is important to understand a few basic concepts.<br />

• <strong>WML</strong>Script is case sensitive. All language elements must be spelled<br />

using the proper capitalization of letters.<br />

• <strong>WML</strong>Script supports integer, floating point, string, and Boolean<br />

literals. String literals must be enclosed in single or double quotes ('<br />

' or " ") and Boolean values are set to "true" or "false."<br />

• <strong>WML</strong>Script will automatically convert between values to deliver the<br />

right type of value (integer, string, etc.) to the function that receives<br />

the value.<br />

• Whitespace is ignored in <strong>WML</strong>Script, allowing the programmer to<br />

format the code as desired.<br />

• Comments in <strong>WML</strong>Script can consist of a single line or multiple lines.<br />

Single-line comments start with a double slash (//). For multiple-line<br />

comments, start the block with a slash followed by an asterisk (/*)<br />

and end the block with an asterisk followed by a slash (*/).<br />

• As with any language, <strong>WML</strong>Script has a handful of reserved words<br />

such as "function," "var," "continue," etc. For a full list, consult the<br />

language guide for the version of <strong>WML</strong>Script you are using.<br />

• You must declare all variables in <strong>WML</strong>Script.<br />

• <strong>WML</strong>Script can access variables set in <strong>WML</strong>. You can set a variable in<br />

a <strong>WML</strong> card and reference that same variable in a <strong>WML</strong>Script. You<br />

can also pass values to <strong>WML</strong>Script functions and return values from<br />

the function to the calling <strong>WML</strong>.<br />

• <strong>WML</strong>Script is contained in external files (usually with a <strong>WML</strong>S<br />

extension) and called from standard <strong>WML</strong> navigation elements using<br />

the following expression:<br />

#()<br />

For example, to call the function "validate" in the file "validation.wmls"<br />

you would use<br />

validation.wmls#validate()<br />

Your First Script<br />

The easiest way to learn a scripting language is by example. The<br />

following basic example accepts a number, multiplies it by 2, and


displays the result. This is a relatively useless script, but it helps<br />

simply to illustrate the mechanics between <strong>WML</strong> and <strong>WML</strong>Script.<br />

This example uses the following two files:<br />

sample.wml<br />

1. <br />

2. <br />

3. <br />

4. <br />

5. <br />

6. <br />

7. <br />

8. <br />

9. <br />

10. <br />

11. <br />

12. <br />

13. <br />

14. <br />

15. <br />

16. <br />

17. <br />

18. <br />

19. Your number ( $(num) ) multiplied by 2 is:<br />

20. $(numX2)<br />

21. <br />

22. <br />

23. <br />

sample.wmls<br />

24. extern function multiply() {<br />

25. var Num = <strong>WML</strong>Browser.getVar("num");<br />

26. var NumX2;<br />

27. NumX2 = Num * 2;<br />

28. <strong>WML</strong>Browser.setVar("numX2", NumX2);<br />

29. <strong>WML</strong>Browser.go("sample.wml#card2");<br />

30. }


Notice that the <strong>WML</strong>Script file begins with an "extern" keyword in front<br />

of the function definition. This keyword allows a function to be<br />

accessed from outside the file; for this example, from our <strong>WML</strong> deck.<br />

Functions accessed internally by the same <strong>WML</strong>Script file do not need<br />

this keyword.<br />

Our example executes as follows:<br />

1 As the first card is displayed, the handler causes the<br />

variable "num" to be initialized as blank (lines 5-9). Although not<br />

completely necessary, this step is good practice to ensure that the<br />

variable exists and is in a known state.<br />

2 Input is received via the element and stored in the variable<br />

"num" (line 14). Note that the "format" parameter forces numeric<br />

input.<br />

3 The related element (lines 11-13) calls the "multiply" function<br />

in the wmls file.<br />

4 The "multiply" function declares two variables, setting the variable<br />

"Num" to the value of the XML variable "num," by using the<br />

<strong>WML</strong>Browser.getVar function (lines 25-26).<br />

5 The variable "Num" is multiplied by 2; the resulting value is stored in<br />

the variable "NumX2" (line 27).<br />

6 The script stores the value of NumX2 into a <strong>WML</strong> variable<br />

("numX2"), using the <strong>WML</strong>Browser.setVar function (line 28).<br />

7 The script uses a "go" function to jump to the second card in the<br />

<strong>WML</strong> deck (line 29).<br />

8 The second card (lines 17-22) displays our text, using the variables<br />

"num" and "numX2."<br />

Note: The <strong>WML</strong>Browser library functions (<strong>WML</strong>Browser.setVar, etc.)<br />

provide an easy interface between XML and XMLScript. Consult a<br />

<strong>WML</strong>Script language reference for more information on these<br />

functions.<br />

A More Useful Script<br />

One handy purpose for <strong>WML</strong>Script is validating input and acting<br />

accordingly. For example, suppose we had a card that asked for a twoletter<br />

U.S. state abbreviation. Although the element can


ensure that we have two uppercase letters, it can't match the input<br />

against an acceptable list. For that, we will use <strong>WML</strong>Script.<br />

input.wml<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

$(stateabbr) is a valid State code.<br />

<br />

<br />

<br />

validate.wmls<br />

extern function state() {<br />

var stateabbr = <strong>WML</strong>Browser.getVar("stateabbr");<br />

var pos, x;<br />

var okay = false;<br />

var statelist =<br />

"ALAKASAZARCACOCTDEDCFMFLGAGUHIIDILINIAKS<br />

KYLAMEMHMDMAMIMNMSMOMTNENVNHNJNMNYNCNDMP<br />

OHOKORPWPAPRRISCSDTNTXUTVTVIVAWAWVWIWY";<br />

if (String.length(stateabbr) == 2) {


pos = String.find(statelist,stateabbr);<br />

x = pos / 2;<br />

if (Float.int(x) == x) { okay = true; }<br />

}<br />

if (okay) {<br />

<strong>WML</strong>Browser.go("input.wml#stateok");<br />

} else {<br />

Dialogs.alert("Invalid input!");<br />

<strong>WML</strong>Browser.go("input.wml#state");<br />

}<br />

}<br />

The concept is relatively simple. The <strong>WML</strong> file takes the input, stores it<br />

in the "stateabbr" variable, and then calls the validation function. The<br />

function grabs the state abbreviation that was entered and performs<br />

two checks:<br />

• Is the abbreviation two characters long?<br />

• Does it appear in a valid list?<br />

The String library function "length" is used to determine whether the<br />

abbreviation is two characters long. If so, the second check is<br />

performed. The second check attempts to find the abbreviation in the<br />

"statelist" string. The position is then divided by two-if the result is an<br />

integer, the state is valid.<br />

Note: A simple substring search ("String.find") is not enough to<br />

validate the input, as combinations like "SK" would pass the test. The<br />

abbreviation must be in an even-numbered position (evenly divisible<br />

by 2). If the abbreviation is not found, the find function returns -1,<br />

which also fails the even number test.<br />

More Information on <strong>WML</strong>Script<br />

A wealth of information can be found at the Openwave developer site,<br />

http://developer.openwave.com. Take a look at their Technical Library<br />

and be sure to visit the Developer Forum for answers to more difficult<br />

problems.


<strong>WML</strong> Scripting Tips and Integration with PHP<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

some additional uses for <strong>WML</strong>Script and how to integrate PHP with<br />

<strong>WML</strong>. Future articles will cover more advanced PHP and <strong>WML</strong><br />

techniques.<br />

Note: These articles cover <strong>WML</strong> and <strong>WML</strong>Script version 1.1, which are<br />

supported by the majority of mobile devices in use today. The articles<br />

assume a working knowledge of HTML and general Web technologies,<br />

and further assume that you have read the previous article(s) in this<br />

series.<br />

<strong>WML</strong>Scripting<br />

In the last article we discussed how to use <strong>WML</strong>Script to perform basic<br />

scripting tasks within your <strong>WML</strong> pages. As mentioned in that article,<br />

<strong>WML</strong>Script can be used to validate input and perform basic operations<br />

on <strong>WML</strong> variables. <strong>WML</strong>Script can also be used to control the display of<br />

various cards and the information contained on them. For example,<br />

the following code will display an animation by changing an image's<br />

source and refreshing a card every two seconds:<br />

animation.wml<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Animation Done!


<br />

animation.wmls<br />

extern function main() {<br />

var Num = <strong>WML</strong>Browser.getVar("num");<br />

var Image = "";<br />

if (Num < 9) {<br />

Num++;<br />

Image = "image" + Num + ".wbmp";<br />

<strong>WML</strong>Browser.setVar("num", Num);<br />

<strong>WML</strong>Browser.setVar("image", Image);<br />

<strong>WML</strong>Browser.refresh();<br />

} else {<br />

<strong>WML</strong>Browser.go("animation.wml#card2");<br />

}<br />

}<br />

The deck (animation.wml) sets two variables and forces a refresh.<br />

These are the variables:<br />

num: The number to append to the image source<br />

image: The starting image source (image1.wbmp)<br />

Every two seconds, the main() function in animation.wmls is called.<br />

This function adds one to "num," creates a new image source<br />

(image.wbmp), and refreshes the card, which causes the new<br />

image to be displayed. After the ninth image (image9.wbmp) is<br />

displayed, the script goes to the second card in the deck, ending the<br />

animation.<br />

Note: The above example assumes that your animation consists of<br />

nine images that, when displayed sequentially, create the animation.<br />

<strong>WML</strong>Scripting is flexible and brings basic scripting to <strong>WML</strong>. However,<br />

it's possible to lend more programming power to <strong>WML</strong> by infusing it<br />

with PHP, as covered in the next section.<br />

<strong>WML</strong> Scripting Tips and Integration with PHP


Integrating PHP into <strong>WML</strong><br />

Although <strong>WML</strong> is well suited to most mundane content delivery tasks,<br />

it falls short of being useful for database integration or extremely<br />

dynamic content.<br />

Another Web technology, PHP, fills this gap quite nicely-integrating<br />

into most databases and other Web structures and languages. It's<br />

possible to "cross-breed" mime types in Apache and IIS to enable PHP<br />

to deliver <strong>WML</strong> content. The following sections show you how to<br />

configure your server and how to integrate the two technologies.<br />

Note: This section assumes that you have a basic understanding of<br />

PHP. To learn more about PHP, visit the PHP Web site (www.php.net)<br />

and browse through the language documentation. You can also find a<br />

series of articles here on Developer.com for learning PHP. The first is<br />

<strong>Learning</strong> PHP: The What's and the Why's<br />

What Is PHP?<br />

According to the PHP documentation, "PHP (recursive acronym for<br />

'PHP: Hypertext Preprocessor') is a widely-used Open Source generalpurpose<br />

scripting language that is especially suited for Web<br />

development and can be embedded into HTML."<br />

Essentially, this scripting language is a server-side language that's<br />

processed before being sent to the requesting client. This is in contrast<br />

to scripting languages like JavaScript that are client-side and<br />

processed by the client's browser after being sent to the client.<br />

The benefit of being a preprocessed language is that PHP pages can be<br />

highly dynamic. Decisions can be made from user input, databases,<br />

server conditions, and so on about how the requested content is<br />

delivered to the client. It's exactly what we need to increase the power<br />

of our <strong>WML</strong>.<br />

Installing PHP<br />

PHP is available for most major Web server platforms, including<br />

Apache and Microsoft's Internet Information Server (IIS). Visit


www.php.net, download the appropriate version for your server, and<br />

install it by following the included instructions.<br />

Note: If you're running Debian, Red Hat, or another version of Linux<br />

with a robust packaging system, PHP is probably available in a<br />

preconfigured package. Check your distribution's Web site and<br />

documentation for more info on PHP package(s).<br />

Cross-breeding with <strong>WML</strong>: Apache<br />

To enable <strong>WML</strong> pages to be parsed by PHP, you simply have to add the<br />

<strong>WML</strong> suffix to the PHP application definition in your Apache<br />

configuration file:<br />

AddType application/x-httpd-Php .php .wml<br />

Note the addition of ".wml" to the definition. This will cause the server<br />

to parse all .wml files with PHP. Don't forget to restart Apache so it will<br />

read the new configuration. Make sure that your <strong>WML</strong> definitions<br />

appear before the PHP definition to ensure that Apache recognizes<br />

".wml" as a valid type.<br />

Tip: As mentioned above, this causes all .wml files to be parsed by<br />

PHP. Although this is usually desirable, you could create another mime<br />

type ("pwml") and include it in both the <strong>WML</strong> and PHP definitions.<br />

Then you could continue to use .wml for pure <strong>WML</strong> files and the new<br />

type for PHP-parsed <strong>WML</strong>.<br />

Cross-breeding with <strong>WML</strong>: IIS<br />

To enable <strong>WML</strong> pages to be parsed by PHP, you need to add PHP as an<br />

application to handle your .wml files.<br />

To do this, follow the steps below:<br />

1 Open the Internet Information Services Management Console.<br />

2 Right-click on the site where you want to add this functionality (or<br />

use Default Web Site) and choose Properties.


3 Access the Home Directory tab and click the Configuration button<br />

near the bottom of the dialog.<br />

4 Click the Add button to add an application.<br />

5 Add the path and filename of the PHP executable (usually<br />

"C:\PHP\php.exe") in the Executable field and ".wml" in the<br />

Extension field.<br />

6 Ensure that "Script Engine" and "Check that file exists" are both<br />

checked.<br />

7 Select OK back to the IIS MC.<br />

8 Although not usually required, you may need to restart your Web<br />

server.<br />

In step 6, above, setting the "Check that file exists" option is not<br />

entirely necessary, but can be helpful in debugging server issues.<br />

Without that option being set, IIS will pass all requests for .wml pages<br />

to PHP, even if the requested page doesn't exist. Instead of generating<br />

a "404 - Not Found" error, the Web server will report a problem with<br />

PHP (which failed because there was no file to parse).<br />

These steps will result in all .wml files being parsed by PHP. Although<br />

this is usually desirable, you could create another mime type ("pwml")<br />

and include it in the <strong>WML</strong> definition and define PHP as an application<br />

for the new type. Then you could continue to use .wml for pure <strong>WML</strong><br />

files and the new type for PHP-parsed <strong>WML</strong>.<br />

Getting the Client to Recognize PHP-parsed <strong>WML</strong><br />

Now that the server has been configured to deliver PHP-parsed <strong>WML</strong>,<br />

you need to ensure that the client will receive it as <strong>WML</strong>. This is<br />

accomplished by having each PHP-parsed page send the appropriate<br />

header to the client.<br />

You should begin each PHP file with the following line:<br />

header("Content-type: text/vnd.wap.wml");


This line needs to come before anything else is sent to the client-it<br />

pays to make a habit of including it right after the "\n";<br />

print "\n";<br />

print "\n";<br />

print "\n";<br />

print "Hello world\n";<br />

print "\n";<br />

print "\n";<br />

?><br />

Note that there are several ways to accomplish sending the <strong>WML</strong> to<br />

the client. I've chosen to use individual print statements, each ending<br />

with a newline code. This keeps the resulting <strong>WML</strong> fairly readable and<br />

PHP mimics the line breaks. In some cases, namely long stretches of<br />

code, a print "here document" structure would work better. The above<br />

example would resemble the following in "here document" structure:<br />

print


In any case, remember that your target platform is <strong>WML</strong>-stick to <strong>WML</strong>compatible<br />

tags and structure and avoid using reserved characters<br />

(such as "$") in places where they might be misinterpreted. Also,<br />

debugging the code is twice as hard as straight <strong>WML</strong> because you have<br />

to debug the PHP code as well as the resulting <strong>WML</strong>. The next article in<br />

this series will provide more debugging tips.<br />

Utilizing the Power of PHP<br />

As previously mentioned, PHP's interoperability with other technologies<br />

makes it a powerful ally for <strong>WML</strong>. One such technology, databases, is<br />

especially useful.<br />

As an introduction, we'll cover a basic example here. The next article<br />

in the series will showcase more examples of using PHP to extend<br />

<strong>WML</strong>.<br />

For this example we'll use MySQL, a popular open-source database<br />

solution, although any database with PHP connectivity would work as<br />

well.<br />

Suppose that we have a team of salespeople in the field who need<br />

regular access to customer data. In this example we'll only be<br />

concerned with the customer's phone number, but it illustrates the<br />

underlying power of using PHP while creating a useful "online"<br />

phonebook.<br />

Our database for this example is very simple:<br />

First Name Last Name Phone Number<br />

John Smith 555-723-0900<br />

Kathy Lamarr 555-876-2222<br />

Sam Kinkaid 555-243-8766<br />

Holly Haute 555-327-0987<br />

In MySQL, the database and table would be created using the following<br />

code:<br />

CREATE DATABASE Customer;<br />

USE Customer;


CREATE TABLE Phone (<br />

FirstName varchar(30) default NULL,<br />

LastName varchar(30) default NULL,<br />

Phone varchar(12) default NULL<br />

);<br />

INSERT INTO Phone VALUES ('John','Smith','555-723-0900');<br />

INSERT INTO Phone VALUES ('Kathy','Lamarr','555-876-2222');<br />

INSERT INTO Phone VALUES ('Sam','Kinkaid','555-243-8766');<br />

INSERT INTO Phone VALUES ('Holly','Haute','555-327-0987');<br />

Now we'll create a relatively simple PHP program to display the<br />

customers in a select list. When the user selects an entry, the phone<br />

will dial the selected number.<br />

1.


24. print "\n";<br />

25. ?><br />

The code is fairly straightforward:<br />

• Lines 1-5 are our standard <strong>WML</strong> preamble.<br />

• Lines 6-8 open the required tags.<br />

• Lines 9-12 create the link to the database. Be sure to substitute an<br />

actual username and password for the placeholders.<br />

• Lines 13-15 query the database for all fields in all rows of the<br />

Customers database.<br />

• Lines 16-21 step through the results row by row and build an<br />

for each one.<br />

• Lines 22-24 close all open tags.<br />

Each includes an "onpick" attribute that will dial the selected<br />

number on most devices, via the "wtai://wp/mc;"<br />

URL scheme. This is a useful trick for all users to dial numbers from<br />

within your <strong>WML</strong>.<br />

The generated output resembles the following figure:<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave


SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

Quite a few changes can be made to the code above:<br />

• The "order by" clause of the query can be changed to sort by a<br />

different field or by multiple fields.<br />

• Although the current layout allows each record to be displayed in its<br />

entirety, using the "wrap" option in the tags would allow more<br />

records to be displayed per screen (but each would have to be<br />

highlighted for the full record to be seen).<br />

• This example is convenient because there are only four rows in our<br />

database. To make our user interface more "friendly," we should<br />

create paging code to step through the data nine rows at a time<br />

(ensuring that each row has a quick access key [1-9] associated with<br />

it).<br />

<strong>Learning</strong> <strong>WML</strong> -Scripting Tips And Integration<br />

With PHP<br />

Project Description<br />

In our last article we implemented a simple phone list using PHP and<br />

MySQL. The basic premise was to give salespeople access to a central<br />

contact database without the need of synchronizing their<br />

phones/PDAs. The script pulled contact data from a database,<br />

displayed the records, and dialed the selected record. This project will<br />

expand on that example, adding the following features:<br />

• The contact database will include address information.<br />

• There will be more contacts, requiring the list to be displayed a few<br />

records at a time.<br />

• A search feature will be added.<br />

Because there will be more than one feature (list and search), we will<br />

also need a menu so the user can choose what feature he or she<br />

wants. Each screen will also need a way to return to the menu.<br />

PHP Script Outline<br />

The project will use one PHP script and a controlling variable ($cmd)


that tells the script card that it is supposed to display. The script<br />

recursively calls itself with the proper value of $cmd, matching the<br />

function it is supposed to perform next. The script's functionality<br />

resembles the following:<br />

If $cmd is empty, set it to "Menu"<br />

If $cmd is set to "Menu," display the Menu card<br />

If $cmd is set to "List," display the List card<br />

If $cmd is set to "Display,"<br />

display the Display card, with the appropriate record<br />

If $cmd is set to "Search," display the Search card<br />

When a function is chosen,<br />

set $cmd appropriately and recursively call self.<br />

The search function will search through the database looking for first<br />

or last names that meet the text entered in the search card. The list<br />

function will display five records at a time and will include options for<br />

moving to the next set of five records or returning to the home menu.<br />

Each option (record or link) will have a quick key. Essentially, the list<br />

will resemble the following:<br />

1 <br />

2 <br />

3 <br />

4 <br />

5 <br />

6 [Next 5 link]<br />

7 [Home link]<br />

Selecting a record results in displaying all of the data associated with<br />

the record, along with "dial" and "home" links.<br />

The Contact List<br />

Our contact database will contain the following data:<br />

FirstName LastName Phone Address City State Zip LastUpdate<br />

Jack Hampton 317-555-2200 213 Main St Indianapolis IN<br />

46222 2002-08-22<br />

Samuel Marks 317-555-8764 2 Northridge Dr Fishers IN<br />

46203 2002-08-22<br />

Sally Nash 317-555-8765 644 Innovation Pl Ft Wayne IN<br />

46875 2002-08-22


Bill Haskins 317-555-8766 201 W 103rd Indianapolis<br />

IN 46240 2002-08-24<br />

Jill Payton 317-555-0098 55 W 96th St Westfield<br />

IN 46222 2002-08-22<br />

Mary Martinez 317-555-7544 9433 E. 75th Ave Greenwood<br />

IN 46784 2002-08-22<br />

Ned Tanner 317-555-9877 77 E Marchen Ft Wayne<br />

IN 46875 2002-08-23<br />

Bruce Wilten 317-555-1111 3 Prospect Indianapolis IN<br />

46038 2002-08-22<br />

Naomi Waters 317-555-4323 1121 Central Pl Westfield<br />

IN 46055 2002-08-22<br />

Angela Renault 317-555-0988 5674 E 6th Ave Noblesville<br />

IN 46234 2002-08-22<br />

Markus Elliot 317-555-3232 9755 Carter Indianapolis IN<br />

46250 2002-08-22<br />

Steve Albert 317-555-5444 95 Crescent Dr Indianapolis IN<br />

46250 2002-08-26<br />

Martin Rolfsen 317-555-6767 5678 E 7th Ave Indianapolis<br />

IN 46234 2002-08-22<br />

Lisa Biggins 317-555-3644 7732 Allisonville Indianapolis<br />

IN 46240 2002-08-22<br />

Eric Gonday 317-555-0500 9466 Pike Plaza Greenfield<br />

IN 46533 2002-08-22<br />

Douglas Poser 317-555-0123 55 Tower Pl Noblesville IN<br />

46234 2002-08-24<br />

John Palmer 317-555-4444 12433 N Cumberland Fishers<br />

IN 46038 2002-08-22<br />

The database will also contain an "ID" field as a primary key (integer).<br />

That will serve as a unique key into each record. To create the data in<br />

MySQL, the following commands should be used:<br />

CREATE DATABASE customers;<br />

USE customers;<br />

CREATE TABLE Phone (<br />

Id int(11) NOT NULL auto_increment,<br />

FirstName varchar(30) default NULL,<br />

LastName varchar(30) default NULL,<br />

Phone varchar(12) default NULL,<br />

Address varchar(30) default NULL,<br />

City varchar(30) default NULL,<br />

State char(2) default NULL,<br />

Zip varchar(5) default NULL,<br />

LastUpdate date default NULL,<br />

PRIMARY KEY (Id)<br />

) TYPE=MyISAM;<br />

INSERT INTO Phone VALUES (1,'Jack','Hampton','317-555-2200',<br />

'213 Main St','Indianapolis','IN','46222','2002-08-22');<br />

INSERT INTO Phone VALUES (2,'Samuel','Marks','317-555-8764',<br />

'2 Northridge Dr','Fishers','IN','46203','2002-08-22');


INSERT INTO Phone VALUES (3,'Sally','Nash','317-555-8765',<br />

'644 Innovation Pl','Ft Wayne','IN','46875','2002-08-22');<br />

INSERT INTO Phone VALUES (4,'Bill','Haskins','317-555-8766',<br />

'201 W 103rd','Indianapolis','IN','46240','2002-08-24');<br />

INSERT INTO Phone VALUES (5,'Jill','Payton','317-555-0098',<br />

'55 W 96th St','Westfield','IN','46222','2002-08-22');<br />

INSERT INTO Phone VALUES (6,'Mary','Martinez','317-555-7544',<br />

'9433 E 75th Ave','Greenwood','IN','46784','2002-08-22');<br />

INSERT INTO Phone VALUES (7,'Ned','Tanner','317-555-9877',<br />

'77 E Marchen','Ft Wayne','IN','46875','2002-08-23');<br />

INSERT INTO Phone VALUES (8,'Bruce','Wilten','317-555-1111',<br />

'3 Prospect','Indianapolis','IN','46038','2002-08-22');<br />

INSERT INTO Phone VALUES (9,'Naomi','Waters','317-555-4323',<br />

'1121 Central Pl','Westfield','IN','46055','2002-08-22');<br />

INSERT INTO Phone VALUES (10,'Angela','Renault','317-555-0988',<br />

'5674 E 6th Ave','Noblesville','IN','46234','2002-08-22');<br />

INSERT INTO Phone VALUES (11,'Markus','Elliot','317-555-3232',<br />

'9755 Carter','Indianapolis','IN','46250','2002-08-22');<br />

INSERT INTO Phone VALUES (12,'Steve','Albert','317-555-5444',<br />

'95 Crescent Dr','Indianapolis','IN','46250','2002-08-26');<br />

INSERT INTO Phone VALUES (13,'Martin','Rolfsen','317-555-6767',<br />

'5678 E 7th Ave','Indianapolis','IN','46234','2002-08-22');<br />

INSERT INTO Phone VALUES (14,'Lisa','Biggins','317-555-3644',<br />

'7732 Allisonville','Indianapolis','IN','46240','2002-08-22');<br />

INSERT INTO Phone VALUES (15,'Eric','Gonday','317-555-0500',<br />

'9466 Pike Plaza','Greenfield','IN','46533','2002-08-22');<br />

INSERT INTO Phone VALUES (16,'Douglas','Poser','317-555-0123',<br />

'55 Tower Pl','Noblesville','IN','46234','2002-08-24');<br />

INSERT INTO Phone VALUES (17,'John','Palmer','317-555-4444',<br />

'12433 N Cumberland','Fishers','IN','46038','2002-08-22');<br />

The Cards<br />

The following sections list the PHP code used for each function/card.<br />

Note that the <strong>WML</strong> and header code is taken care of by global<br />

statements-each card need only output the tags and<br />

everything in-between. The $cmd variable controls what function is<br />

executed, and hence, which card is displayed.<br />

Note: I've used "echo" commands in this script, but "print" commands<br />

would do just as well. Also, to increase the readability of the output<br />

I've added line feeds (via a variable, $lf, set to ASCII character 10) to<br />

most lines of output. I've chosen to append this variable to the "echo"<br />

statements for readability purposes--using "\n" in your "echo"


commands would do just as well but tends to clutter the information<br />

between the quotes. Also note the use of the entity "&amp;" in the<br />

URLs instead of a straight ampersand ("&"). This is necessary to keep<br />

<strong>WML</strong> from assuming that the ampersand is the beginning of an entity<br />

name.<br />

Menu<br />

The menu function displays a simple select list, allowing the user to<br />

choose what function he or she wants to access:<br />

echo "\n";<br />

echo "".$lf;<br />

// Set up Select menu list<br />

echo "".$lf;<br />

// Go through results from Query, listing each as a CHOICE entry<br />

echo "";<br />

echo "List Contacts".$lf;<br />

echo "";<br />

echo "Search Contacts".$lf;<br />

// Close select<br />

echo "".$lf;<br />

// Close card<br />

echo "".$lf."".$lf;<br />

The code is straightforward, defining a simple list. Each<br />

in the list calls the current script, passing the appropriate<br />

value of $cmd. Note that we need to handle the case when $cmd is<br />

empty, which it will be when the script is first called. In the body, near<br />

the beginning of the script, we add the following line:<br />

if (empty($cmd)) { $cmd = "Menu"; }<br />

That ensures that if no command is given (via $cmd), the menu will be<br />

displayed.


List<br />

This is the meat of the script, displaying both the raw list as well as<br />

search results, five records at a time. (For reference after the listing,<br />

each line has been numbered.)<br />

1 // Construct appropriate *count* query<br />

2: $query = "select count(*) from Phone";<br />

3: if (!empty($search)) {<br />

4: $query = $query." where FirstName like \"%".$search."%\" or";<br />

5: $query = $query." LastName like \"%".$search."%\"";<br />

6: }<br />

7: $result = mysql_query($query,$link)<br />

8: or die("Query failed:$query");<br />

9: list($total_rows) = mysql_fetch_array($result);<br />

10: // Construct appropriate query<br />

11: $query = "select * from Phone";<br />

12: if (!empty($search)) {<br />

13: $query = $query." where FirstName like \"%".$search."%\" or";<br />

14: $query = $query." LastName like \"%".$search."%\"";<br />

15: }<br />

16: // Get first/next five records<br />

17: $query = $query." order by LastName limit ".$idx.",5";<br />

18: $result = mysql_query($query,$link)<br />

19: or die("Query failed:$query");<br />

20: // Advance DB index<br />

21: $next = $idx + 5;<br />

22: // Start card<br />

23: echo "\n";<br />

24: echo " ".$lf;<br />

25: // Display appropriate full/search heading<br />

26: if (empty($search)) {<br />

27: echo "Phone Book".$lf;<br />

28: } else {<br />

29: echo "Search Results".$lf;<br />

30: }<br />

31: // Set up Select list (list of five records)<br />

32: echo "".$lf;


33: // Go through results from Query, listing each as a CHOICE entry<br />

34: while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {<br />

35: $recordid = $line[Id];<br />

36: $Name = $line[LastName] . ", " . $line[FirstName];<br />

37: $Number = $line[Phone];<br />

38: $Prompt = $Name . " (" . $Number . ")";<br />

39: // Build URL for option, include DB index and search<br />

40: $option = "";<br />

43: $option = $option.$Prompt."".$lf;<br />

44: echo $option.$lf;<br />

45: }<br />

46: // If there are more records to display, set up paging<br />

47: // else mark end of list (to keep Home as same option)<br />

48: if ($total_rows >= $next) {<br />

49: // Link to next five<br />

50: echo "[Next Records]".$lf."".$lf;<br />

56: } else {<br />

57: echo "[End of List]".$lf."".$lf;<br />

64: }<br />

65: // Add option for Home


66: echo "".$lf;<br />

67: echo "[Back to Home]".$lf;<br />

68: echo "".$lf;<br />

69: // Close select<br />

70: echo "".$lf;<br />

71: // Close card<br />

72: echo "".$lf."".$lf;<br />

This code makes use of global variables ($idx, $search) to display the<br />

list of contacts. Both variables are passed as a name/value pair when<br />

the script is called with $cmd equal to "List." If $search is empty, the<br />

full list of contacts is displayed, else the text of $search is added to the<br />

query and records are returned only if FirstName or LastName contains<br />

the search text.<br />

The $idx variable marks what results the script currently is listing. The<br />

record at location $idx is the first record on the current page.<br />

This becomes more self-explanatory as we work through the code:<br />

Lines 1-9 construct and execute a "count" query, storing the number<br />

of returned rows in the variable $total_rows. This value is used later<br />

(line 48) to determine whether there are more pages of data to<br />

display. Note that the search text is added if $search is not empty<br />

(hence contains search criteria).<br />

Lines 10-19 construct a query to return the target dataset. Again, the<br />

search criterion is added to the query, if it exists. Line 17 appends a<br />

limit clause to the query, causing the query to return only five records<br />

(or less), starting at the record indicated by the value of $idx. If $idx<br />

is zero (which it will be the first time the script executes List), the first<br />

five records are returned. The variable $next is set to a value of $idx +<br />

5 (line 21) and used to call the next iteration of List, causing the next<br />

five records to be displayed.<br />

Note: This method is far from perfect. For example, if a record is<br />

added or modified that causes a record to be added or removed from<br />

the returned dataset, the next page will return different results than it


would before the record was added or modified. If the list is being<br />

displayed while the records are being modified-which happens in most<br />

database applications-the displayed results can be somewhat<br />

unpredictable.<br />

Lines 22-30 begin the display card definition, including the appropriate<br />

header-"Phone Book" if the search string is empty (raw list being<br />

displayed) or "Search Results" if the search string is not empty (search<br />

results being displayed). This helps guarantee that the result set<br />

returned will be the same, allowing consistent paging through the set<br />

(with the caveat explained above).<br />

Line 32 begins the list, with lines 34-70 building and<br />

displaying five items as s. Lines 35-38 build the text for the<br />

prompt, while lines 39-44 construct the statement<br />

with an appropriate "onpick" parameter that recursively calls the script<br />

with $cmd equal to "Display" and the ID of the record to display. Note<br />

that $idx and $search are also passed to maintain their values through<br />

the recursive call, just in case we need them later.<br />

Lines 49-64 build option number 6 in our select list. If there are more<br />

records to display ($total_rows >= $next) the script generates the<br />

option "[Next Records]" with an appropriate "onpick" parameter to<br />

recursively call the script, specifying the next starting record to display<br />

(via idx=$next). If there are no more records to display, the script<br />

generates an "[End of List]" option, which recursively calls the script<br />

with the same starting point as is currently displayed. Each option also<br />

includes the search criteria if it exists (lines 51-54 and 58-61).<br />

Finally, a "Home" option is created (lines 65-68) to allow the user to<br />

return to the home menu from any page of the listing. The open tags<br />

are then closed.<br />

Search<br />

The search function is simply an input tag that accepts up to 10<br />

characters and recursively calls the script supplying the text entered<br />

and sets $cmd equal to "List."<br />

echo "\n";<br />

echo "".$lf;


echo "".$lf;<br />

echo "".$lf;<br />

echo "".$lf;<br />

echo "".$lf;<br />

echo "Phone Book SearchSearch for:".$lf;<br />

echo "";<br />

echo "".$lf;<br />

echo "".$lf;<br />

Display<br />

Display uses the record ID passed in $idx to select a record from the<br />

database and display all of its related information (name, address,<br />

phone, etc.).<br />

echo "\n";<br />

// Get specific record<br />

$query = "select * from Phone where Id = \"".$idx."\"";<br />

$result = mysql_query($query,$link)<br />

or die("Query failed:$query");<br />

// Get data and display<br />

while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {<br />

$recordid = $line[Idx];<br />

}<br />

echo "".$lf;<br />

echo "$line[LastName], $line[FirstName]".$lf;<br />

echo "$line[Address]".$lf;<br />

echo "$line[City], $line[State] $line[Zip]".$lf;<br />

echo "";<br />

echo "$line[Phone]".$lf;<br />

echo "";<br />

echo "[Home Menu]".$lf;<br />

$Date = date("M j, Y", strtotime($line[LastUpdate]));<br />

echo "Record Updated:$Date";<br />

// Close record display card<br />

echo $lf."".$lf;


Note that the card has a "Dial" option mapped to the Accept key and is<br />

displayed with the phone number highlighted. This allows the user to<br />

quickly dial the selected number on devices that support URL dialing.<br />

Other items of note include a "Home Menu" link and a more verbose<br />

format for the last update date. We do not need to provide any<br />

functionality to return to the last page of record listings-the user can<br />

do so by pressing the Back key on his or her device.<br />

The Entire Script<br />

Now that we've defined the various functions of the script, let's tie it<br />

all together with a "switch" statement and some additional initialization<br />

statements:<br />


if (empty($idx)) { $idx = 0; }<br />

if (empty($cmd)) { $cmd = "Menu"; }<br />

switch ($cmd) {<br />

case "Menu";<br />

echo "\n";<br />

echo "".$lf;<br />

// Set up Select menu list<br />

echo "".$lf;<br />

// Go through results from Query, listing each as a CHOICE entry<br />

echo "";<br />

echo "List Contacts".$lf;<br />

echo "";<br />

echo "Search Contacts".$lf;<br />

// Close select<br />

echo "".$lf;<br />

// Close card<br />

echo "".$lf."".$lf;<br />

break;<br />

case "List";<br />

// Construct appropriate *count* query<br />

$query = "select count(*) from Phone";<br />

if (!empty($search)) {<br />

$query = $query." where FirstName like \"%".$search."%\" or";<br />

$query = $query." LastName like \"%".$search."%\"";<br />

}<br />

$result = mysql_query($query,$link)<br />

or die("Query failed:$query");<br />

list($total_rows) = mysql_fetch_array($result);<br />

// Construct appropriate query<br />

$query = "select * from Phone";<br />

if (!empty($search)) {<br />

$query = $query." where FirstName like \"%".$search."%\" or";<br />

$query = $query." LastName like \"%".$search."%\"";<br />

}


Get first/next five records<br />

$query = $query." order by LastName limit ".$idx.",5";<br />

$result = mysql_query($query,$link)<br />

or die("Query failed:$query");<br />

// Advance DB index<br />

$next = $idx + 5;<br />

// Start card<br />

echo "\n";<br />

echo " ".$lf;<br />

// Display appropriate full/search heading<br />

if (empty($search)) {<br />

echo "Phone Book".$lf;<br />

} else {<br />

echo "Search Results".$lf;<br />

}<br />

// Set up Select list (list of five records)<br />

echo "".$lf;<br />

// Go through results from Query, listing each as a CHOICE entry<br />

while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {<br />

}<br />

$recordid = $line[Id];<br />

$Name = $line[LastName] . ", " . $line[FirstName];<br />

$Number = $line[Phone];<br />

$Prompt = $Name . " (" . $Number . ")";<br />

// Build URL for option, include DB index and search<br />

$option = "";<br />

$option = $option.$Prompt."".$lf;<br />

echo $option.$lf;<br />

// If there are more records to display, set up paging<br />

// else mark end of list (to keep Home as same option)<br />

if ($total_rows >= $next) {<br />

// Link to next five<br />

echo "


Pass Search criteria if exists<br />

if (!empty($search)) {<br />

echo "&amp;search=".$search;<br />

}<br />

echo "\">[Next Records]".$lf."".$lf;<br />

} else {<br />

}<br />

echo "[End of List]".$lf."".$lf;<br />

// Add option for Home<br />

echo "".$lf;<br />

echo "[Back to Home]".$lf;<br />

echo "".$lf;<br />

// Close select<br />

echo "".$lf;<br />

// Close card<br />

echo "".$lf."".$lf;<br />

break;<br />

case "Search";<br />

echo "\n";<br />

echo "".$lf;<br />

echo "".$lf;<br />

echo "".$lf;<br />

echo "".$lf;<br />

echo "".$lf;<br />

echo "Phone Book SearchSearch for:".$lf;<br />

echo "";<br />

echo "".$lf;


echo "".$lf;<br />

break;<br />

case "Display";<br />

echo "\n";<br />

// Get specific record<br />

$query = "select * from Phone where Id = \"".$idx."\"";<br />

$result = mysql_query($query,$link)<br />

or die("Query failed:$query");<br />

// Get data and display<br />

while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {<br />

$recordid = $line[Idx];<br />

}<br />

echo "".$lf;<br />

echo "$line[LastName], $line[FirstName]".$lf;<br />

echo "$line[Address]".$lf;<br />

echo "$line[City], $line[State] $line[Zip]".$lf;<br />

echo "";<br />

echo "$line[Phone]".$lf;<br />

echo "";<br />

echo "[Home Menu]".$lf;<br />

$Date = date("M j, Y", strtotime($line[LastUpdate]));<br />

echo "Record Updated:$Date";<br />

// Close record display card<br />

echo $lf."".$lf;<br />

break;<br />

}<br />

echo $lf."".$lf;<br />

mysql_close($link);<br />

?>


Note that we pass a handful of headers at the beginning of the script<br />

to inhibit caching. Since our database is frequently updated and<br />

correct/up-to-date information in the field is valuable, we do not want<br />

the device to display cached information instead of recently updated<br />

information. However, generally speaking, inhibiting the cache is a bad<br />

idea and should be done sparingly, if at all.<br />

The Script in Action<br />

Now let's see the script in action. The following figures demonstrate<br />

each function:<br />

Note: All images courtesy Openwave Systems Inc. (Openwave, the<br />

Openwave logo, Openwave SDK, Openwave SDK Universal Edition,<br />

Openwave SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems<br />

Inc. All rights reserved.)<br />

Figure 1 - The menu.


Figure 2 - The list. Notice the [Next Records] option.<br />

Figure 3 - The end of the list. Notice the [End of List] option.


Figure 4 - The search form.<br />

Figure 5 - The results of a search (for "in"). Note the first result<br />

is "Biggins, Lisa" but the text has scrolled to the phone number<br />

due to the "nowrap."


Figure 6 - A record in the display card.<br />

Room for Improvement<br />

This script has plenty of room for improvement, including the following<br />

items:<br />

• Applying the cache inhibitor headings only to cards that could cause<br />

problems (such as Display), instead of globally.<br />

• Optimizing the output to avoiding duplicating code (such as the<br />

addition of search criteria to the URL(s)).<br />

• Adding more prompts for the user through card titles, etc.<br />

• Optimizing and standardizing variable naming and usage.<br />

• Providing means for the user to edit records. Although it can be<br />

tedious to enter data on most mobile devices, simple corrections or<br />

notes would be welcome. A "last called" field could also be entered<br />

automatically each time a contact is called.<br />

This script represents only a small portion of what can be done with<br />

PHP and a database such as MySQL. This example could be expanded<br />

to offer group calendaring, scheduling, order placement, stock<br />

checking, etc. As long as you keep the target audience and the<br />

respective design goals in mind, the sky's the limit.


Delivering HTML To a <strong>WML</strong> Device<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

techniques to use when delivering standard HTML to <strong>WML</strong>-compatible<br />

devices.<br />

Note: These articles cover <strong>WML</strong> and <strong>WML</strong>Script version 1.1, which are<br />

supported by the majority of mobile devices in use today. The articles<br />

assume a working knowledge of HTML and general Web technologies,<br />

and further assume that you have read the previous article(s) in this<br />

series.<br />

Delivering Converted HTML<br />

There may be several reasons why you may need to deliver standard<br />

HTML markup text to a <strong>WML</strong>-compatible device. You may have data<br />

stored in a database that is typically displayed in a standard browser;<br />

legacy data or pages that resist conversion; or cross-platform text that<br />

needs to be primarily available for a standard HTML browser, but<br />

would be useful if delivered to <strong>WML</strong> clients.<br />

For example, I'm the administrator for a movie news Web site. The<br />

articles for the site are marked up using standard HTML, stored in a<br />

SQL database, and delivered to the clients using PHP pages. The bitesized<br />

articles also make for great wireless content_something that can<br />

be browsed while in an airport or during other downtime_so I decided<br />

to make this content available in <strong>WML</strong>. Unfortunately, I quickly found<br />

out how incompatible even minor HTML tags are with <strong>WML</strong>, creating<br />

the need for some simple conversion procedures. Although not perfect,<br />

those procedures are used as the basis for this article.<br />

Tip: The quick-and-dirty methods described in this article are handy as<br />

a temporary or short-term measure. If you intend to support a<br />

particular platform long-term, I recommend creating custom code for<br />

that platform.<br />

Standard HTML vs. <strong>WML</strong><br />

Standard HTML documents don't work well on <strong>WML</strong>-capable devices.


Even if the wireless services offer translation services through their<br />

gateways, standard HTML seldom displays as the developer or user<br />

would like.<br />

Limited Tags<br />

<strong>WML</strong> supports a very limited subset of HTML tags. Among those<br />

supported are the following tags:<br />

• Character formatting<br />

◦ - Bold<br />

◦ - Italic<br />

◦ - Underline<br />

◦ - Big text<br />

◦ - Small text<br />

◦ - Strong (visually emphasized) text<br />

◦ - Paragraph<br />

• Table tags<br />

◦ <br />

◦ - Table row<br />

◦ - Table column/cell<br />

Several tags nearly alike between the two languages, but their<br />

formatting and/or parameters are different enough to cause problems.<br />

For example, the line break tag is simply in HTML, but in<br />

<strong>WML</strong>. Also, tags such as the table tags support many more options and<br />

parameters in HTML than in <strong>WML</strong>, and rarely allow HTML tables to<br />

display properly in <strong>WML</strong>.<br />

Device Display Limitations<br />

Standard HTML documents are generally designed for large displays,<br />

such as 800 x 600 resolution CRTs connected to a PC, not a 240 x 320<br />

LCD on a PDA (or smaller, if a cell phone). Even devices that run<br />

HTML-compliant browsers (such as IE in Windows CE devices) have<br />

problems with the majority of today's Web sites.


The almost unrecognizable internet.com home page, displayed in IE on<br />

a Pocket PC (Windows CE).<br />

Tip: To gauge roughly how a page will look on a smaller device, shrink<br />

your standard PC browser window down to that size.<br />

Most mobile devices don't support the vast array of text formatting<br />

available toPC browsers. For example, earlier versions of certain<br />

mobile browsers don't support underlining; others don't support italic<br />

or bold text. Tables are especially problematic due to their width.<br />

Device memory is also a problem. Most mobile browsers only support<br />

pages (decks) a few kilobytes in size, requiring the content to be<br />

broken down into bite-sized chunks and displayed across several<br />

cards, if not several decks.


Finally, most modern PC-based browsers (IE, Mozilla, Netscape, and so<br />

on) have built-in logic to handle incomplete or misused tags. For<br />

example, most PC browsers are forgiving of HTML documents that fail<br />

to close a major element such as a table or the body of the document.<br />

Most mobile browsers are far less forgiving, requiring very strict use of<br />

tags.<br />

Device Input Limitations<br />

Interactive Web pages present even more challenges to the mobile<br />

user. Anyone who has needed to tap/write out even a short note on a<br />

PDA can appreciate the need to keep interfaces simple. Those who<br />

have tried to compose more than just a few characters on a standard<br />

cell phone keypad can appreciate this even more strongly.<br />

The simplest Web interface is the form, whose structure is<br />

considerably different in <strong>WML</strong>. Simply converting the structure and<br />

tags isn't sufficient; you also have to consider how it will affect the end<br />

user on his or her individual platform. For example, choosing the<br />

correct state code from a drop-down list is easy on a standard<br />

browser. However, drop-down lists translate to select lists in <strong>WML</strong>,<br />

necessitating a list of 50 entries that the user must scrolled through<br />

(usually 9 items per page) to select the proper code.<br />

When Is Converting Worth the Effort?<br />

Given the discussion above, there are a few HTML-to-<strong>WML</strong> conversions<br />

that are more problematic than they are worth:<br />

• Tables<br />

Unless you know that every table in the document is extremely<br />

narrow and contains no fancy formatting/parameters, you should<br />

simply remove the tags.<br />

• Graphics<br />

Some gateways will convert graphic files into the prerequisite WBMP<br />

format. However, most will simply refuse to display the standard<br />

JPG/GIF/PNG Web formats. Unless you have the appropriate<br />

graphics available in the WBMP format, remove the graphics.<br />

• Code<br />

HTML pages that rely on Java, JavaScript, or some other scripting


language generally will not be compatible with mobile devices,<br />

especially those compatible only with <strong>WML</strong>. Devices using IE (such<br />

as CE-equipped PDAs) will fare much better, but you can't rely on<br />

that.<br />

In short, only textual pages are worth the time to convert. More<br />

complex pages should be redesigned for each individual platform you<br />

want to support. Keep in mind that straight <strong>WML</strong> does not have the<br />

facility to convert HTML--you must use a CGI or PHP script to deliver<br />

the content instead.<br />

Note: See the two previous articles on how to integrate PHP into your<br />

<strong>WML</strong> delivery.<br />

Conversion Procedures<br />

Converting standard HTML-formatted text is a two-step process. First,<br />

remove any tags that are not supported by the target platform.<br />

Second, tailor supported tags to the target platform. For example, the<br />

line break tag is supported by <strong>WML</strong>, but needs to have the slash added<br />

("").<br />

Removing Unsupported Tags<br />

To ensure a smooth conversion, remove all but the following tags from<br />

the HTML code:<br />

• <br />

• <br />

• <br />

You can also retain text-formatting tags that your target browser<br />

supports, such as , , etc.<br />

If you are using PHP, the code to strip the offending tags is very<br />

simple:<br />

$wml = strip_tags($html,'');<br />

Using the HTML (stored in $html), the above code removes all tags but<br />

those given in the "strip_tags" parameter, and stores the result in the


variable $wml.<br />

If you are feeling adventurous and know the format of tables in the<br />

code, you can parse the table tags down to the bare minimum<br />

parameters (as supported by your target browser). However, only the<br />

smallest tables will display conveniently on mobile devices.<br />

Converting Supported Tags<br />

Although paragraph () and line break () tags are supported<br />

in <strong>WML</strong>, their usage varies from that in HTML. For example, blocks of<br />

text must be enclosed in paragraph tags; you cannot use a stray tag<br />

to separate paragraphs, like this:<br />

Paragraph . . .<br />

<br />

Paragraph . . .<br />

Although such use is sloppy when used anywhere, it has become<br />

prevalent in HTML pages. Instead of creating a sophisticated parsing<br />

scheme to ensure the matching pairs of tags, it's much easier to<br />

convert all open and closing paragraph tags to double line break tags.<br />

This causes the current line to break where the paragraph tag was<br />

used, and inserts the extra space between the paragraphs.<br />

Again, if you are using PHP, the code is straightforward:<br />

$wml = str_replace("","",$wml);<br />

$wml = str_replace("","",$wml);<br />

The above code will replace every "" and "" with "".<br />

Each line break tag in <strong>WML</strong> must end in a slash. A similar PHP<br />

str_replace statement takes care of this requirement:<br />

$wml = str_replace("","",$wml);


Note: PHP functions that support regular expressions can be more<br />

versatile and can do more work per statement if constructed correctly.<br />

I prefer to use individual statements for later flexibility and clearer<br />

code.<br />

Miscellaneous Cleanup<br />

Two more items need to be cleaned up to display correctly in <strong>WML</strong>:<br />

ampersands ("&") and dollar signs ("$"). An ampersand must be<br />

converted to an entity ("&amp;"), and a dollar sign must be doubled<br />

("$$").<br />

Again, in PHP you can use the str_replace function:<br />

$wml = str_replace("&","&amp;",$wml);<br />

$wml = str_replace("$","$$",$wml);<br />

Note: An abundance of special characters can find their way into<br />

otherwise mundane HTML code. For example, when text is cut-andpasted<br />

from a word processing document into HTML documents, single<br />

and double quotes usually appear as extended ASCII characters, and<br />

must be converted to the appropriate plain text characters or HTML<br />

entities. Only direct experience and experimentation with your specific<br />

documents can determine what problems you may have and need to<br />

work around.<br />

Interactive Fun and Games with <strong>WAP</strong> and<br />

<strong>WML</strong><br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

creating an interactive game for deployment on mobile <strong>WML</strong> devices.<br />

Note: These articles cover <strong>WML</strong> and <strong>WML</strong>Script version 1.1, which are<br />

supported by the majority of mobile devices in use today. The articles<br />

assume a working knowledge of HTML and general Web technologies,<br />

and further assume that you have read the previous article(s) in this<br />

series.


Uses for Mobile Devices<br />

Mobile devices are the most useful when they are connected to data<br />

sources and have the ability to deliver various data whenever needed.<br />

However, mobile devices are also very useful for entertainment<br />

purposes-I've spent many hours in airports with only my PDA and<br />

phone for company. Although <strong>WML</strong> doesn't lend itself to a complex<br />

gaming experience, it is fairly easy to create interactive entertainment.<br />

In this article I will lead you through the steps to create a rudimentary<br />

"hangman" game.<br />

Project Specifications<br />

Hangman is a straightforward game. For anyone unfamiliar with the<br />

game, a word is shown as blanks and the player guesses letters that<br />

may be in the word. If a letter guessed is in the word, all instances of<br />

that letter are revealed. If a guessed letter is not in the word, part of a<br />

hanging stick figure is drawn on a scaffold, at the end of a<br />

noose_usually starting with the head, then the body, then the limbs<br />

(one at a time). The game ends when the word is guessed or the<br />

figure is completely drawn. In the former case the player wins, in the<br />

latter the player loses.<br />

The functionality for our project is as simple as the pen-and-paper<br />

version of the game:<br />

1 A word is chosen and displayed as blanks.<br />

2 The player picks a letter.<br />

3 The letter is compared to the letters in the word; if the letter is in<br />

the word, the appropriate blanks are changed to display the letter.<br />

If the letter is not in the word, a piece is added to the hanging stick<br />

figure.<br />

4 The game ends when the user knows the word or the figure is<br />

complete.<br />

Once the functionality has been determined, the interface must be<br />

drafted.<br />

• All output must fit on a display that is approximately 16 characters<br />

wide and 4-10 lines tall.<br />

• The stick figure will be represented by ASCII characters instead of<br />

graphics since we can't ensure that the player will be using a


graphics-capable device. The figure will be made of 6 segments,<br />

giving the player 6 letter selections.<br />

• Several cards will be used, one for each function. That will help<br />

segregate the functions and keep the display lean.<br />

• The words must be random and taken from a decent-sized list to<br />

keep the player engaged.<br />

• To simplify coding, all words will be in lowercase and the player's<br />

input will be forced to lowercase.<br />

Coding the <strong>Basics</strong><br />

This project will utilize <strong>WML</strong> for the input and output and <strong>WML</strong>Script<br />

for the behind-the-scenes processing. Because debugging tools for<br />

<strong>WML</strong>Script are limited, we will build this project in small stages, adding<br />

features only after the current feature set is solid.<br />

To start, we will create a simple deck of two cards. The first card will<br />

call a <strong>WML</strong>Script function to initialize the game's variables. The second<br />

card will then be displayed, showing the word as blanks and as plain<br />

text (for debugging purposes).<br />

Our skeletal code looks like this:<br />

<strong>WML</strong><br />

1 <br />

2 <br />

4 <br />

5 <br />

6 <br />

7 <br />

8 <br />

9 <br />

10 <br />

11 <br />

13 Initializing...<br />

14


15 <br />

16 <br />

17 <br />

18 $word<br />

19 $blank<br />

20 <br />

21 <br />

22 <br />

<strong>WML</strong>S<br />

1 extern function initword() {<br />

2 // Define word list (pseudo array)<br />

3 var words = "animal announce banana doctor elephant giraffe";<br />

4 var idx,x = 0;<br />

5 var blank,word = "";<br />

6 // Randomize a word<br />

7 idx = Lang.random(6);<br />

8 word = String.elementAt(words,idx," ");<br />

9 // Add an "*" for every letter in the chosen word<br />

10 for (x = 1; x


pushed out to the browser and the next card is displayed, showing all<br />

the values.<br />

Note: Even this part of the script was developed in pieces, though for<br />

the sake of brevity in this article I've chosen to start with this chunk of<br />

code. Initially, I created the stub <strong>WML</strong> code to call the init function,<br />

which simply set a variable. Then I created the word list and<br />

randomized a word, which was displayed by the <strong>WML</strong>. Finally, I<br />

created the blanking code to create the blank word.<br />

Tip: The Openwave SDK provides a great environment to develop<br />

applications in a stairstep method. Visit the developer site at<br />

www.openwave.com for more information or to download the SDK.<br />

Next we need to add the player entry code and the ability to check the<br />

entry against the word. The new code resembles the following listings:<br />

<strong>WML</strong><br />

1 <br />

2 <br />

4 <br />

5 <br />

6 <br />

7 <br />

8 <br />

9 <br />

10 Initializing...<br />

11 <br />

12 <br />

13 <br />

14 <br />

15 <br />

16 <br />

17 <br />

18 Guess: <br />

19 $word<br />

20 $blank<br />

21 <br />

22 <br />

23


<strong>WML</strong>S<br />

1 extern function initword() {<br />

2 var words = "animal announce banana doctor elephant giraffe";<br />

3 var idx,x,hang = 0;<br />

4 var blank,word = "";<br />

5 idx = Lang.random(6);<br />

6 word = String.elementAt(words,idx," ");<br />

7 for (x = 1; x


the new $blank variable.<br />

Image courtesy Openwave Systems Inc. (Openwave, the Openwave<br />

logo, Openwave SDK, Openwave SDK Universal Edition, Openwave<br />

SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems Inc. All rights<br />

reserved.)<br />

The new code allows for input and displays debugging output. Note<br />

that "a" was guessed in the previous round, revealing the 3 a's in<br />

"banana." Because the $guess variable is persistent, the value sticks in<br />

the input field. We will have to fix that eventually.<br />

Adding the Scoring (Hangman)<br />

As Figure 8.1 shows, we are quickly running out of screen real estate<br />

for our game. Since we are using text to depict our hanging man, we<br />

will need 4-6 more lines to display him - and we obviously do not have<br />

enough lines. Time for an interface change.<br />

Instead of putting the input on the same page as the status, we will<br />

move it to a "guess" card. It means more work for the user (he or she<br />

must select a "guess" button to display the input), but cleans up our<br />

interface.<br />

For the hanging man, we will use the following ASCII representation:


0<br />

-|-<br />

/ \<br />

He is crude but recognizable. We can break the man down into six<br />

pieces: the head, torso, two arms, and two legs. If we get creative and<br />

treat each piece as a string, we can add appropriate line breaks and<br />

print each string in sequence, effectively "building" the man. Consider<br />

the following:<br />

Head: "0"<br />

Arm1: "-"<br />

Torso: "|"<br />

Arm2: "-"<br />

Leg1: "/"<br />

Leg2: "\"<br />

When printed in sequence, the hanging man is displayed. If we print<br />

only the first three strings, only three pieces of the man (head, arm,<br />

torso) are displayed.<br />

Again, because <strong>WML</strong>S lacks real arrays, we will build a delimited string<br />

that contains all the pieces:<br />

" , 0\r,-,|,-\r,/,\\\r"<br />

Note that a comma is used as the delimiting character, and that we<br />

put a blank piece (a space) in the beginning to give the first real piece<br />

an index of 1 instead of 0. Also note that we use escape codes for line<br />

breaks (\r = newline) and we must escape the backslash. Because<br />

<strong>WML</strong> doesn't parse variables, using <strong>WML</strong> code for line breaks would<br />

only result in the tags (such as "") being displayed as text<br />

instead of being interpreted as line breaks.<br />

Finishing Up


Although it seems like there is a lot left to do, the project is mopped<br />

up pretty quickly by doing the following:<br />

• Adding a variable and code to count the incorrect guesses and<br />

determine loss<br />

• Checking whether the word has been totally revealed (win)<br />

• Adding appropriate cards for win/loss<br />

• Building a string variable to display the hanging man<br />

• Various housekeeping and cleanup chores (renaming some<br />

functions, cleaning up some variables, adding comments, etc.)<br />

Most of these tasks revolve around counting the number of guesses.<br />

The last task is the usual housekeeping that takes place at the end of<br />

a project. The final code looks like this:<br />

<strong>WML</strong><br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

Initializing...<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

&nbsp;|<br />

$man<br />

$blank<br />


<br />

<br />

<br />

<br />

<br />

<br />

$blank<br />

Guess: <br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

You've been hanged!<br />

Word was:<br />

&nbsp;$word<br />

Your guess:<br />

$blank<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

<br />

You win!<br />

Word was:<br />

&nbsp;$word<br />

<br />

<br />

<br />

<strong>WML</strong>S<br />

// hangman.wmls<br />

// Functions for hangman game


Variables:<br />

// word = word to be guessed<br />

// blank = blank representation of 'word', letters<br />

// begin as asterisks ("*") and are revealed as guessed<br />

// hang = current count of incorrect guesses<br />

// man = current ASCII representation of hanging man<br />

// guess = current character guessed<br />

// Initialize game<br />

extern function init() {<br />

// Init vars. Change words every so often (or add to them)<br />

var words = "animal announce banana doctor elephant giraffe";<br />

var idx,x,hang = 0;<br />

var blank,word,man = "";<br />

// Pick a random word from list<br />

idx = Lang.random(6);<br />

word = String.elementAt(words,idx," ");<br />

// Build a blank string (letters all "*") that<br />

// is the same length as our word<br />

for (x = 1; x


Get current values<br />

var word = <strong>WML</strong>Browser.getVar("word");<br />

var hang = <strong>WML</strong>Browser.getVar("hang");<br />

var blank = <strong>WML</strong>Browser.getVar("blank");<br />

var guess = <strong>WML</strong>Browser.getVar("guess");<br />

// Walk one character at a time through word<br />

// If guess = character, reveal character<br />

// If guess != character, keep current value<br />

// (revealed character or blank)<br />

// Also, set "correct" if at least one char found<br />

for (x = 0; x


} else {<br />

<strong>WML</strong>Browser.go("hangman.wml#status");<br />

}<br />

}<br />

}<br />

The starting screen of the game, complete with hangman's noose.<br />

Pressing the Accept key brings up the guessing card.


As the player progresses, correct guesses are shown by revealing<br />

characters in the word; incorrect guesses add to the hanging man.<br />

Images are courtesy Openwave Systems Inc. (Openwave, the<br />

Openwave logo, Openwave SDK, Openwave SDK Universal Edition,<br />

Openwave SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems<br />

Inc. All rights reserved.)<br />

If the player guesses six incorrect letters, the game ends. Guessing all


characters in the word, conversely, shows a "win" screen. Note the<br />

"Restart" function mapped to Accept.<br />

Things to Add<br />

There's always room for improvement, especially in programming and<br />

interfaces. Given time, I'd add the following to the game:<br />

• A graphic version of the hanging man as a default, allowing users<br />

to switch to text if necessary.<br />

• Tracking and displaying the incorrect characters guessed<br />

• A splash screen (introductory graphical screen), help text, and a<br />

cheat function (displays one character not yet revealed)<br />

I'd also lengthen the word list and dynamically change it from time to<br />

time. One idea would be to run a script on the server that swaps<br />

different versions of the <strong>WML</strong>S file into place, each with a different<br />

word list. Another idea is to implement the entire game using PHP or<br />

another, more robust scripting language that could tie into a massive<br />

word database.<br />

What Do You Want from <strong>WML</strong>?<br />

I'm interested in hearing what you need/want to do with <strong>WML</strong>. I'll use<br />

some of the more challenging or common ideas in upcoming articles.<br />

Send your ideas to the address below.<br />

Building <strong>WML</strong> Gadgets: World Time Clock<br />

Review<br />

As mentioned in the last few articles, it is possible to add value to a<br />

mobile device by creating a small but ultimately useful application. In<br />

designing such an application, remember that the user will need to be<br />

online to use it, so the application's utility needs to be weighed against<br />

the potential cost of use.<br />

Note: Most mobile service plans offer a base amount of online time<br />

dedicated to "Web" use. So the user generally isn't paying more for<br />

the occasional gadget use.


World Time Application<br />

This article describes how to create another useful small application: a<br />

world time clock. This clock tells the time in prominent time zones and<br />

areas around the world. If you need to make a call to Sydney,<br />

Australia, for example, it would be nice to know if you are in danger of<br />

waking someone up, or are calling during the lunch hour.<br />

Our world time clock should accomplish the following:<br />

• Allow the user to search for a given time zone or area<br />

• Display the accurate time for the zone(s) found<br />

• Be easy to use, but complete enough to be useful<br />

More Help from CGI<br />

We could utilize only mobile technologies (<strong>WML</strong> and <strong>WML</strong>Script) to<br />

accomplish our goals. However, we'd have to do the following:<br />

• Find a comprehensive time zone database<br />

• Parse the database into an array in <strong>WML</strong>Script<br />

• Do time and date calculations to determine the time in various zones<br />

from the resulting data<br />

At first, this approach doesn't seem too bad. There are a finite number<br />

of time zones worldwide. However, you also have to take daylight<br />

saving time into account. For example, central Indiana (which includes<br />

Indianapolis) does not observe daylight savings. This means that<br />

Indiana, geographically in the Central time zone, seemingly bounces<br />

between Central and Eastern time zones. (In reality, central Indiana<br />

stays on Eastern Standard Time, and the zones around it shift.)<br />

A comprehensive open source database exists that can be used for this<br />

purpose. The database, often referred to as the tz or zoneinfo<br />

database, is packaged with most implementation of the GNU C Library,<br />

primarily Linux installations. Several tools are available for reading the<br />

data from the database, including a Perl module, Time::Timezone<br />

(available from CPAN, www.cpan.org).<br />

Note: As with previous articles, teaching Perl is out of the scope of this<br />

series. There are numerous sources on the Internet for learning Perl,


including the tutorial at<br />

http://wdvl.internet.com/Authoring/Languages/Perl/PerlfortheWeb/toc.<br />

html.<br />

Starting the Script -- Reading Time Zones and Telling Time<br />

To start the script, we will simply output the time zones and the<br />

current time for each. The simple script shown below accomplishes this<br />

objective:<br />

Listing: tztest.pl<br />

#!/usr/bin/perl<br />

use Time::ZoneInfo ':all';<br />

my $zones = Time::ZoneInfo->new();<br />

foreach my $zone ($zones->zones) {<br />

print $zone."\n";<br />

}<br />

This script simply creates a list of all the available zones and outputs<br />

each to the console. Next, we need to know what time it is in each<br />

zone. Fortunately, the operating system (Linux, in this case) can do<br />

the work for us.<br />

Most Linux applications that need to tell time do so by referencing the<br />

environment variable TZ. This variable contains the current time zone<br />

for the system. The OS can use this variable to decode its internal time<br />

clock into the correct value for the zone. If you change this variable<br />

and request the time from an application that uses it, you can easily<br />

tell the time in another zone.<br />

With help from the Date::Calc module, we can add the current time<br />

zone to our test:<br />

Listing: tztest.pl<br />

#!/usr/bin/perl


use Time::ZoneInfo ':all';<br />

use Date::Calc ':all';<br />

my $zones = Time::ZoneInfo->new();<br />

foreach my $zone ($zones->zones) {<br />

$ENV{TZ} = $zone;<br />

($year,$month,$day,$hour,$min,$sec, $doy,$dow,$dst) =<br />

System_Clock();<br />

print $zone.":";<br />

print $year."-".$month."-".$day." ";<br />

print $hour.":".$min.":".$sec."\n";<br />

}<br />

Sample tztest.pl output:<br />

. . .<br />

America/New_York: 2002-12-29 23:35:22<br />

America/Detroit: 2002-12-29 23:35:22<br />

America/Louisville: 2002-12-29 23:35:22<br />

America/Kentucky/Monticello: 2002-12-29 23:35:22<br />

America/Indianapolis: 2002-12-29 23:35:22<br />

America/Indiana/Marengo: 2002-12-29 23:35:22<br />

America/Indiana/Knox: 2002-12-29 23:35:22<br />

America/Indiana/Vevay: 2002-12-29 23:35:22<br />

America/Chicago: 2002-12-29 22:35:22<br />

America/Menominee: 2002-12-29 22:35:22<br />

America/North_Dakota/Center: 2002-12-29 22:35:22<br />

America/Denver: 2002-12-29 21:35:22<br />

America/Boise: 2002-12-29 21:35:22<br />

America/Shiprock: 2002-12-29 21:35:22<br />

America/Phoenix: 2002-12-29 21:35:22<br />

America/Los_Angeles: 2002-12-29 20:35:22<br />

America/Anchorage: 2002-12-29 19:35:22<br />

America/Juneau: 2002-12-29 19:35:22<br />

America/Yakutat: 2002-12-29 19:35:22<br />

America/Nome: 2002-12-29 19:35:22<br />

America/Adak: 2002-12-29 18:35:22


. . .<br />

Note that we simply change the TZ environment variable and call the<br />

System_Clock method. The variable is only changed within the<br />

application space, so the change in time zone doesn't affect the whole<br />

system--just our application.<br />

Searching for Time Zones<br />

Next, we need the ability to search for a particular time zone. Adding a<br />

variable, "findzone", and a simple substring search accomplishes this<br />

goal:<br />

Listing: tztest.pl<br />

#!/usr/bin/perl<br />

use Time::ZoneInfo ':all';<br />

use Date::Calc ':all';<br />

my $findzone = (shift @ARGV);<br />

my $zones = Time::ZoneInfo->new();<br />

foreach my $zone ($zones->zones) {<br />

if ((index uc($zone), uc($findzone)) != -1) {<br />

$ENV{TZ} = $zone;<br />

($year,$month,$day,$hour,$min,$sec, $doy,$dow,$dst) =<br />

System_Clock();<br />

print $zone.":";<br />

print $year."-".$month."-".$day." ";<br />

print $hour.":".$min.":".$sec."\n";<br />

}<br />

}<br />

Note the use of the IF statement. We uppercase both the zone and the<br />

findzone variables to help ensure a match (if "asia" is entered, it will


still match all "Asia" entries). For example, running the script with this<br />

entry:<br />

./tztest.pl pacific<br />

displays the following:<br />

Pacific/Easter: 2002-12-29 23:40:20<br />

Pacific/Galapagos: 2002-12-29 22:40:20<br />

Pacific/Yap: 2002-12-30 14:40:20<br />

Pacific/Truk: 2002-12-30 14:40:20<br />

Pacific/Ponape: 2002-12-30 15:40:20<br />

Pacific/Kosrae: 2002-12-30 15:40:20<br />

Pacific/Tarawa: 2002-12-30 16:40:20<br />

Pacific/Enderbury: 2002-12-30 17:40:20<br />

Pacific/Kiritimati: 2002-12-30 18:40:20<br />

Pacific/Majuro: 2002-12-30 16:40:20<br />

Pacific/Kwajalein: 2002-12-30 16:40:20<br />

Pacific/Auckland: 2002-12-30 17:40:20<br />

Pacific/Chatham: 2002-12-30 18:25:20<br />

Pacific/Tahiti: 2002-12-29 18:40:20<br />

Pacific/Marquesas: 2002-12-29 19:10:20<br />

Pacific/Gambier: 2002-12-29 19:40:20<br />

Pacific/Johnston: 2002-12-29 18:40:20<br />

Pacific/Midway: 2002-12-29 17:40:20<br />

Pacific/Wake: 2002-12-30 16:40:20<br />

Pacific/Honolulu: 2002-12-29 18:40:20<br />

Suppose that the user doesn't know what time zone he/she is looking<br />

for. For example, Pacific/Honolulu covers Hawaii, but isn't necessarily<br />

intuitive to someone looking for "Hawaii." To help the user, we will add<br />

"ALL" as a valid search that returns all zones. To do so, we doctor the<br />

IF statement accordingly:<br />

if (((index uc($zone), uc($findzone)) != -1) ||<br />

("uc($findzone)" eq "ALL" )) {


Now, if the user enters "ALL" for the search, he/she will get all known<br />

zones.<br />

Creating <strong>WML</strong> Cards<br />

Using methods described in previous articles, it's relatively easy to<br />

output compliant <strong>WML</strong> for mobile devices. We will need another Perl<br />

module, CGI, for a couple of purposes:<br />

• Parsing arguments passed to the script<br />

• Supplying the mobile browser with an appropriate <strong>WML</strong> header<br />

Note: After my calendar gadget article was posted, a reader wrote to<br />

chastise me for writing my own argument-parsing routine. As I stated<br />

in the article, the fact that the script was passing itself cleanly<br />

formatted parameters was justification for simple argument parsing.<br />

However, one point I missed (brought up by the reader) was that the<br />

code might go on to be incorporated in other scripts where the sterility<br />

of the parameters could not be guaranteed. To help promote solid<br />

coding, I've pledged to use the CGI method "param" from now on.<br />

To output a <strong>WML</strong> card, we use the following code:<br />

# Pass <strong>WML</strong> header<br />

print header(-type=>'text/vnd.wap.wml');<br />

# Print <strong>WML</strong> header and beginning tags<br />

print


# Print closing tags<br />

print


if ( "$findzone" eq "" ) {<br />

# No time zone specified; display input card<br />

print zones) {<br />

if (((index uc($zone), uc($findzone)) != -1) ||<br />

( "uc($findzone)" eq "ALL" )) {<br />

# Display each match, or all if "ALL" was entered<br />

print $zone." :";<br />

$ENV{TZ} = $zone;<br />

($year,$month,$day, $hour,$min,$sec,<br />

$doy,$dow,$dst) = System_Clock();<br />

print $year."-".$month."-".$day." ";<br />

print $hour.":".$min.":".$sec."\n";<br />

$matches++;<br />

}<br />

}


# If no time zone matches, tell user<br />

if ( $matches eq 0 ) {<br />

print "No time zone match for: \n";<br />

print $findzone."";<br />

}<br />

}<br />

# Print closing tags<br />

print


As with previous projects, several things could be added to improve<br />

our world time clock:<br />

• A better format for the time output. Right now values under 10 are<br />

output as a single digit. For example, nine o'clock AM is displayed<br />

as: "9:0." Using a format mask or some simple logic we could pad<br />

the time accordingly (e.g., "09:00").<br />

• Break long listings into multiple pages/cards. For example, the "ALL"<br />

timezone listing ends up weighing in at just under 5K. That's five<br />

times the suggested 1K card data limit. Using some simple logic,<br />

the Perl script could display the data in a sequence of cards, each<br />

containing 7-12 records.<br />

• Add a control on the results card(s) to return to the search card.<br />

• Find/create a more comprehensive time zone database that<br />

includes named zones such as EST, CST, etc. (A comprehensive<br />

database can be compiled by downloading the source files found at<br />

ftp://elsie.nci.nih.gov/pub.)<br />

Building <strong>WML</strong> Gadgets: Phone Message<br />

Application<br />

This series of articles describes how to provide Web content to mobile<br />

devices through <strong>WML</strong> (Wireless Markup Language). This article covers<br />

creating an application to aid the user of a mobile phone.<br />

Note: These articles cover <strong>WML</strong> and <strong>WML</strong>Script version 1.1, which are<br />

supported by the majority of mobile devices in use today. The articles<br />

assume a working knowledge of HTML and general Web technologies,<br />

and further assume that you have read the previous article(s) in this<br />

series.<br />

Simple Applications<br />

Not all wireless applications have to be super-applications. Some of<br />

the best wireless applications perform simple tasks to improve wireless<br />

functionality. The last few articles in this series have shown how<br />

simple, single-purpose gadgets can boost the functionality of mobile<br />

devices. This article will present a slightly more complex application in


the same "extending functionality" vein.<br />

The Application<br />

This article will cover how to build a simple phone message<br />

application. Although we live in a time of portable phones, intelligent<br />

voicemail, and other electronic telephone magic, there are still times<br />

when messages are taken by one person (operator) and passed to<br />

others (recipients). For example, consider a businessman who often<br />

travels outside the home office. Many of his customers and contacts<br />

may occasionally call the home office and leave messages with his<br />

secretary. Using a simple Web form, the secretary can pass the<br />

message to the businessman's cell phone, where he can review the<br />

message and even return the call with the simple press of a button.<br />

Application Specifications<br />

This application will utilize the following components:<br />

• A simple HTML form to input the message<br />

• A flat-file database to store the messages<br />

• A CGI script to access the database<br />

Essentially, the application operates as shown in the following<br />

diagram:


FIGURE 1 - Our application's design. The operator uses a Web form to<br />

send the data to a CGI script that stores the data in a database. The<br />

same script is used by a mobile user (recipient) to access that data.<br />

We'll use Perl for the CGI script, for the same reasons we've used it<br />

previously: It's available for most platforms and extensible enough to<br />

perform almost any task necessary.<br />

Note: As with previous articles, teaching Perl is out of the scope of this<br />

series. There are numerous sources on the Internet for learning Perl,<br />

including the tutorial at<br />

http://wdvl.internet.com/Authoring/Languages/Perl/PerlfortheWeb/toc.<br />

html.<br />

Coding the Application<br />

Let's break down the individual processes and then code for each.<br />

Database Design<br />

Our "database" will be a simple delimited flat file. Although we could<br />

go the fancy route with an actual database format, the delimited


format will work well for our simple application.<br />

The database will contain the following fields:<br />

• Date and time the message was taken<br />

• Caller's name<br />

• Caller's message<br />

• Caller's phone number<br />

We'll use a double vertical bar for our delimiter. We could use a more<br />

standard delimiter, such as a comma, but we need something that<br />

wouldn't end up in the middle of the message field. In short, our<br />

database records will resemble the following:<br />

||||


<br />

Note that we include an extra field, "cmd." This hidden field will be<br />

passed to our CGI script to tell it what to do; namely, "save" the data.<br />

The CGI Script<br />

Our CGI script will be one multipurpose script, performing the<br />

following functions:<br />

• Saving the data (caller info)<br />

• Listing the caller record(s)<br />

• Displaying a selected record's details<br />

• Automatically dialing the caller's number<br />

• Optionally deleting the record and then calling the number<br />

The script could also display the input form. However, for maximum<br />

portability, we'll use a simple HTML file. With this method, our form<br />

can easily be included in almost any Web page template, simply by<br />

applying a style sheet or by cutting-and-pasting the "guts" of the form<br />

into another page.<br />

Saving the Data<br />

The following code fragment saves the data entered into the form:<br />

Listing: phonemsg.pl - Save data fragment<br />

# Start response page<br />

print header;<br />

print "";<br />

print "";<br />

print "Please wait...";<br />

# Grab the parameters<br />

$from = param('from');<br />

$message = param('message');<br />

$callback = param('number');<br />

# Remove any vertical bars<br />

$message =~tr/|/ /;<br />

# Check file lock


$count = 0;<br />

if (-e "msgfile.lock") {<br />

select(undef,undef,undef,0.1);<br />

$count++;<br />

if ($count = 10) { die "Can't open message file! "; }<br />

}<br />

open LOCK, ">msgfile.lock";<br />

# Write new record to end of file<br />

open FILE, ">>msgfile.txt";<br />

print FILE $date ." ". $time ."||";<br />

print FILE $from ."||". $message ."||". $callback;<br />

print FILE "\n";<br />

close FILE;<br />

close LOCK;<br />

unlink "msgfile.lock";<br />

# All done, close response page<br />

print "";<br />

Because the form and the mobile device could both be utilizing the<br />

script (and hence, the database) simultaneously, we must use file<br />

lockingto avoid having two processes accessing the database and<br />

corrupting our data. We'll use a simple method: creating a file to lock<br />

the database_if the file exists, the process waits for it to be deleted<br />

before accessing the database. When a process is done with the<br />

database, the lock file is deleted and other processes are allowed to<br />

access the database. Instead of failing right away if the file is locked,<br />

or waiting forever for the file to be unlocked, the code loops 10 times,<br />

waiting a tenth of a second between the iterations. If the file is still<br />

locked, we assume that something has gone wrong and exit with an<br />

appropriate error message.<br />

Windows users: Some implementations of Perl on the Windows<br />

platform don't support the four-argument call of "select" used in our<br />

file-locking loop. If this code generates errors on a Windows system,<br />

substitute another delay function.<br />

Listing the Records on the Mobile Device<br />

The following code fragment comprises the code to list five records on<br />

the mobile device, along with an option to move to the next five


ecords:<br />

Listing: phonemsg.pl - List records fragment<br />

#Pass <strong>WML</strong> header<br />

print header(-type=>'text/vnd.wap.wml');<br />

# Print <strong>WML</strong> header and beginning tags<br />

print


# (nextitem + 4 or end of file)<br />

if ($nextitem + 4


When the user picks a record from the laundry list of records, the<br />

script needs to be able to display the record's details. The following<br />

code takes care of that activity:<br />

Listing: phonemsg.pl - Display specific record fragment<br />

# What record to display<br />

$rec = param('rec');<br />

#Pass <strong>WML</strong> header<br />

print header(-type=>'text/vnd.wap.wml');<br />

# Print beginning of <strong>WML</strong> file<br />

print


$datetime<br />

$from<br />

$message<br />

Callback Number:<br />

$callback<br />

<br />

Call<br />

<br />

Call & Del<br />

<br />

<br />

<br />

MESSAGE<br />

The record number to display is passed to the script via the "rec"<br />

variable. As in the other cases, the file lock is checked, the database is<br />

locked, the database is read into an array, and then the database is<br />

released (unlocked). The required record is then read from the array,<br />

unpacked into fields (based on the delimiter), and displayed.<br />

Notice that two links are placed at the bottom of the displayed record:<br />

"Call" and "Call & Del(ete)". The first simply uses the "call this<br />

number" URL format to make the mobile device dial a number. The<br />

latter option allows the user to delete the message before placing the<br />

call, since the message is being returned and should no longer be<br />

stored in the database as an open message.<br />

Placing a Call and Optionally Deleting a Record<br />

The script handles returning a call by including a link to a URL<br />

containing the number to call (see the previous section). Deleting a<br />

record before the call is slightly more complex, and is handled by the<br />

following code:<br />

Listing: phonemsg.pl - Place call/delete record fragment<br />

# What number to call and what record to delete<br />

$number = param('number');<br />

$rec = param('rec');<br />

#Pass <strong>WML</strong> header<br />

print header(-type=>'text/vnd.wap.wml');


# Print start of <strong>WML</strong> file<br />

print


close LOCK;<br />

unlink "msgfile.lock";<br />

# Close card<br />

print " \n \n ";<br />

To delete and call a number, the script is passed the number and the<br />

record number to delete. The number could be retrieved from the<br />

record before the deletion, but is passed separately so it doesn't have<br />

to be parsed from the record. This design decision falls into the "halfa-dozen<br />

versus six" category of decisions; I opted to pass the number<br />

we already have, saving the lines required to decode the record before<br />

deleting it.<br />

To perform the deletion, we read the entire file into an array and then<br />

reconstruct the file by writing all records except the deleted record<br />

back to the file.<br />

The Completed Script<br />

After adding a few declaration lines, the controlling structure using the<br />

$cmd variable, and some connecting tissue, our completed script<br />

becomes the following:<br />

Listing: phonemsg.pl<br />

#!/usr/bin/perl<br />

# Include modules<br />

use CGI qw(:standard);<br />

use Date::Calc qw(:all);<br />

# Set command to execute; default = list<br />

$cmd = param('cmd');<br />

if ("$cmd" eq "") { $cmd = "list"; }<br />

# Set current time/date<br />

($year,$month,$day) = Today();<br />

($hour,$min,$sec) = Now();<br />

$time = $hour.":".$min;<br />

$date = $month."/".$day."/".$year;<br />

#<br />

# Call from HTML form, save the data, and return to form<br />

if ("$cmd" eq "save") {


# Start response page<br />

print header;<br />

print "";<br />

print "";<br />

print "Please wait...";<br />

# Grab the parameters<br />

$from = param('from');<br />

$message = param('message');<br />

$callback = param('number');<br />

# Remove any vertical bars<br />

$message =~tr/|/ /;<br />

# Check file lock<br />

$count = 0;<br />

if (-e "msgfile.lock") {<br />

select(undef,undef,undef,0.1);<br />

$count++;<br />

if ($count = 10) { die "Can't open message file! "; }<br />

}<br />

open LOCK, ">msgfile.lock";<br />

# Write new record to end of file<br />

open FILE, ">>msgfile.txt";<br />

print FILE $date ." ". $time ."||";<br />

print FILE $from ."||". $message ."||". $callback;<br />

print FILE "\n";<br />

close FILE;<br />

close LOCK;<br />

unlink "msgfile.lock";<br />

# All done, close response page<br />

print "";<br />

}<br />

#<br />

# Call from mobile device to list calls<br />

if ("$cmd" eq "list") {<br />

#Pass <strong>WML</strong> header<br />

print header(-type=>'text/vnd.wap.wml');<br />

# Print <strong>WML</strong> header and beginning tags<br />

print


<br />

<br />

ENDHEADER<br />

# Print start of card<br />

print


print "";<br />

print $from ." (". $datetime .") \n";<br />

print " \n";<br />

}<br />

# Set nextitem for NEXT function<br />

$nextitem = $lastitem + 1;<br />

# Display NEXT option<br />

print "";<br />

print "Next \n";<br />

print " \n \n";<br />

print " \n \n";<br />

}<br />

#<br />

# Call from mobile device to show call detail<br />

if ("$cmd" eq "display") {<br />

# What record to display<br />

$rec = param('rec');<br />

#Pass <strong>WML</strong> header<br />

print header(-type=>'text/vnd.wap.wml');<br />

# Print beginning of <strong>WML</strong> file<br />

print


push(@lines,$_) while ();<br />

close FILE;<br />

close LOCK;<br />

unlink "msgfile.lock";<br />

# Split record into fields<br />

($datetime,$from,$message,$callback) = split /\|\|/,$lines[$rec];<br />

# Display record with "call"<br />

# and "call & delete" options<br />

print


ENDHEADER<br />

# Print beginning of card<br />

# (Number is dialed in 10 secs)<br />

print


Note that the default action of the script is to list the records. This<br />

allows the mobile device to call the script without arguments<br />

("http://URL/phonemsg.pl") to get the ball rolling. Subsequent calls<br />

are handled by the script ("display," "callNdel," etc.) where it controls<br />

the parameters, saving the mobile user from having to<br />

enter/bookmark them.<br />

To test the application, we seed the database with the following data:<br />

Listing: msgfile.txt - Sample data<br />

1/25/2003 12:15||Caller Number01||Sample message, from sample caller.||317-<br />

555-1212<br />

1/25/2003 12:25||Caller Number02||Sample message, from sample caller.||317-<br />

555-1212<br />

1/25/2003 12:35||Caller Number03||Sample message, from sample caller.||317-<br />

555-1212<br />

1/25/2003 13:15||Caller Number04||Sample message, from sample caller.||317-<br />

555-1212<br />

1/26/2003 14:15||Caller Number05||Sample message, from sample caller.||317-<br />

555-1212<br />

1/26/2003 14:23||Caller Number06||Sample message, from sample caller.||317-<br />

555-1212<br />

1/26/2003 15:15||Caller Number07||Sample message, from sample caller.||317-<br />

555-1212<br />

1/27/2003 9:15||Caller Number08||Sample message, from sample caller.||317-<br />

555-1212<br />

1/27/2003 9:35||Caller Number09||Sample message, from sample caller.||317-<br />

555-1212<br />

1/28/2003 8:05||Caller Number10||Sample message, from sample caller.||317-<br />

555-1212<br />

1/28/2003 10:15||Caller Number11||Sample message, from sample caller.||317-<br />

555-1212<br />

1/28/2003 11:11||Caller Number12||Sample message, from sample caller.||317-<br />

555-1212<br />

1/28/2003 12:01||Caller Number13||Sample message, from sample caller.||317-<br />

555-1212<br />

1/28/2003 14:15||Caller Number14||Sample message, from sample caller.||317-<br />

555-1212<br />

1/28/2003 16:45||Caller Number15||Sample message, from sample caller.||317-<br />

555-1212<br />

1/29/2003 8:25||Caller Number16||Sample message, from sample caller.||317-<br />

555-1212<br />

1/29/2003 9:04||Caller Number17||Sample message, from sample caller.||317-<br />

555-1212<br />

1/29/2003 10:35||Caller Number18||Sample message, from sample caller.||317-<br />

555-1212


1/29/2003 10:39||Caller Number19||Sample message, from sample caller.||317-<br />

555-1212<br />

1/30/2003 12:15||Caller Number20||Sample message, from sample caller.||317-<br />

555-1212<br />

1/30/2003 15:02||Caller Number21||Sample message, from sample caller.||317-<br />

555-1212<br />

1/30/2003 16:05||Caller Number21||Sample message, from sample caller.||317-<br />

555-1212<br />

Using this data, our application resembles the following graphics on a<br />

mobile device:<br />

FIGURE 2 - The laundry list of messages.


FIGURE 3 - A selected message is displayed.<br />

FIGURE 4 - Two links at the bottom of the record allow the user to call<br />

and optionally delete the message.<br />

Images are courtesy Openwave Systems Inc. (Openwave, the<br />

Openwave logo, Openwave SDK, Openwave SDK Universal Edition,<br />

Openwave SDK <strong>WAP</strong> Edition are trademarks of Openwave Systems<br />

Inc. All rights reserved.)


Room for Improvement<br />

This application makes a nice, general phone message system.<br />

However, given time and incentive, the following improvements could<br />

be made:<br />

• The code could be streamlined. Because it was written in sections for<br />

this article, the code is not as svelte as it could be_in multiple<br />

places, code is duplicated that could be placed in commonly<br />

accessed functions/subroutines. Also, the code breaks a few "good<br />

Perl coding" rules (non-local variables, loose variable naming, etc.);<br />

that problem should be rectified.<br />

• There's no value checking in the HTML form and it is only set up to<br />

accept domestic numbers (12 characters, area code, prefix, suffix,<br />

and two dashes).<br />

• A real database structure could be used for the data, alleviating the<br />

need for stringent file locking and enabling true random access.<br />

• Another option could be added to enable the user to back up through<br />

the list of messages (we already allow forward access via the Next<br />

link).<br />

• A search feature could be added to find particular messages or to<br />

display messages in a specified timeframe.<br />

• Multiple users could be added by specifying the person taking the<br />

message (operator) and the person for whom the message is<br />

designated (recipient). Then multiple operators could take<br />

messages for multiple recipients. This would also necessitate a login<br />

or other authentication process for the mobile user (identifying<br />

himself/herself as the intended recipient), unless multiple users<br />

return calls from "the pool."<br />

• A status field could be added so the records could be<br />

tracked. Instead of the record simply existing ("need to call") or being<br />

deleted ("called"), a message could be flagged for a variety of<br />

purposes, including archiving.<br />

Tracking Users Using <strong>WML</strong><br />

This article describes how to provide Web content to mobile devices<br />

through <strong>WML</strong> (Wireless Markup Language). More specifically, this<br />

article covers how to track users; that is, how to recognize a repeat


visitor to your site.<br />

<strong>WML</strong> and <strong>WML</strong>Script version 1.1 are supported by the majority of<br />

mobile devices in use today. The articles assume a working knowledge<br />

of HTML and general Web technologies, and further assume that you<br />

have read the previous article(s) in this series.<br />

The Value of Recognizing Users<br />

There are a variety of reasons to implement a recognition system that<br />

acknowledges that a user that has previously visited your site. The<br />

most useful of these reasons is to remember user preferences.<br />

For example, suppose that you offer a service of finding particular<br />

restaurants near a user. Each user may prefer a certain type of food,<br />

environment, etc., and knowing where the user is located is important<br />

so that you can find restaurants in that vicinity. Of course, users don't<br />

want to input all their preferences every time they visit your site—it's<br />

better to save most of the settings, recognize users when they return,<br />

and recall their settings.<br />

How To Recognize Users<br />

When <strong>WML</strong> was first implemented, the code could retrieve the user's<br />

cell phone number from the device; this phone number could act as a<br />

unique identifier. Unfortunately, this ability was recognized as an<br />

invasion of privacy, and the feature was discontinued. Now there is no<br />

way to retrieve a unique identifier from the user's device.<br />

What options are left? You could have the user input his or her<br />

telephone number on each visit, assign the visitor a login name, or<br />

have the user enter an identifier during each visit. Better yet, why not<br />

store the identifier on the user's device for recall each time he or she<br />

visits your site?<br />

Cookies: The Good, the Bad, and the Ugly<br />

The term cookie refers to the HTTP technology that allows a site to<br />

store data on a user's machine. When cookies were first used, they<br />

were fairly innocuous, intended primarily for storing user preferences.


However, it didn't take long for the business side of the Web to realize<br />

the potential of cookies and begin using them for their own purposes—<br />

tracking user activities, shopping habits, and other bits of personal<br />

information.<br />

Shortly thereafter, the media and user advocates led the charge<br />

against cookies, causing most modern browsers to offer the option of<br />

refusing to store any cookies, or allowing the user to choose when a<br />

cookie should or should not be stored. Unfortunately, refusing cookies<br />

causes problems for Web sites that use cookies to store user<br />

preferences and login info, requiring the user to reenter such info on<br />

each visit.<br />

Note: I don't condone the illicit use of cookies for tracking users'<br />

personal information, but do recognize the utility of storing frequently<br />

used information to aid the user experience.<br />

How Cookies Work<br />

Cookies work by storing data on the user's local computer/device. This<br />

data is stored via an HTTP dialog between the server and the client.<br />

That data can then be recalled by the server, processed, updated, etc.<br />

Figure 1 shows the data paths associated with storing and retrieving<br />

cookies.<br />

Figure 1 - Cookie data is passed back and forth between the<br />

server and client via the HTTP stream, but the data is actually


stored on the client side.<br />

The information stored in the cookie can be just about any type of<br />

data: string, date, an integer, or a real number. Most sites choose to<br />

encode cookie data into a lengthy string that can be decoded and<br />

parsed by the site code.<br />

Note: Windows users can examine the cookies that have been stored<br />

on the local machine. Look in your local settings directory(ies) for<br />

"cookies" files. Windows XP users can find the cookies in the following<br />

directory:<br />

C:\Documents and Settings\{Username}\Cookies<br />

In addition to data, cookies are stored with a time to live (TTL),<br />

specifying how long the cookie should be stored before being<br />

discarded. The cookie also indicates the scope for which it should be<br />

used—that is, what directory(ies) on the server are valid for that<br />

cookie. This allows a site to store multiple cookies with the same<br />

name, but different scopes. For example, a site with several sections<br />

could store preferences for each section in a cookie named "prefs."<br />

Using Cookies with <strong>WML</strong><br />

Because <strong>WML</strong> doesn't have any built-in cookie functions, you have to<br />

use other technologies to store and retrieve cookies. This article shows<br />

how to use Perl, which has robust cookie-handling abilities.<br />

We'll use the HTTP header Set-Cookie to set cookies in the examples.<br />

Although <strong>WML</strong> has a tag, don't confuse the HTTP header with<br />

the <strong>WML</strong> card head—they're different animals. Cookies must be set<br />

before the end of the HTTP header; the <strong>WML</strong> comes after the<br />

HTTP header has been sent, and therefore it cannot be used to set<br />

cookies.<br />

You can use almost any language that supports cookie functions to<br />

accomplish the goals in this article. For example, PHP's header()<br />

function can be used to set cookies. Even more control can be<br />

accomplished with PHP's setcookie() function. Several variable<br />

structures exist in PHP to read cookies, including<br />

$HTTP_COOKIE_VARS and $_COOKIE arrays.


Handling Cookies with Perl<br />

There are many options for handling cookies in Perl, including simple<br />

HTTP methods and even dedicated Perl libraries such as cookie-lib.pl.<br />

We'll use the simple HTTP methods for this article.<br />

For more robust cookie management, the reader is encouraged to<br />

check out other cookie-handling methods, such as cookie-lib.pl and<br />

HTTP::Cookies. The former is available online at The CGI Resource<br />

Index<br />

(http://cgi.resourceindex.com/Programs_and_Scripts/Perl/Cookies/);<br />

the latter is from CPAN (http://search.cpan.org/author/RSE/lcwa-<br />

1.0.0/lib/lwp/lib/HTTP/Cookies.pm).<br />

Setting a Cookie with Perl<br />

As discussed earlier, we can use the HTTP header Set-Cookie to set<br />

cookies. In its simplest usage, this header takes the following form:<br />

Set-Cookie: =<br />

For example, a real header might be as follows:<br />

Set-Cookie: name=Steve<br />

When this header is passed to a browser, it sets the cookie "name"<br />

equal to "Steve." Because the header doesn't include a time to live<br />

(TTL), the cookie is only valid for the current session. When the<br />

browser is closed, the cookie expires and is deleted.<br />

To include a TTL, you add the parameter expires as shown in the<br />

following example:<br />

Set-Cookie: name=Steve; expires=Monday, 24-Mar-03 23:59:59 GMT<br />

Notice the use of a semicolon (;) to delimit the parameters. The date<br />

is in the format "weekday, dd/Mon/yy hh:mm:ss." In the example<br />

above, the cookie will expire at one second before midnight on<br />

Monday, March 24, 2003, Greenwich Mean Time.<br />

Tip: It's important to include at least one blank line after the Set-<br />

Cookie header and before the <strong>WML</strong> headers so the client correctly<br />

identifies the <strong>WML</strong> headers.


Let's look at a real example of using Perl to set a cookie for a <strong>WML</strong><br />

deck. The following code snippet shows how Perl is used to set a<br />

cookie and output a status ("Cookie set") message:<br />

#!/usr/bin/perl<br />

# Define minimal deck<br />

$deck = '<br />

<br />

<br />

<br />

Cookie set.<br />

<br />

<br />

';<br />

# Send Content-type and Set-Cookie headers<br />

print "Content-type: text/vnd.wap.wml \n";<br />

print "Set-Cookie: name=Steve \n";<br />

# Send <strong>WML</strong> header info<br />

print "\n\n";<br />

print "\n";<br />

# Send deck<br />

print $deck;<br />

The cookie in the code above was set without a TTL. To set an<br />

expiration date, we draw on the Date::Calc module to do our date<br />

calculations:<br />

#!/usr/bin/perl<br />

# Include Date::Calc.<br />

use Date::Calc':all';<br />

# Get today in GMT<br />

($year,$month,$day) = Today([$gmt]);<br />

# Add a year (365 days)<br />

($year,$month,$day) =<br />

Add_Delta_Days($year,$month,$day,"365");<br />

# Get textual representations of month and day of week<br />

$dow = Day_of_Week_to_Text(Day_of_Week($year,$month,$day));<br />

$month = Month_to_Text($month);<br />

# Make sure day is two digits<br />

if ($day


}<br />

# Assemble expiration date<br />

$date = $dow.", ".$day."-".$month."-".$year." 23:59:59 GMT";<br />

# Define deck<br />

$deck = '<br />

<br />

<br />

<br />

Cookie set.<br />

<br />

<br />

';<br />

# Send Content-type and Set-Cookie headers<br />

print "Content-type: text/vnd.wap.wml \n";<br />

print "Set-Cookie: name=Steve; expires=$date; \n";<br />

# Send <strong>WML</strong> headers<br />

print "\n\n";<br />

print "\n";<br />

# Send the deck<br />

print $deck;<br />

Note: The Date::Calc module has been covered in several previous<br />

articles. The module is available from CPAN, at<br />

http://search.cpan.org/author/STBEY/Date-Calc-5.3/Calc.pod.<br />

Reading Cookies with Perl<br />

Reading cookies with Perl is even easier than setting them, thanks to<br />

the environment variable HTTP_COOKIE. This variable contains<br />

name/value pairs for all applicable cookies (those matching the current<br />

scope).<br />

To parse the name/value list, you could use the following code:<br />

@nvpairs=split(/[,;] */, $ENV{'HTTP_COOKIE'});<br />

foreach $pair (@nvpairs) {<br />

($name, $value) = split(/=/, $pair);<br />

$cookie{$name} = $value;<br />

}


This code effectively parses the cookie list into the array $cookie,<br />

where each value can be accessed by its name. For example, our<br />

earlier "name" example would yield:<br />

$cookie{'name'} = "Steve"<br />

An extended example, displaying the cookie value in <strong>WML</strong>, is shown<br />

below:<br />

#!/usr/bin/perl<br />

# Break cookies into name/value pairs<br />

# and store into cookie array<br />

@nvpairs=split(/[,;] */, $ENV{'HTTP_COOKIE'});<br />

foreach $pair (@nvpairs) {<br />

($name, $value) = split(/=/, $pair);<br />

$cookie{$name} = $value;<br />

}<br />

# Define <strong>WML</strong> deck<br />

$deck = '<br />

<br />

<br />

<br />

Cookie (name) = '<br />

.$cookie{'name'}.'<br />

<br />

<br />

';<br />

# Send Content-type and Set-Cookie headers<br />

print "Content-type: text/vnd.wap.wml \n";<br />

print "Set-Cookie: name=Steve; expires=$date; \n";<br />

# Send <strong>WML</strong> headers<br />

print "\n\n";<br />

print "\n";<br />

# Send the deck<br />

print $deck;<br />

If the cookie was still set, this code would display the following:<br />

Cookie (name) = Steve<br />

Deleting Cookies<br />

To remove a cookie, you simply set the expiration of the cookie to a


date/time that has already passed. For example, we could use the<br />

code earlier but subtract a day or two to expire the cookie. The code,<br />

using Date::Calc functions, would resemble the following:<br />

# Get today in GMT<br />

($year,$month,$day) = Today([$gmt]);<br />

# Subtract a year (365 days)<br />

($year,$month,$day) =<br />

Add_Delta_Days($year,$month,$day,"-365");<br />

# Get textual representations of month and day of week<br />

$dow = Day_of_Week_to_Text(Day_of_Week($year,$month,$day));<br />

$month = Month_to_Text($month);<br />

# Make sure day is two digits<br />

if ($day


Alternatively, you could encode all this data into one lengthy string.<br />

My advice, however, would be to tie one unique piece of data that<br />

identifies the user to a database record. Basically, store only what you<br />

need on the client device, and store the rest of the data in a database<br />

accessible by the server. The flowchart for an application using this<br />

method might resemble Figure 2.<br />

Click here for larger image<br />

Figure 2 - Flowchart of application using described method<br />

On entering the site, the user's browser is checked for an ID cookie. If<br />

the cookie is found, the user's preferences are retrieved from the


server's database and the site is displayed with those preferences. If<br />

the cookie is not set, the user is taken to a preference form and<br />

queried for his/her preferences. Those preferences are stored in the<br />

server's database and the user's ID is stored as a cookie.<br />

http://www.developer.com/lang/php/

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!