Handling Events

An event is a significant occurrence in a system or application. In a desktop application, events occur as a result of a user action such as a key press or the clicking of a mouse button. Events also occur due to system activity such as system shutdown or, more catastrophically, a system error. By raising events, the system gives the application an opportunity to react. Applications typically respond to events by performing some useful work. Application programmers for operating systems such as Microsoft Windows and the Apple Macintosh are accustomed to handling events. Web programmers familiar with client-side JavaScript handle events that occur when the user interacts with the Web browser or when the Web browser navigates to an HTML document.

The voice application programmer has the opportunity to catch call-related events using a variety of tags defined in the VoiceXML schema. In addition, the programmer can throw events. This document focuses on the mechanics of handling events and the use of application-defined events in a VoiceXML application.

1. Understanding event types

VoiceXML places events in two categories - pre-defined events and application-defined events. Pre-defined events are events thrown by the Platform and are divided into two subcategories - normal events and error events. Application-defined events are custom events that are both thrown and caught by the voice application. Application-defined events are discussed later in this document.

1.1. Pre-defined normal events

The following table describes the pre-defined normal events defined in the VoiceXML 2.0 specification.

help Thrown when the user requests help.
noinput Thrown within an interactive call state when the user has said nothing within the timeout period.
nomatch Thrown when the user utters something outside the active grammars.
connection.disconnect.hangup Thrown when the user disconnects or is disconnected. Post-hang-up processing is limited to 5 seconds. If you need to do extensive processing, your event handler should execute a submit to your Web server to perform any additional processing. Prior to Revision 1, this event was telephone.disconnect.hangup.
cancel Thrown when the user has requested the current prompt be canceled.
exit Thrown when the user has asked to exit.
maxspeechtimeout Thrown when the user input exceeded the 'maxspeechtimeout' property.
connection.disconnect.transfer Thrown when the user has been transferred unconditionally to another line and will not return. Prior to Revision 1, this event was telephone.disconnect.transfer.

1.2. Pre-defined error events

The following table describes pre-defined error events defined in the VoiceXML 2.0 specification:

error.semantic Thrown when the VoiceXML interpreter detects a run-time error.
error.badfetch Thrown when an HTTP request for a VoiceXML document, external grammar, or external script fails. If the VoiceXML document that required the fetch contains semantic errors, the badfetch event is thrown to the container element that attempted to navigate to the document. Because the Tellme VoiceXML interpreter loads scripts on demand, a badfetch event that results from the fetch of a script is not thrown until the script element is encountered by the VoiceXML interpreter.
error.noauthorization Thrown when the user is not authorized to perform the requested operation.
error.unsupported.format Thrown when the requested resource is in an unsupported format.
error.unsupported.language Thrown when the Platform doesn't support the specified language.
error.unsupported.element Thrown when the Platform doesn't support the given VoiceXML element.

2. Handling events

To handle events, use the catch element and set the event attribute to the name of the desired event. For example, the following nomatch handler queues some audio and then listens for user input:

<catch event="nomatch">
  I'm sorry. I didn't get that. 
  Please say the name of a sport. 
  For example, say baseball.
  
</catch>

To handle the common pre-defined events - help, noinput, and nomatch - the VoiceXML specification defines corresponding elements with the same names to make it easier for the VoiceXML developer. For example, the following noinput handler queues some audio to the user and then replays the prompt:

<noinput>
   I'm sorry. I didn't hear you
   <reprompt/>
</noinput>

To handle different events with the same handler, delimit each event name with a space in the event attribute of the catch element. For example, the following code defines an event handler for a noinput and nomatch event.

<catch event="nomatch noinput">
  I'm sorry. I didn't get that. 
  Please say the name of a sport. 
  For example, say baseball.
</catch>

3. Understanding event handler selection

In the previous section you learned that the VoiceXML interpreter executes an event handler whose name matches that of the event that was thrown. This is an oversimplified explanation. The interpreter uses a number of additional criteria when selecting an event handler. These include:

