Karli Wap
Karli Wap
Karli Watson
Introduction
'WAP' seems to be everywhere at the moment. You can hardly switch on your TV our read a
news story on the Internet without seeing some kind of mention of WAP. Usually, this media
coverage seems to fall into two categories: hype and negativity. Half the time you hear about
how WAP will change our lives, the other half you hear that it is a passing fad and should be
ignored and left to die. One thing that you don't really get is a feel for what WAP actually is,
without which it is impossible to decide for yourself what media information is accurate.
If I were to grab a few people in the street and ask them what WAP was I'd more than likely be
greeted with quite a few blank expressions. Perhaps I would get a few people saying "it's
sticking the Internet on your mobile phone init?". Even among web developers it only seems
like a small percentage that understand the issues involved. This small percentage tend to be
those that have done a bit of research into WAP, and perhaps started to develop their own WAP
applications.
It seems a shame that this is the case, and certainly seems to be contributing to the current
crop of "WAP is cr*p" articles that seem to be appearing.
In this paper, and the associated talk, I hope to explain exactly what WAP is, and why it's so
special. I would also like to provide enough information to get people up and started developing
WAP applications, both using its native languages and sprucing things up with ASP dynamic
content generation. Finally, I'd like to take a look into the future and see what is in store for us.
Karli Watson
Karli Watson is an in-house author from Wrox Press, having
written material for both Beginning ATL 3 COM Programming and
Beginning WAP Programming with WML and WMLScript. His
current interests include all things wireless, as well as a secret
love for 'proper' programming using C++ etc. Currently, Karli is
researching C# and Quartz with the aim of producing articles and
books in the future.
What is WAP?
WAP stands for "Wireless Application Protocol", and was created by the WAP forum
(www.wapforum.org). In terms of a name this doesn't really tell us a whole lot. To really gain an
understanding of what WAP is it is necessary to take a step back, and look at what WAP is
hoping to achieve.
Two things that have startled almost everyone in the near past have been the growth of the
Internet and the proliferation of mobile phones. Nowadays it seems like almost everyone is, or
at least wants to be 'on-line'. Everything has a URL, from major companies to little Bobby's
homepage that his Mum helped him build. Similarly, the ringing of mobile phones is prone to
penetrate our eardrums throughout the day, whether you are sitting in a cafe in the city or
wandering through the countryside.
It seems only natural that some kind of link between these technologies should be made. This
idea covers a lot. For example, if you want to check your e-mail without sitting in front of a PC
screen you should be able to do so – without a laptop. More than this, though, the idea of
accessing information on the Internet wherever you are leads to more exciting ideas. You
might want to check the film times at the cinema round the corner from the cafe without
getting out of your seat. You might want to check traffic information from inside your car. You
might want a sneaky peep at the value of your shares when your girlfriend pops out of the
room to go to the bathroom.
As an extension of this, location awareness may well become an exciting new application – if
you were somewhere you hadn't been before and fancied going to the cinema you wouldn't
necessarily know where to look for film times. Location awareness would allow you to find the
nearest cinema at the touch of a button, without even having to say where you are.
But we're starting to get ahead of ourselves now. It's time to take a look at what this means
under the hood.
Traditionally, getting information from the Internet onto your PC involves sending an HTTP
request for information on a server (the origin server), then receiving this information in the
form of an HTTP response. However, if we throw a mobile device into the equation, like so:
Mobile Origin
Client Server
It is immediately obvious that the mobile device is not part of the Internet, so we can't just
drag the information we need off a server in the normal way.
2
What we need to do is to link the mobile device into the Internet. In order to do this, the WAP
specification introduces the concept of a WAP gateway, which effectively acts as a proxy for
the mobile device. This gateway can communicate with the mobile device using WAP protocols
over existing wireless bearers. It can also communicate with origin servers on the Internet via
HTTP or other Internet protocols.
Using the gateway, mobile devices can communicate with the Internet, placing requests for
data via the gateway, which retrieves what is wanted and sends it back to the mobile device.
The third figure, below, illustrates this.
Wireless HTTP
WAP Origin
Mobile Bearer Response
Response Gateway Server
Client
So, how does this picture tie in with an explanation of what WAP is? Well, WAP is the
specification that details everything between the mobile device and the origin server. The WAP
specification takes into account all of the restrictions that are inherent in the system, and lays
out the communication protocols at all levels. In addition to this, the WAP specification contains
details of WML and WMLScript, which are the presentation and scripting languages that should
be supported by WAP devices. We'll take a closer look at these later.
The key point to understand is how all-encompassing the WAP specification is. This is
something that is often forgotten in news articles, which may well say that they are criticizing
WAP, but are often actually criticizing one small part of WAP. The problem here is that these
critical articles usually end up saying that WAP is due for an untimely death. I've yet to see an
article that contains an argument that justifies this death properly. I'll return to this point later
on, when discussing the future of WAP.
WAP Protocols
One of the main functions of the WAP gateway is to translate between the WAP and web
protocol stacks. As this is an introductory session, I see no real reason to get bogged down in
the details of all this, other than to take a very brief look at the mapping between these stacks,
both of which derive from the standard OSI (Open Systems Interconnection) model:
3
There are many similarities here, but the key point to note, again, is that the WAP specification
covers all of this – another sign of its richness.
Perhaps it is also worth noting why this new protocol stack is necessary. Apart from the more
obvious differences in connections (there aren't any wires!), there are some less obvious, but
just as important, points to consider in a system of this nature, which relate to the limitations
of the devices that will use this protocol stack.
Device Limitations
For a start, the data transfer rate is going to be much slower than is possible in a connected
system. This is not just down to bandwidth, it is also more frequent due to loss of service –
driving through a tunnel with no reception for example. Much of the web protocol stack is quite
heavyweight, especially when compared to WAP equivalents. A significant part of the WAP
protocol stack is optimized for communications to and from a mobile device under these
conditions.
In addition to this, WAP devices, by their very nature, will have small display areas, not much
memory or processing power, and awkward methods of user input.
One consequence of this is that HTML is not really suitable for display on a typical WAP device.
To illustrate this, consider the Amazon home page displayed on a mobile phone:
4
I think you'll agree that this is hardly ideal, unless you want to use a magnifying glass to see
information!
As well as being unsuitable for simply displaying information, there are other considerations to
take into account when viewing HTML on a mobile phone. Much of the content of HTML pages is
targeted towards multimedia platforms, and as such will be quite bandwidth intensive
(especially if you consider streaming audio, animations, etc.). In order to fit in with the target
devices, a far more lightweight approach is needed – at least with today's technology. The next
section of this paper will therefore look at the current solution – WML and WMLScript.
WML
WML stands for 'Wireless Markup Language', and it does "exactly what it says on the tin". It is,
in effect, HTML for mobile devices. As well as this, it is an XML application, making
manipulation with, for example, XSLT a definite possibility. Unlike HTML it is optimized more for
content then display, there are a lot of areas where individual browsers can format this content
however they choose (which has its plusses and minuses).
The easiest way to describe WML is probably to take a look at some, so here's the code for a
simple 'Hello World' type application written in WML, example1.wml:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
5
<card id="First" title="First Card">
<p>
<!-- Card implementation -->
Let's get mobile!
</p>
</card>
</wml>
Taking this from the top, we start off with the standard XML declaration:
<?xml version="1.0"?>
<wml>
<card id="First" title="First Card">
<p>
<!-- Card implementation -->
Let's get mobile!
</p>
</card>
</wml>
The top-level element, <wml>, contains what is known as the WML deck. A WML deck is made
up of individual cards, each contained in a <card> element. At any one time, a single card will
be displayed on screen, with navigation possible between cards both in a single deck and
contained in other decks.
It is common for people to make analogies between this 'deck of cards' system and traditional
web pages. For instance, a card can be though of as an HTML page in that it is displayed on its
own in a browser, and a deck can be though of as a group of related web pages, or a complete
web site. However, these analogies tend to break down on inspection – particularly when you
consider that a single deck is severely limited in size (commonly 1400 bytes or less). A single
WAP application is likely to consist of several decks. Individual cards are also likely to be small,
with the information contained on a single HTML page likely to be spread out across several.
Before we take a look at card content, let's take a look at the output of the above code on the
Phone.com UP.Browser, emulated on a PC:
6
This emulator, available free of charge from Phone.com, simulates version 4.0 of the
UP.Browser, which is WAP 1.1 compliant and supports WML and WMLScript. I'll use it for the
rest of the examples in this paper. More details of this and other toolkits are given in the Tools
section.
The content of each card in a WML deck is held, as mentioned above, in a <card> element. This
example simply outputs some text, held in a paragraph (<p>) element. Note that as WML is an
XML application we also need a </p> tag to complete the paragraph, unlike HTML:
<p>
<!-- Card implementation -->
Let's get mobile!
</p>
This paragraph element also contains a comment, in standard XML format – which doesn't get
displayed on the device.
On some browsers this will be used for display purposes, but the Phone.com browser ignores it.
Far more important is the id attribute, which uniquely identifies individual cards, making
navigation possible. Cards can be referred to using URL fragment syntax, where a given card's
id attribute is included in the URL after a # symbol (similar to an HTML bookmark). For
example, this example card could be referred to as:
example1.wml#First
We'll come back to WML code examples in a little while, and see some more examples.
WMLScript
WMLScript, also part of the WAP specification, is a language that is derived from ECMAScript,
but made to be much more lightweight. It shares a similar syntax, so WMLScript files look a lot
like JavaScript, but isn't as powerful. This makes it easier to compile script files down to an
absolute minimum memory footprint, while still providing enough capabilities to be useful.
7
The following is a simple example script:
WMLScript files are made up of functions that, if marked with the extern keyword, are
accessible from WML files via a URL syntax. For example, the URL required to call the above
function would be:
filename.wmls#add()
Most of the functionality that WMLScript supplies is provided in libraries. The above example
uses functions from the WMLBrowser library to get and set browser variables (more on these
later) and refresh the display, and the Lang library to ensure that internally help WMLScript
variables are correctly set as integers.
ASP
ASP can be used to generate WML and WMLScript files in the same way as HTML pages etc. All
we need to do to achieve this is to set the correct MIME type for WML or WMLScript, for
example:
<wml>
<card id="aspwml" title="ASPWML">
<p>
Today's date is: <%Response.Write(now())%>
</p>
</card>
</wml>
Note that there is no white space in between the first section of script (which sets the MIME
type) and the XML processing instruction that commences the WML file. Some browsers,
particularly those in actual devices, get confused if WML files start with anything other than the
initial processing instruction, and will fail to process them if, for example, they start with a
white space character.
The output from the above file would be along the lines of:
8
I've just used the now() function to output the current date and time. In order to serve up
WML and WMLScript pages with more interesting content, it's time to look at some more
examples to see what we can achieve.
Examples
To start off with, we need to see more of the capabilities of WML. Let's add a second card to
the deck we created earlier:
<wml>
<card id="First" title="First Card">
<p>
<!-- Card implementation -->
Let's get mobile!
<a href="#Second">Next</a>
</p>
</card>
<card id="Second" title="Second Card">
<p>
Still mobile!
</p>
</card>
</wml>
If we were to look at this in a browser we'd just see the first card – with no way of navigating
to the second card other than to manually enter a URL ending in #Second.
Navigation
Navigation in WML can be achieved in a number of ways, but the two main ones are softkeys
and anchor links (hyperlinks).
Softkeys
Softkeys are options that appear on screen and are usually selected using a specific button on
the phone (the default OK button provided by the Phone.com browser above is a softkey). We
can specify these using <do> elements. For example, to add a softkey that will navigate to the
second card in our deck we can add the following code to our first card:
9
The type attribute tells the browser what sort of softkey to create, in this case a simple 'press
to accept' softkey, and the label attribute gives a hint to the browser as to how to display it
(again, this may be ignored by specific browsers). Within the <do> element we have a <go>
element that instructs the browser to navigate to our second card when the softkey is
activated. On screen this looks like this:
The default OK softkey on this card acts as a 'Back' option in the Phone.com browser – but this
won't be supplied on every browser. To force a Back softkey to appear we can use another
<do> element in the second card, with a type attribute of back:
The <prev> element simply instructs the browser to navigate backwards through its history
stack. The card will now look like this:
There is now an obvious Back softkey, which will return the browser to the first card.
10
Anchor Links
As with HTML, it is possible to add hyperlinks to a WML card. We can do this using either the
<anchor> element or the short-form <a> element – which provides less functionality but is fine
for short links. We can add a link to our first card using the following code:
This places a contextual link in the card. When we select this link using the scroll keys on the
device we can follow it to its destination. The Phone.com browser automatically labels a softkey
with the title attribute of the link for selection (another device specific implementation):
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="First" title="First Card">
<p>
<!-- Card implementation -->
Let's get mobile!
<a href="#Second" title="Second">Second</a>
</p>
<do type="accept" label="Next">
<go href="#Second"/>
</do>
</card>
<card id="Second" title="Second Card">
<p>
Still mobile!
</p>
<do type="back" label="Back">
<prev/>
</do>
</card>
</wml>
11
Browser Variables and User Input
A WAP browser is capable of storing variables, each of which consists of a name and a content
string. There are a number of ways of setting these variables, one of which relies on user input
– the <input> element. This element allows the user to enter data into a card for later
processing. Again, the easiest way to explain this is to see it in action. The beginnings of our
next example, example3.wml, involve adding a user input field to our first card and removing
the anchor link:
I've typed some data into the field here. The Phone.com browser generates a softkey in the
bottom right for changing entry format between its preset types, including alphabetic, numeric,
etc. This makes it easier to add data using the keys on the phone, which are more awkward
than a standard keyboard to say the least. Each vendor has its own way of doing this to speed
things up for the user. These methods usually involve limiting the characters that can be
entered by a given key on the WAP device.
The <input> element stores user input in the variable specified in name, in this case A.
Selecting the Next softkey will take us to the next card, where we will want to use this variable.
To output the content of a variable we use the case sensitive format $(var_name), so to output
A into the content of the next card we use simply:
12
With this code, following the link results in the following display:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="First" title="First Card">
<p>
Name: <input type="text" name="A"/>
</p>
<do type="accept" label="Next">
<go href="#Second"/>
</do>
</card>
<card id="Second" title="Second Card">
<p>
Hello $(A)!
</p>
<do type="back" label="Back">
<prev/>
</do>
</card>
</wml>
List Selection
In addition to straight user input, we can also provide list selection capabilities with <select>
and <option> elements. The selection or selections made by the user can be stored in two
variables, specified in the <select> element's name and iname attributes. These variables store
textual data and index data respectively, as semicolon-separated strings. The textual data
stored is that of the value attribute of selected <option> elements, which are contained by the
<select> element.
As an example I've chosen to create a list of spice girl attributes. If you were to see a spice girl
on the street, say, you could use your WAP enabled mobile device to make a quick tally of their
outstanding features. The following shows the modifications made to the previous example to
create example4.wml:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="First" title="First Card">
13
<p>
Spice girl attributes:
<select name="A" iname="B" multiple="true">
<option value="F">Fearsome</option>
<option value="D">Disturbing</option>
<option value="L">Loud</option>
<option value="A">Annoying</option>
</select>
</p>
<do type="accept" label="Next">
<go href="#Second"/>
</do>
</card>
<card id="Second" title="Second Card">
<p>
Attributes: $(A)<br/>
Indices: $(B)
</p>
<do type="back" label="Back">
<prev/>
</do>
</card>
</wml>
I've used the <select> element's multiple attribute to allow for spice girls who fulfill more
than one criterion. The end result looks like this:
Here I've encountered a disturbing and loud spice girl. Selecting the Next softkey takes us to
the next page, where we display the content of the two variables stored in name and iname:
Using WMLScript
For the next example I'll expand the spice girl attribute application above to use some
WMLScript. I'll pass the values selected from the list into a simple script which will give me a
"result". This result will be to inform me whether it is necessary to do a runner to escape the
spice girl in question.
14
The first modification to make, and the first step on the way to example5.wml, is to change the
Next softkey to a Calculate softkey, which targets a WMLScript function called
spicegirlcalc() in example5.wmls (which we'll see in a moment). We'll pass it the list of
indices of spice girl attributes as a parameter, using the same variable format as before:
We'll make the script store the result of its calculation in a variable called C, which we'll display
in the second card:
Now all we have to do is write the script. To start with, we'll need the function skeleton in
example5.wmls:
We've declared our externally accessible spicegirlcalc() function, supplied it with a name for
its single attribute, and put in a dummy function body between curly braces. Note that the line
of code between these braces shown above is a comment – which is in the standard ECMA
form, and will probably look familiar.
The attribute parameter will be a semicolon-separated list of index values of spice girl
attributes. The WMLScript String library provides us with several functions capable of accessing
separated strings; we just need to know what the separator is. In this case, the separator ";" is
escaped in the URL to the octet "%3B", so we use this as our separator, and use the library
function to count the number of elements making up the string:
Next we test this value, stored in the variable atNum, and set the value of the browser variable
C accordingly:
15
extern function spicegirlcalc(attribute)
{
var atNum = String.elements(attribute, "%3B");
if (atNum <= 2)
{
WMLBrowser.setVar("C", "Just bearable.");
}
else
{
WMLBrowser.setVar("C", "Run away! Now!");
}
Finally, we instruct the browser to navigate to the second card where the result will be
displayed:
Hitting Calculate for the disturbing loud spice girl encountered earlier results in:
So we're safe for now. More than two outstanding spice girl features would have resulted in:
16
And immediate action would be necessary.
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="First" title="First Card">
<p>
Spice girl attributes:
<select name="A" iname="B" multiple="true">
<option value="F">Fearsome</option>
<option value="D">Disturbing</option>
<option value="L">Loud</option>
<option value="A">Annoying</option>
</select>
</p>
<do type="accept" label="Calculate">
<go href="example5.wmls#spicegirlcalc('$(B)')"/>
</do>
</card>
<card id="Second" title="Second Card">
<p>
Result: $(C)
</p>
<do type="back">
<prev/>
</do>
</card>
</wml>
17
ASP
The ASP page we saw earlier, example6.asp, isn't really that much more than a basic WML
page:
<%Response.ContentType = "text/vnd.wap.wml"%>
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="aspwml" title="ASPWML">
<p>
Today's date is: <%Response.Write(now())%>
</p>
</card>
</wml>
The key thing to remember is to set the correct MIME type. A full set of WAP MIME types is
given later on, in the Serving WAP Applications section.
ADO
For this example we'll expand the spice girl calculator example to use values from a database.
Specifically, we'll work out from the attributes given whether the spice girl you have seen is
one of the five that are known. We'll store this information in a simple Access database called
example7.mdb, containing the following data:
We'll only make minor modifications to the earlier file, example5.wml - we'll remove the
second card, and change the navigation to point at an ASP file, example7.asp. WML allows us
to add data to URLs in the form accepted by ASP pages using the <postfield> element. We'll
use this to send the list of attribute indices to the server. example7.wml will look like this:
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
<wml>
<card id="First" title="First Card">
<p>
Spice girl attributes:
<select name="A" iname="B" multiple="true">
<option value="F">Fearsome</option>
<option value="D">Disturbing</option>
<option value="L">Loud</option>
<option value="A">Annoying</option>
18
</select>
</p>
<do type="accept" label="Calculate">
<go href="example7.asp">
<postfield name="attribs" value="$(B)"/>
</go>
</do>
</card>
</wml>
Response.ContentType = "text/vnd.wap.wml"
Response.Expires = -1
Response.AddHeader "Pragma", "no-cache"
Response.AddHeader "Cache-Control", "no-cache, must-revalidate"
Dim strAttribs
Dim isFearsome, isDisturbing, isLoud, isAnnoying
strAttribs = Request.QueryString("Attribs")
if inStr(strAttribs, "1") Then isFearsome = "Yes" Else isFearsome = "No"
if inStr(strAttribs, "2") Then isDisturbing = "Yes" Else isDisturbing =
"No"
if inStr(strAttribs, "3") Then isLoud = "Yes" Else isLoud = "No"
if inStr(strAttribs, "4") Then isAnnoying = "Yes" Else isAnnoying = "No"
Dim strConn
strConn = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" & _
Server.MapPath("example7.mdb") & ";"
Dim objConn
Dim objRec
Dim strQuery
Set objConn = Server.CreateObject("ADODB.Connection")
Set objRec = Server.CreateObject("ADODB.Recordset")
objConn.open strConn
strQuery = "SELECT * FROM Spice WHERE Fearsome='" & isFearsome _
& "' AND Disturbing='" & isDisturbing _
& "' AND Loud='" & isLoud _
& "' AND Annoying='" & isAnnoying & "'"
objRec.Open strQuery, objConn, adOpenForwardOnly, adLockReadOnly,
adCmdText
%><?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
"http://www.wapforum.org/DTD/wml_1.1.xml">
19
<wml>
<card id="spice" title="Spice Girl">
<p>
<%
If Not objRec.EOF Then
Response.Write("Could be ")
Response.Write(objRec("name"))
objRec.MoveNext
While Not objRec.EOF
Response.Write(" or " & objRec("name"))
objRec.MoveNext
Wend
Response.Write(". Best leg it.")
Else
Response.Write("Unknown spice girl. Best err on the side of caution.")
End If
%>
</p>
<do type="prev">
<prev/>
</do>
</card>
</wml>
<%
objRec.Close
objConn.Close
%>
Just about all of this is standard ASP with ADO, and what isn't is exactly the same as what
appears in the simple example.
The results of this code are as follows; enter your spice girl details:
20
Serving WAP Applications
WAP applications can be served from any existing web server, including IIS, PWS, Apache etc.
All you need to do is to set up the required MIME types:
Tools
Toolkits & emulators are available in many places on the web. For example, check out:
Nokia: www.nokia.com
Phone.com: www.phone.com
Ericsson: www.ericsson.com
There are also other resources available that you might want to look at:
WAP is here to stay. It is just too well developed not to. However, WML is likely to evolve – or
even be replaced. One current candidate for this is XHTML (eXtensible HyperText Markup
Language), which has the manipulative advantages of XML, and can be displayed on normal
web browsers. It would be possible, for example, to strip out bandwidth heavy sections and
fine tune pages on the fly, for consumption by web or WAP browser.
In the future we are likely to see much higher bandwidths being available. It is unlikely that
these bandwidths will ever challenge wired bandwidths – where technology is increasing at a
similar rate. Wireless bandwidths will always be a step or two behind. But things will be a lot
better than they are now, that's for sure!
Devices are likely to have better processing power, which would enable more client side
processing and an altogether more responsive user experience. The drive here will be driven as
much by consumer demand as technology – it is already possible to have more detailed colorful
displays for example, but the associated reduction in battery life will probably hold this back for
a while (at the time of writing, color screen devices are rapidly being announced). And, when it
comes down to it, do we really need colored displays for WAP content?
21
Soon, devices will be "always on", that is, "always connected". From the moment you switch
your device on in the morning you will have a WAP connection. This will mean that e-mail
receipt will be instant, and "WAP push" applications will be possible.
Location awareness is also likely to appear soon, possibly using GPS technology. In some ways
that'll be great – instant traffic reports with the minimum of effort is good, targeted push
advertising when you walk past a shop window isn't. Still, it'll happen.
Further Reading
Professional WAP, WROX Press (ISBN 1861004044)
Beginning WAP Programming with WML and WMLScript, WROX Press (ISBN 1861004583),
expected release October 2000
22