When considering the event handler selection process, construct a list of potential handlers for the event, apply one criterion at a time, and cross the handlers off the list that do not meet the current criteria. After applying all criteria, select the first event handler still on the list. That's the event handler the VoiceXML intepreter would execute. If the list is empty, you should modify your code to include a handler for the unhandled event.

The following sections discuss each of the criteria in detail.

3.1. Understanding event scoping

In the introductory section on handling events, you learned to declare a handler within a field element to catch events such as a noinput or a nomatch. The field defines an anonymous scope, and the event handler has access to all the variables, scripts, and data declared within that scope in addition to those scopes defined by containing elements including the form also known as dialog scope, the vxml element also known as document scope, and the application root document also known as application scope. In addition to defining variables, scripts, and data at dialog, document, and application scope, you can also define event handlers at these scopes. When the VoiceXML interpreter begins the event handler selection process, it considers the handlers defined within the current anonymous scope as well as the outer containing scopes.

The following diagram illustrates the scopes the VoiceXML interpreter searches for an appropriate event handler when an event is thrown from within a field:

The following example defines a noinput event handler at application scope. If the user says nothing within the default timeout in response to the prompt, the VoiceXML interpreter gathers the event handlers at the anonymous scope defined by the fruit field, the dialog scope defined by the get_fruit dialog, the document scope defined by the vxml element in "fruit.vxml", and the application scope defined by the vxml element in "app_root.vxml." The interpreter executes the event handler at application scope for lack of discovering a qualified event handler from a more immediate scope. Observe that the event handler has access to both the variable ex defined at dialog scope as well as the function SayEx defined at document scope.

<!-- app_root.vxml -->
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">
 <noinput>
  Sorry. I couldn't hear you.
  Say the name of a fruit.
  <script>SayEx(ex);</script>
</noinput>
</vxml>

<!-- fruit.vxml -->
<vxml version="2.1"
 application="app_root.vxml"
 xmlns="http://www.w3.org/2001/vxml"> 

<!-- doc scope -->

<script>
function SayEx(s) {return "For example, say " + s;}
</script>

<form id="get_fruit"> <!-- dialog scope -->
   <var name="ex" expr="apple"/>
   <field name="fruit"> <!-- anonymous scope -->
      <prompt>Pick a fruit</prompt>
      <grammar mode="voice" type="application/srgs+xml" src="fruits.grxml"/>
      <filled>
         You picked <value expr="fruit"/>
      </filled>
   </field>
</form>
</vxml>

Later when you learn about an event handler's count attribute, the definition of event handlers at different scopes will take on more relevance.

1.1. Understanding default platform behavior

The Tellme VoiceXML interpreter defines the following default event-handling behavior:

"Help" When the user utters, "Help," the Platform responds with the recorded audio "I'm sorry, no help is available" and executes a reprompt. You can override this behavior by providing your own help event handler. Because the Platform provides a default link for the utterance "help", you no longer need to explicitly include "help" in your grammars or a help link.
nomatch When a nomatch event occurs, the Platform responds with the recorded audio "I'm sorry, I didn't get that." and executes a reprompt.
noinput When a noinput event occurs, the Platform responds with the recorded audio "I'm sorry, I didn't hear you." and executes a reprompt.

Because the VoiceXML interpreter defines this behavior at session scope, your application can override this behavior at anonymous, dialog, document, or application scope. While these default event handlers will get you up and running, Tellme recommends that you override the default Platform behavior in your application in order to provide your users with the best possible user experience.

3.2. Understanding document source order

You can define more than one handler for an event at the same or at different scopes. As described above, when selecting the most qualified handler for an event, the VoiceXML interpreter not only considers the scope of the handler but also considers the order in which the event handler occurs compared to other event handlers within the same scope. This ordering is referred to as document source order. The count and cond attributes notwithstanding, if two handlers at the same scope catch the same event, the VoiceXML interpreter will select the first handler it encounters in document source order.

The following example implements two noinput handlers at the anonymous scope defined by the field element. When a noinput event occurs, the VoiceXML interpreter selects the first of these. The second handler will never get executed.

<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<!-- doc scope -->
<form id="get_fruit"> <!-- dialog scope -->
   <var name="ex" expr="apple"/>
   <field name="fruit"> <!-- anonymous scope -->
   <prompt>Pick a fruit</prompt>
   <grammar mode="voice" type="application/srgs+xml" src="fruits.grxml"/>
   <noinput>
     Sorry. I couldn't hear you.
     <reprompt/>
   </noinput>

   <noinput>
     Sorry. I still couldn't hear you.
     <reprompt/>
   </noinput>

   <filled>
     You picked <value expr="fruit"/>
   </filled>
   </field>
</form>
</vxml>

In the next sections when you learn about the count and cond attributes, you'll see the usefulness of defining multiple event handlers.

3.3. Understanding the count attribute

An effective voice application takes notice if the user is experiencing trouble in a particular dialog. Trouble can be detected by the application if the Platform throws multiple nomatch, noinput, or help events in a single dialog. Rather than repeating the same information, the application can assist the user by varying the prompts played by these respective handlers.

The VoiceXML interpreter keeps track of the number of times an event is thrown within any form item or menu and allows you to implement distinct handlers for each occurrence of the event. You do so by setting the count attribute on your event handler. Each time an event executes, the VoiceXML interpreter selects the event handler that has the highest count less than or equal to the current count for that event.

Event counts are reset under the following circumstances:

If you do not specify an explicit count attribute for a handler, the default is one. If you implement multiple handlers for an event, you should specify the count attribute on each handler explicitly with a unique value. If the VoiceXML interpreter encounters multiple qualifying handlers with the same count value, it will only execute the first one it encounters in document source order within the same scope with further precedence dictated by increasing scope.

The following example implements multiple nomatch and noinput handlers for a dialog. Because the handlers exist at the same scope, the event handler selection process is simple as demonstrated by the following sample transcript.

A sample transcript for this example follows:

Tellme: Okay, now pick a fruit. (aka prompt count=1)
Caller: (noinput)
Tellme: I'm sorry. I didn't hear you. (aka noinput count=1)
Tellme: Please say the name of a fruit. For example, say banana. (aka prompt count=2)
Caller: softball
Tellme: I'm sorry. I didn't get that. (aka nomatch count=1)
Tellme: Please say the name of a fruit. For example, say banana. (aka prompt count=2)
Caller: cat
Tellme: I'm sorry. I still didn't get that. (aka nomatch count=2)
Tellme: Please say the name of a fruit. For example, say banana. (aka prompt count=2)
Caller: apple
Tellme: You chose apple.

<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

  <form id="get_fruit">
    <field name="fruit">

    <prompt count="1">
      Okay, now pick a fruit.
    </prompt>

    <prompt count="2">
      Please say the name of a fruit. For example, say
      banana.
    </prompt>

    <grammar mode="voice" type="application/srgs+xml" src="fruits.grxml" />

    <nomatch count="1">
      I'm sorry. I didn't get that.
      <reprompt />
    </nomatch>

    <nomatch count="2">
      I'm sorry. I still didn't get that.
      <reprompt />
    </nomatch>

    <nomatch count="3">
      I'm sorry. I'm having trouble understanding. Type the
      first few letters of the fruit. Press pound when you're
      done.
    </nomatch>

    <nomatch count="4">
      <exit />
    </nomatch>

    <noinput count="1">
      I'm sorry. I didn't hear you.
      <reprompt />
    </noinput>

    <noinput count="2">
      I'm sorry. I still didn't hear you.
      <reprompt />
    </noinput>

    <noinput count="3">
      <exit/>
    </noinput>

    <filled>
       You chose <value expr="fruit"/>
    </filled>
    </field>
  </form>
</vxml>

The next example adds some complexity to the previous example by moving the noinput handler with a count of three and the nomatch handler with a count of four to application scope. When the third noinput or fourth nomatch occurs, the application terminates. Rather than repeating this standard behavior in every dialog throughout the application, it makes sense to define them once in the application root document. As described in the previous section, the VoiceXML interpreter considers these as part of the standard event handler selection process.

<!-- app_root.vxml -->
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

    <nomatch count="4">
      <exit/>
    </nomatch>

    <noinput count="3">
      <exit/>
    </noinput>
</vxml>

<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml"
 application="app_root.vxml">
  <form id="get_fruit">
    <field name="fruit">

    <prompt count="1">
      Okay, now pick a fruit.
    </prompt>

    <prompt count="2">
      Please say the name of a fruit. 
      For example, say banana.
    </prompt>

    <grammar mode="voice" type="application/srgs+xml" src="fruits.grxml" />

    <nomatch count="1">
      I'm sorry. I didn't get that.
      <reprompt />
    </nomatch>

    <nomatch count="2">
      I'm sorry. I still didn't get that.
      <reprompt />
    </nomatch>

    <nomatch count="3">
      I'm sorry. I'm having trouble understanding. 
      Type the first few letters of the fruit. 
      Press pound when you're done.
    </nomatch>

    <noinput count="1">
      I'm sorry. I didn't hear you.
      <reprompt />
    </noinput>

    <noinput count="2">
      I'm sorry. I still didn't hear you.
      <reprompt />
    </noinput>

    <filled>
       You chose <value expr="fruit"/>
    </filled>
    </field>
  </form>
</vxml>

3.4. Understanding the cond attribute

As part of the event handler selection process, the VoiceXML interpreter evaluates the cond attribute of an event handler at the time the event is fired to determine if it should be used to handle the event.

The value of the cond attribute must be a JavaScript expression that evaluates to a boolean (true or false). If the expression evaluates to true, the handler remains a valid candidate for handling the current instance of the event. If the expression evaluates to false, the handler is disqualified.

The following example initializes a boolean variable bIncludeEx to true. When the first nomatch event occurs, the VoiceXML interpreter evaluates the cond attribute of the first nomatch handler, determines that it is true, and because it is the first nomatch in document source order in the anonymous scope of the field element with a qualifying count, chooses it over the second nomatch handler. Upon execution of the handler, the bIncludeEx variable is set to false. When the second nomatch event occurs, the VoiceXML interpreter reevaluates the cond attribute of the first nomatch handler, determines that it is false, and selects the second handler for execution instead.

<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<form id="get_fruit">
   <var name="bIncludeEx" expr="true"/>

   <field name="fruit">
   <prompt>Pick a fruit</prompt>

    <grammar mode="voice" type="application/srgs+xml" src="fruits.grxml" />

   <nomatch cond="bIncludeEx">
      I'm sorry. I didn't hear you.
      Pick a fruit. For example, say banana.
      <assign name="bIncludeEx" expr="false">
   </nomatch>
   
   <nomatch>
      I'm sorry. I still didn't hear you.
      <reprompt/>
   </nomatch>

   <filled>
      You picked <value expr="fruit"/>
   </filled>
   </field>

   </form>
</vxml>

4. Understanding event name prefix matching

You can use a single event handler to catch multiple events named with the same leading prefix. As part of the event selection process, the VoiceXML interpreter considers handlers with names that match the prefix of the event on period (.) boundaries.

In the following snippet the "error.badfetch.http.404" handler only catches events thrown when the Platform attempts to fetch a resource that doesn't exist. The "error.badfetch" handler catches events thrown when the Platform fails to fetch a resource for any of a variety of reasons including HTTP 404 errors. The "error" handler catches all events that begin with the prefix "error." This includes the events error, error.badfetch, and error.badfetch.http.404. Because the handlers are defined in the same scope, and the VoiceXML interpreter uses document source order to select an appropriate event handler, when an error.badfetch.http.404 event occurs, the "error.badfetch.http.404" handler is selected and executed while the more generic handlers are not executed.

<!-- catch HTTP 404 errors -->
<catch event="error.badfetch.http.404">
  I'm sorry, but an error has occurred.
  <log>Application error: HTTP 404</log>
  <disconnect />
</catch>

<!-- catch fetch failures in general -->
<catch event="error.badfetch">
  I'm sorry, but an error has occurred.
  <log>Application error: <value expr="_event"/></log>
  <disconnect />
</catch>

<!-- catch all other errors -->
<catch event="error">
  I'm sorry, but an error has occurred.
  <!-- _event stores the complete event name -->
  <log>Application error: <value expr="_event"/></log>
  <disconnect />
</catch>


Note. Because document source order takes precedence over prefix-matching within the same scope, you should always declare your more specifically named handlers before the less specific ones.

5. Application-defined events

Application-defined events are events arbitrarily defined by an application. You can use application-defined events to enforce consistency in your application by defining behavior in one place and by calling it anywhere your application requires that behavior by throwing the event.

5.1. Throwing application-defined events

To cause an application-defined event to occur, use the throw element. Use the throw element's event attribute to specify the name of the event to be thrown.

You can give an application-defined event any name as long as it doesn't conflict with an event name that is already specified by VoiceXML and as long as it conforms to the XML specification for attribute values of type PCDATA. To avoid conflicts with current and future VoiceXML event names, use the following convention:

com.<company-name>.<app-name>.<event-name>

Replace <company-name> with the name of your company. Replace <app-name> with the name of your application. Replace <event-name> with a logical name for the event. For a precise definition of valid XML attribute values of type PCDATA, please see the XML specification.

Rather than prefixing your application-defined error events with "com", consider prefixing them with "error" and defining a generic error event handler in your application root document. You'll be able to use a single event handler to catch both Platform and application-generated errors. The following example throws the application-defined event error.mycompany.myapp.password.max_retries_exceeded from within the filled element of a subdialog and catches it from a generic error handler in the application root.

<!-- app_root.vxml -->
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<!-- application-scoped variables -->
<var name="uid" />
<var name="passwd" />

<!-- count errors -->
<var name="giErrors" expr="0"/>

<catch event="error">
   <!-- _event stores the fully-qualified event name -->
   <log>App Error: <value expr="_event"/></log>

   <assign name="giErrors" expr="giErrors+1"/>

   <!-- attempt to transfer to agent on first error -->
   <if cond="giErrors &lt;= 1">
      <!-- attempt to transfer to agent -->
      <goto next="app_root.vxml#xfer2agent"/>
   <else/>
      I'm sorry, but we're experiencing technical difficulties.
      Please try again later.
      <disconnect/>
   </if>
</catch>

<!-- transfer to agent -->
<form id="xfer2agent">
<transfer bridge="true" dest="800xxxyyyy">
   <catch event="">
      <!-- 
      avoid reentrancy into application-level catch,
      and ensure the call ends
      -->
      <disconnect/>
   </catch>
   <filled>
      <disconnect/>
   </filled>
</transfer>
</form>

</vxml>

<!-- signin.vxml -->
<vxml version="2.1"
  xmlns="http://www.w3.org/2001/vxml"
  application="app_root.vxml">
<form id="login">
<!-- 
  calls a hypothetical subdialog that collects a user's id/passwd
  and validates them
-->
   <subdialog src="login.vxml" name="oResult">
      <!-- configure the subdialog to allow 3 retries -->
      <param name="retries" value="3"/>
      <filled>
        <if cond="oResult.status == -1">
           <throw 
             event="error.mycompany.myapp.password.max_retries_exceeded"/>
        <else/>
           <assign name="application.uid" expr="oResult.uid"/>
           <assign name="application.passwd" expr="oResult.passwd"/>
        </if>
      </filled>
   </subdialog>
</form>
</vxml>


An event can be thrown from any anonymous scope. Elements that define an anonymous scope include block, filled, or an event handler. When the event is thrown, the resources declared within the anonymous scope which may include variables, scripts, and data go away. The selected event handler defines a new anonymous scope where new resources can be declared. In addition, the code in the event handler has access to the dialog, document, and application scopes that were available to the anonymous scope from which the event was thrown.

In addition to throwing application-defined events, you can also throw pre-defined events. Just specify the name of the pre-defined event you want to throw. For example, the following code snippet throws the pre-defined nomatch event if the user says "strawberry" and the current season is not summer.

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<script><![CDATA[
// return the number of days elapsed between d1 and d2
function daysElapsed(d1, d2)
{
   return (Date.UTC(d1.getFullYear(),
         d1.getMonth(),d1.getDate(),0,0,0) - 
      Date.UTC(d2.getFullYear(),
         d2.getMonth(),d2.getDate(),0,0,0))/1000/60/60/24;
}

/*
Returns a code representing the current season:
  0  - winter
  1  - spring 
  2  - summer
  3  - fall
  -1 - no idea

approximate seasonal ranges (they vary slightly from year to year):
  winter = 12/21-3/19
  spring = 3/20-6/20
  summer = 6/21-9/21
  fall = 9/22-12/20
*/
function GetCurrentSeason()
{
   var dNow = new Date(); // today
   var dFirst = new Date("1/1/" + dNow.getFullYear()); // jan 1
   var iDOY = daysElapsed(dNow, dFirst); // days between

   if ((iDOY >= 354) || (iDOY <= 77)) { // winter
     return 0;
   } else if ((iDOY >= 78) && (iDOY <= 170)) { // spring
     return 1;
   } else if ((iDOY >= 171) && (iDOY <= 263)) { // summer
     return 2;
   } else if ((iDOY >= 264) && (iDOY <= 353)) { // fall
     return 3;
   } else { // ??
     return -1;
   }
}
]]></script>

<var name="season" expr="GetCurrentSeason()"/>
<form id="get_fruit">
<field name="fruit">
   
   <grammar mode="voice"
         root="root_rule"
         tag-format="semantics/1.0"
         type="application/srgs+xml"
         version="1.0"
         xml:lang="en-US">
      <rule id="root_rule" scope="public">
         <one-of>
            <item>
               <one-of>
                  <item>
                     apple
                  </item>
               </one-of>
               <tag>out.fruit = "apple";</tag>
            </item>
            <item>
               <one-of>
                  <item>
                     orange
                  </item>
               </one-of>
               <tag>out.fruit = "orange";</tag>
            </item>
            <item>
               <one-of>
                  <item>
                     banana
                  </item>
               </one-of>
               <tag>out.fruit = "banana";</tag>
            </item>
            <item>
               <one-of>
                  <item>
                     strawberry
                  </item>
               </one-of>
               <tag>out.fruit = "strawberry";</tag>
            </item>
         </one-of>
      </rule>

   </grammar>

   <prompt>Pick a fruit</prompt>
   <catch event="noinput nomatch">
     Sorry. I didn't get that.
     Say
     <if cond="season==2">strawberry</if>
     apple, orange, or banana.
   </catch>
   <filled>
      <if cond="fruit == 'strawberry' &amp;&amp; season != 2">
         <clear namelist="fruit"/> <!-- reset the form item -->
         <throw event="nomatch"/>
      <else/>
         Okay. One <value expr="fruit"/> coming up.
      </if>      
   </filled>
   </field>
</form>
</vxml>

Note. Throwing a pre-defined event increments the VoiceXML interpreter's counter for that event.

1.1. Augmenting events with messages

You can send arbitrary string data with the events that you throw using the message or messageexpr attributes of the throw element. Your code can examine the message by inspecting the variable _message within the scope of an event handler. If a message hasn't been defined for the event, the value of this variable is null.

The following example demonstrates the use of the message attribute, and the corresponding _message variable.

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<catch event="com.mycomp.myapp.event1">
  <log><value expr="_event"/> : <value expr="_message"/></log>
  <goto next="#form2"/>
</catch>

<catch event="com.mycomp.myapp">
  <log><value expr="_event"/> : <value expr="_message"/></log>
  <exit />
</catch>

<form id="form1">
   <block>
      <throw event="com.mycomp.myapp.event1"
         message="form1" />
   </block>
</form>

<form id="form2">
   <block>
      <throw event="com.mycomp.myapp.event2"
         messageexpr="'form2 : ' + new Date()" />
   </block>
</form>

</vxml>

5.2. Handling application-defined events

As with pre-defined events, use the catch element to handle application-defined events. The following example catches the application-defined event "com.mybrokerage.vbroker.onfatalerror". This event is not thrown by the system. An application can throw this event to itself from various points within an application if something catastrophic occurs. By defining a custom event and catching it at application scope, the application can handle the situation in a consistent manner.

<catch event="com.mybrokerage.vbroker.onfatalerror">
    I'm sorry, but we're experiencing technical
    difficulties. Now connecting you to a customer service
    representative

    <!-- 
    jump to a dialog in the application root document
    that attempts a transfer to a live agent.
    The root doc should already be loaded and requires no 
    additional fetch.
   -->
    <goto next="app_root.vxml#xfer2agent"/>
</catch>

If an application does not provide an explicit handler for an application-defined event, the VoiceXML interpreter terminates the application when the event is thrown at run-time.

6. Putting it all together

The following example demonstrates the handling of two normal events - nomatch and noinput - and three application-defined events - com.mycomp.myapp.picknum.*.

The pick_a_num dialog initializes a document-scoped variable iRandom and gives the user three opportunities to guess it until the number of guesses stored in the dialog-scoped variable iRetries exceeds the value of the application-scoped variable iMax. Each time the user picks a number, the contents of the filled element is executed. This code simply throws the application-defined event com.mycomp.myapp.picknum.check. The handler for this event compares the value chosen by the user stored in iPicked against the random value stored in iRandom. If the values match, the code throws the application-defined event com.mycomp.myapp.picknum.valid. Otherwise, the code throws com.mycomp.myapp.picknum.invalid.

While there is no explicit com.mycomp.myapp.picknum.valid handler, the VoiceXML interpreter uses the selection process to determine that the com.mycomp.myapp.picknum should be executed. This code simply logs the fully-qualified event name, the user's guess, and the number of tries, and navigates to the winner dialog.

The com.mycomp.myapp.picknum.invalid event handler tracks the number of retries, incrementing the value of iRetries each time the handler is executed. If iRetries is less than iMaxRetries, the user is given a hint before clearing the form item variable iPicked causing interpretation of the pick_a_num dialog to resume. Otherwise, the handler navigates to the loser dialog.

Observe that each handlers has access to variables declared at the same scopes accessible to the code from which the event was thrown. For example, the com.mycomp.myapp.picknum.invalid handler accesses the dialog-scoped variables iRetries and iPicked, the document-scoped variable iRandom, and the application-scoped variable iMaxRetries.

The following is the listing for the application root document.

<?xml version="1.0"?>
<!-- catch3_root.vxml -->
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">
  <!-- app-defined event fires when user says
       anything in contained grammar
  -->
  <link event="com.mycomp.myapp.onquit">
     
     <grammar mode="voice"
         root="root_rule"
         tag-format="semantics/1.0"
         type="application/srgs+xml"
         version="1.0"
         xml:lang="en-US">
          <rule id="root_rule" scope="public">
               <one-of>
                    <item>
                         quit
                    </item>
                    <item>
                         exit
                    </item>
                    <item>
                         bye
                    </item>
               </one-of>
          </rule>

     </grammar>

  </link>

  <!-- handler for app-defined event -->
  <catch event="com.mycomp.myapp.onquit">
     <exit />
  </catch>

  <!-- handler that checks if the user's pick was correct -->
  <catch event="com.mycomp.myapp.picknum.check">
     <if cond="iRandom == iPicked">
        <!-- observe the "valid" suffix -->
        <throw event="com.mycomp.myapp.picknum.valid"/>
     <else/>
        <!-- observe the "invalid" suffix -->
        <throw event="com.mycomp.myapp.picknum.invalid"/>      
     </if>
  </catch>

  <!-- 
  list the more specific handler first because 
  doc order takes precedence over more specific naming
  -->
  <catch event="com.mycomp.myapp.picknum.invalid">
    <log>event: <value expr="_event"/>, retries=<value expr="iRetries"/></log>
    <assign name="iRetries" expr="iRetries+1" />
    <value expr="iPicked"/> is incorrect.
    <if cond="iRetries &lt; iMaxRetries">
       <!-- give user another chance after a hint -->
       <if cond="iPicked &lt; iRandom">
          try a larger number
       <else/>
           try a smaller number
       </if>
       Or, if you're tired of playing, say quit.
       <clear namelist="iPicked"/>
    <else/>
       <!-- iMaxRetries exceeded -->
       <goto next="main.vxml#loser"/>
    </if>
  </catch>

  <!-- handles the valid case using prefix matching -->
  <catch event="com.mycomp.myapp.picknum">
    <log>event: <value expr="_event"/></log>
    <log>guess = <value expr="iPicked"/>, tries = <value expr="iRetries"/></log>
    <goto next="main.vxml#winner" />
  </catch>

  <!-- handle 3rd occurrence of these events -->
  <catch event="nomatch noinput" count="3">
     <disconnect/>      
  </catch>

  <!-- max number of guesses allowed -->
  <var name="iMaxRetries" expr="3" />

  <!-- randomly generated password from 1..10 -->
  <var name="iMax" expr="10"/>

</vxml>

The following is the listing for the main document.

<?xml version="1.0"?>
<!-- catch3.vxml -->
<vxml version="2.1" application="catch3_root.vxml"
 xmlns="http://www.w3.org/2001/vxml">
  <var name="iRandom" />

  <form id="pick_a_num">
    <var name="iRetries" expr="0" />
    <!-- initialize the answer to a number between 1 and iMax -->

    <block>
       <assign name="iRandom" expr="Math.floor(Math.random()*iMax)+1" />
    </block>

    <!-- ask the user to pick a number -->
    <field name="iPicked" type="digits">
      <prompt>
        I'm thinking of a number from one to <value expr="iMax"/>
      </prompt>

      <!-- handle the first nomatch / noinput -->
      <catch event="nomatch noinput" count="1">
         I'm sorry. I didn't get that.
         <reprompt/>
      </catch>

      <!-- handle the second nomatch / noinput -->
      <catch event="nomatch noinput" count="2">
         Pick a number from one to <value expr="iMax"/>.
      </catch>

      <filled>
         <throw event="com.mycomp.myapp.picknum.check"/>
      </filled>
    </field>
  </form>

  <!-- user picked correct number -->
  <form id="winner">
  <block>
     congrats. you win
     <break time="300ms"/>
     <goto next="#pick_a_num"/>
  </block>
  </form>

  <!-- user picked incorrectly iMaxRetries -->
  <form id="loser">
  <block>
     sorry. you lose.
     i was thinking of the number <value expr="iRandom"/>
     <break time="300ms"/>
     <goto next="#pick_a_num"/>
  </block>
  </form>

</vxml>

[24]7 Inc.| Terms of Service| Privacy Policy| General Disclaimers